diff --git a/.gitattributes b/.gitattributes index 24230ba8..45e7fc98 100755 --- a/.gitattributes +++ b/.gitattributes @@ -1,7 +1,3 @@ -# -# https://help.github.com/articles/dealing-with-line-endings/ -# - *.bat text eol=crlf *.sh text eol=lf -gradlew text eol=lf +gradlew text eol=lf \ No newline at end of file diff --git a/.gitignore b/.gitignore index 922d98c1..3c6a1065 100755 --- a/.gitignore +++ b/.gitignore @@ -2,16 +2,15 @@ .settings .classpath .project -build bin -proxyServer/bin -proxyServer/rundir -desktopRuntime/_eagstorage* -desktopRuntime/eclipseProject/bin* -desktopRuntime/hs_err_* -desktopRuntime/crash-reports/* -desktopRuntime/options.txt -desktopRuntime/_eagstorage* -desktopRuntime/filesystem/* -desktopRuntime/downloads/* -desktopRuntime/screenshots/* \ No newline at end of file +/build +/target_*/build +/desktopRuntime/_eagstorage* +/desktopRuntime/eclipseProject/bin* +/desktopRuntime/hs_err_* +/desktopRuntime/crash-reports/* +/desktopRuntime/options.txt +/desktopRuntime/_eagstorage* +/desktopRuntime/filesystem/* +/desktopRuntime/downloads/* +/desktopRuntime/screenshots/* diff --git a/CompileEPK.bat b/CompileEPK.bat deleted file mode 100755 index f22dea0d..00000000 --- a/CompileEPK.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -title epkcompiler -echo compiling, please wait... -java -jar "desktopRuntime/CompileEPK.jar" "desktopRuntime/resources" "javascript/assets.epk" -echo finished compiling epk -pause \ No newline at end of file diff --git a/CompileEPK.sh b/CompileEPK.sh deleted file mode 100755 index 05f907cb..00000000 --- a/CompileEPK.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -java -jar "desktopRuntime/CompileEPK.jar" "desktopRuntime/resources" "javascript/assets.epk" \ No newline at end of file diff --git a/CompileJS.bat b/CompileJS.bat deleted file mode 100755 index b7f7fd27..00000000 --- a/CompileJS.bat +++ /dev/null @@ -1,4 +0,0 @@ -@echo off -title gradlew generateJavascript -call gradlew generateJavascript -pause \ No newline at end of file diff --git a/CompileJS.sh b/CompileJS.sh deleted file mode 100755 index 4d6d4ca5..00000000 --- a/CompileJS.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -chmod +x gradlew -./gradlew generateJavascript \ No newline at end of file diff --git a/EAGLERCRAFTX_README.md b/EAGLERCRAFTX_README.md index a6b18211..e3e740d3 100755 --- a/EAGLERCRAFTX_README.md +++ b/EAGLERCRAFTX_README.md @@ -11,7 +11,6 @@ - **Source code to provide the LWJGL keyboard, mouse, and OpenGL APIs in a browser** - **Patch files to mod the Minecraft 1.8 source code to make it browser compatible** - **Browser-modified portions of Minecraft 1.8's open-source dependencies** - - **Plugins for Minecraft servers to allow the eagler client to connect to them** ### This repository does NOT contain: @@ -49,9 +48,9 @@ The JavaScript runtime of EaglercraftX 1.8 is currently known to work on browser ## WebAssembly GC Support -EaglercraftX 1.8 also has an experimental WebAssembly GC (WASM-GC) runtime, almost all of the features supported on the JavaScript runtime are also supported on the WebAssembly GC runtime, however it is still incompatible with several major browsers (especially Safari) and will not run in Chrome unless you can access the `chrome://flags` menu or request an origin trial token from Google for your website. Its based on experimental technology and may still crash sometimes due to browser bugs or unresolved issues in the Java to WASM compiler. Hopefully in the coming months the required feature (JSPI, WebAssembly JavaScript Promise Integration) will become enabled by default on the Chrome browser. It performs significantly better than the JavaScript client, around 50% more FPS and TPS in some cases, and will hopefully replace it someday. Just make sure you enable VSync when you play it, otherwise the game will run "too fast" and choke the browser's event loop, causing input lag. +EaglercraftX 1.8 also has an experimental WebAssembly GC (WASM-GC) runtime, almost all of the features supported on the JavaScript runtime are also supported on the WebAssembly GC runtime, however it is still incompatible with several major browsers (especially Safari) and will not run in Chrome unless you can access the `chrome://flags` menu or request an origin trial token from Google for your website. Its based on experimental technology and may still crash sometimes, mostly due to browser bugs. Hopefully in the coming months the required feature (JSPI, WebAssembly JavaScript Promise Integration) will become enabled by default on the Chrome browser. It performs significantly better than the JavaScript client, around 50% more FPS and TPS in some cases, and will hopefully replace it someday. Just make sure you enable VSync when you play it, otherwise the game will run "too fast" and choke the browser's event loop, causing input lag. -You can compile the WebAssembly GC runtime by creating a development environment (workspace) and reading the README in the "wasm_gc_teavm" folder. +There is no GUI for compiling the WASM-GC client at the moment, you need to compile it using the `MakeWASMClientBundle` script in the development environment. ## Singleplayer @@ -85,57 +84,7 @@ If you are creating a resource pack and want to disable the blur filter on the m ## Making a Server -To make a server for EaglercraftX 1.8 the recommended software to use is EaglercraftXBungee ("EaglerXBungee") which is included in this repository in the `gateway/EaglercraftXBungee` folder. This is a plugin designed to be used with BungeeCord to allow Eaglercraft players to join your BungeeCord server. It is assumed that the reader already knows what BungeeCord is and has a working server set up that is joinable via java edition. If you don't know what BungeeCord is, please research the topic yourself first before continuing. Waterfall and FlameCord have also been tested, but EaglerXBungee was natively compiled against BungeeCord. - -There is an experimental velocity plugin available in `gateway/EaglercraftXVelocity` but it is still in development and not recommended for public servers, so be sure to check for updates regularly if you use it. Configuration files are basically identical to EaglercraftXBungee so its safe to just directy copy in your old EaglercraftXBungee config files to the `plugins/eaglerxvelocity` folder and they should work with a minimal number of edits if you are migrating your network from BungeeCord to Velocity. - -**Warning:** Both EaglerXBungee and EaglerXVelocity perform a lot of reflection that will inevitably break after a while when BungeeCord or Velocity is updated upstream. Both plugins will display the precise build number of BungeeCord and Velocity that has been tested by the developers and known to be compatible with EaglerXBungee and EaglerXVelocity when the proxy first starts up. If you are experiencing issues, try checking the BungeeCord or Velocity website for old versions and find the closest version number to whatever the current compatible version number is that is printed by EaglerXBungee/EaglerXVelocity, it will probably fix whatever missing functions the error messages are complaining about. - -### Detailed READMEs - -- [**EaglerXBungee README**](README_EAGLERXBUNGEE.md) -- [**EaglerXVelocity README**](README_EAGLERXVELOCITY.md) -- [**EaglerXBukkitAPI README**](README_EAGLERXBUKKITAPI.md) - -### Installation - -Obtain the latest version of the EaglerXBungee JAR file (it can be downloaded in the client from the "Multiplayer" screen) and place it in the "plugins" folder of your BungeeCord server. It's recommended to only join native Minecraft 1.8 servers through an EaglerXBungee server but plugins like ProtocolSupport have allowed some people to join newer servers too. - -Configuration files and other plugin data will be written in `plugins/EaglercraftXBungee` - -### Online Mode Instructions - -1. Enable `online_mode` in BungeeCord's `config.yml` file and make sure it works -2. Join the BungeeCord server using Minecraft Java Edition while logged into your Microsoft account -3. Run the `/eagler` command, it will give you a temporary login code -4. Disconnect from the server, close java edition, launch EaglercraftX 1.8 -5. Set your profile username to the username of your Microsoft account -6. Go to the "Multiplayer" menu, press "Direct Connect", press "Connect to Server", then enter "ws://localhost:8081/" -7. If you are using a VPS, replace "localhost" with the IP address of the VPS when you connect -8. Press "Join Server", a login screen will be displayed, enter the temporary login code into the password field -9. EaglerXBungee will log you into the server as the Microsoft account you generated the login code with - -Players using EaglercraftX will be able to see the vanilla skins of players on the server using vanilla Minecraft, but players on the server using vanilla Minecraft won't be able to see the skins of players using Eaglercraft. Instead they will see the skin of the Minecraft account that was used when the Eaglercraft player originally ran the `/eagler` command. - -To disable this vanilla player skin feature and stop the plugin from downloading the textures of any player heads spawned with commands, edit the EaglercraftXBungee `settings.yml` file in the `plugins/EaglercraftXBungee` folder and change `download_vanilla_skins_to_clients` to `false`. Ratelimits configured in `settings.yml` define the maximum number of times per minute a single player is allowed to trigger profile/skin lookups and also define the maximum number of times per minute the entire server is allowed to actually perform profile/skin lookups. - -By default, EaglercraftXBungee will use a local SQLite database in the server's working directory to store player skins and authentication codes. SQLite will be downloaded automatically if it is not already present. If you would like to use MySQL or something else instead, EaglercraftXBungee is JDBC-based and supports any database type that you can find a driver for. You can set the path of the database, path of the driver JAR, and the name of the driver class (example: `org.sqlite.JDBC`) for storing player skins in `settings.yml` and for storing login codes and profiles in `authservice.yml`. - -### Offline Mode Instructions - -By setting `online_mode` to `false` in the BungeeCord `config.yml` the authentication system will be disabled and players will no longer be required to first generate a code to log in. This should only be used for testing or if you can't get the authentication system to work. EaglercraftXBungee's skin system is supposed to be able to display SkinsRestorer skins if you plan to have vanilla players on the server but it's not guaranteed. - -### Built-in HTTP server - -When configuring the EaglercraftXBungee `listeners.yml` file, every listener includes an `http_server` section that can be used to configure the listener to also behave like a regular HTTP server when the websocket address is entered into a browser. If this is disabled people will get the normal "404 Websocket Upgrade Failure" instead when they accidentally type your server address into their browser. `root` defines the path to the folder containing index.html and the other files you want to host, relative to the `plugins/EaglercraftXBungee` folder. This can be useful for hosting the client if the offline download doesn't work for some reason but might slow your BungeeCord server down if lots of people are loading it all the time. - -### Enabling Voice Chat - -Voice chat is disabled by default in EaglercraftXBungee because it is not recommended for use on public servers. To enable it, add or change `allow_voice: true` to your EaglercraftXBungee `listeners.yml` file. The main difference between Eaglercraft 1.5.2 and EaglercraftX 1.8's voice chat feature is that the "Global" channel now only includes other players on the same server as you instead of every single player connected to the same bungeecord proxy. If you would like to disable voice chat on certain servers, add the names of the servers to the `disable_voice_chat_on_servers` list in the EaglercraftXBungee `settings.yml` file. You may have to add this property to the YML file manually if you've upgraded your server from an older version of EaglercraftXBungee. - -### Disabling FNAW Skins - -Players are known to complain about the high-poly Five Nights At Winstons character skins making PVP harder because of the belief that they change a player's hitbox. If you would like to disable those skins in your PVP worlds you can either set `disable_fnaw_skins_everywhere: true` in your EaglercraftXBungee `settings.yml` file to disable them for all players on your whole BungeeCord proxy, or you can disable them on specific servers by adding the names of the servers to the `disable_fnaw_skins_on_servers` list also in `settings.yml` like with disabling voice chat. +To make a server for EaglercraftX 1.8 the recommended software to use is EaglercraftXServer ("EaglerXServer"), which you can get from lax1dude here: [https://lax1dude.net/eaglerxserver/](https://lax1dude.net/eaglerxserver/) ## Launch Options @@ -259,5 +208,3 @@ The `crashReportShow` hook can be used to capture crash reports and append addit ## Developing a Client There is currently no system in place to make forks of 1.8 and merge commits made to the patch files in this repository with the patch files or workspace of the fork, you're on your own if you try to keep a fork of this repo for reasons other than to contribute to it - -**Note:** If you are trying to use the desktop runtime on Linux, make sure you add the "desktopRuntime" folder to the `LD_LIBRARY_PATH` environment variable of the Java process. This should be done automatically by the Eclipse project's default run configuration, but it might not work properly on every system, or when the Eclipse project is imported into IntelliJ. diff --git a/MakeOfflineDownload.bat b/MakeOfflineDownload.bat deleted file mode 100755 index 3994953b..00000000 --- a/MakeOfflineDownload.bat +++ /dev/null @@ -1,4 +0,0 @@ -@echo off -title MakeOfflineDownload -java -cp "desktopRuntime/MakeOfflineDownload.jar;desktopRuntime/CompileEPK.jar" net.lax1dude.eaglercraft.v1_8.buildtools.workspace.MakeOfflineDownload "javascript/OfflineDownloadTemplate.txt" "javascript/classes.js" "javascript/assets.epk" "javascript/EaglercraftX_1.8_Offline_en_US.html" "javascript/EaglercraftX_1.8_Offline_International.html" "javascript/lang" -pause \ No newline at end of file diff --git a/MakeOfflineDownload.sh b/MakeOfflineDownload.sh deleted file mode 100755 index 163d1862..00000000 --- a/MakeOfflineDownload.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -java -cp "desktopRuntime/MakeOfflineDownload.jar:desktopRuntime/CompileEPK.jar" net.lax1dude.eaglercraft.v1_8.buildtools.workspace.MakeOfflineDownload "javascript/OfflineDownloadTemplate.txt" "javascript/classes.js" "javascript/assets.epk" "javascript/EaglercraftX_1.8_Offline_en_US.html" "javascript/EaglercraftX_1.8_Offline_International.html" "javascript/lang" \ No newline at end of file diff --git a/MakeSignedClient.bat b/MakeSignedClient.bat deleted file mode 100755 index c60fa244..00000000 --- a/MakeSignedClient.bat +++ /dev/null @@ -1,4 +0,0 @@ -@echo off -title MakeSignedClient -java -cp "desktopRuntime/MakeOfflineDownload.jar;desktopRuntime/CompileEPK.jar" net.lax1dude.eaglercraft.v1_8.buildtools.workspace.MakeSignedClient "javascript/SignedBundleTemplate.txt" "javascript/classes.js" "javascript/assets.epk" "javascript/lang" "javascript/SignedClientTemplate.txt" "javascript/UpdateDownloadSources.txt" "javascript/EaglercraftX_1.8_Offline_Signed_Client.html" -pause \ No newline at end of file diff --git a/MakeSignedClient.sh b/MakeSignedClient.sh deleted file mode 100755 index 83e964bd..00000000 --- a/MakeSignedClient.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -java -cp "desktopRuntime/MakeOfflineDownload.jar:desktopRuntime/CompileEPK.jar" net.lax1dude.eaglercraft.v1_8.buildtools.workspace.MakeSignedClient "javascript/SignedBundleTemplate.txt" "javascript/classes.js" "javascript/assets.epk" "javascript/lang" "javascript/SignedClientTemplate.txt" "javascript/UpdateDownloadSources.txt" "javascript/EaglercraftX_1.8_Offline_Signed_Client.html" \ No newline at end of file diff --git a/README.md b/README.md index a4ca1fb7..d3858727 100755 --- a/README.md +++ b/README.md @@ -1,32 +1,31 @@ -### Java 17 is recommended for compiling to TeaVM +# eaglercraft-workspace -### Java 8 or greater is required for the desktop runtime +### Java 17 or greater is required! -**Most Java IDEs will allow you to import this repository as a gradle project for compiling it to JavaScript.** +**To get started, import this entire folder into your IDE as a Gradle project, this will automatically create several different projects to build all the common classes and each runtime.** -Java must be added to your PATH! +The Gradle plugin was created by [cire3](https://github.com/cire3wastaken), and the source code is available [here](https://github.com/The-Resent-Team/open-source-projects). -**To compile the web client:** -1. Run `CompileEPK` -2. Run `CompileJS` (or the `generateJavaScript` gradle task in your IDE) -3. Check the "javascript" folder +**To compile the JavaScript client:** -**To compile an offline download:** -1. Run `CompileEPK` -2. Run `CompileJS` (or the `generateJavaScript` gradle task in your IDE) -3. Run `MakeOfflineDownload` -4. Check the "javascript" folder +Run the `MakeOfflineDownload` script in the "target_teavm_javascript" folder (or the `makeMainOfflineDownload` Gradle task in your IDE) to build the JavaScript client. This will build the "classes.js" and "assets.epk" and the offline downloads, the results will be in the "javascript" folder. -**To compile the WASM GC client:** -Consult the [README](wasm_gc_teavm/README.md) in the wasm_gc_teavm folder +**To compile the WASM-GC client:** -**To use the desktop runtime:** -1. Import the Eclipse project in "desktopRuntime/eclipseProject" into your IDE -2. Open one of the .java files from the source folders (workaround for a bug) -3. Run/Debug the client with the included "eaglercraftDebugRuntime" configuration +Run the `MakeWASMClientBundle` script in the "target_teavm_wasm_gc" folder (or the `makeMainWasmClientBundle` Gradle task in your IDE) to build the WASM-GC client. This will build the "assets.epw" file which contains all the code and assets if the WASM-GC client, and also create an offline download, the results will be in the "javascript_dist" folder. -**Note:** If you are trying to use the desktop runtime on Linux, make sure you add the "desktopRuntime" folder to the `LD_LIBRARY_PATH` environment variable of the Java process. This should be done automatically by the Eclipse project's default run configuration, but it might not work properly on every system, or when the Eclipse project is imported into IntelliJ. +The WASM-GC client uses a custom fork of TeaVM, the source is available [here](https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm). -**See the main 1.8 repository's README for more info** +**To run the desktop runtime:** -The source codes of EaglercraftXBungee and EaglercraftXVelocity are not included here. \ No newline at end of file +**Note:** Athough it may be tempting to release "desktop" copies of your client, the current desktop runtime was designed for debug use only and is a poor choice for distribution to end users. + +Run the `StartDesktopRuntime` script in the "target_lwjgl_desktop" folder (or the `eaglercraftDebugRuntime` Gradle task in your IDE) to run the desktop runtime. This will run the client using the JVM and an LWJGL3-based runtime, which can be useful for debugging crashes and to speed up testing if you don't want to wait for the JavaScript or WASM-GC client to be built. + +Do not use the desktop runtime as substitute for testing you client in a browser, client developers who only test their client on the desktop runtime usually end up with lots of unexpected bugs and crashes in their browser builds. + +**To debug the desktop runtime:** + +If you want to debug the desktop runtime from your IDE, one way you can do it is by enabling the debugger in the LWJGL target's `eaglercraftDebugRuntime` task, but something that will launch even faster is just creating a run configuration in your IDE directly in the LWJGL target project. + +You can do this by creating a run configuration specifying `net.lax1dude.eaglercraft.v1_8.internal.lwjgl.MainClass` as the main class, the `desktopRuntime` folder as the working directory, `-Xmx1G -Xms1G -Djava.library.path=.` in the JVM arguments, and if you're on Linux you'll also want to add an environment variable to append the `desktopRuntime` folder to `LD_LIBRARY_PATH` and set `__GL_THREADED_OPTIMIZATIONS` to `0` if using Nvidia drivers. diff --git a/build.gradle b/build.gradle deleted file mode 100755 index bf5b71c8..00000000 --- a/build.gradle +++ /dev/null @@ -1,73 +0,0 @@ -import org.teavm.gradle.api.OptimizationLevel - -buildscript { - dependencies { - classpath files("src/teavmc-classpath/resources") - } -} - -plugins { - id "java" - id "eclipse" - id "org.teavm" version "0.9.2" -} - -sourceSets { - main { - java { - srcDirs( - "src/main/java", - "src/game/java", - "src/protocol-game/java", - "src/protocol-relay/java", - "src/teavm/java", - "src/teavm-boot-menu/java" - ) - } - } - -} - -repositories { - mavenCentral() -} - -dependencies { - teavm(teavm.libs.jso) - teavm(teavm.libs.jsoApis) - compileOnly "org.teavm:teavm-core:0.9.2" // workaround for a few hacks -} - -def folder = "javascript" -def name = "classes.js" - -teavm.js { - compileJava.options.encoding = "UTF-8" - obfuscated = true - sourceMap = true - targetFileName = "../" + name - optimization = OptimizationLevel.BALANCED // Change to "AGGRESSIVE" for release - outOfProcess = false - fastGlobalAnalysis = false - processMemory = 512 - entryPointName = "main" - mainClass = "net.lax1dude.eaglercraft.v1_8.internal.teavm.MainClass" - outputDir = file(folder) - properties = [ "java.util.TimeZone.autodetect": "true" ] - debugInformation = false -} - -tasks.named("generateJavaScript") { - doLast { - - // NOTE: This step may break at any time, and is not required for 99% of browsers - - def phile = file(folder + "/" + name) - def dest = phile.getText("UTF-8") - def i = dest.substring(0, dest.indexOf("=\$rt_globals.Symbol('jsoClass');")).lastIndexOf("let ") - dest = dest.substring(0, i) + "var" + dest.substring(i + 3) - def j = dest.indexOf("function(\$rt_globals,\$rt_exports){") - dest = dest.substring(0, j + 34) + "\n" + file(folder + "/ES6ShimScript.txt").getText("UTF-8") + "\n" + dest.substring(j + 34) - phile.write(dest, "UTF-8") - } -} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100755 index 00000000..37c7d710 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,51 @@ +plugins { + id("java") +} + +allprojects { + apply(plugin = "eclipse") + + repositories { + mavenCentral() + } + + plugins.withId("java") { + java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } + } + } +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +sourceSets { + named("main") { + java.srcDirs( + "src/main/java", + "src/game/java", + "src/protocol-game/java", + "src/protocol-relay/java", + "src/platform-api/java" + ) + } +} + +dependencies { + implementation(libs.bundles.common) +} + +tasks.withType { + // TeaVM will fail if anything from platform-api is in the JAR + fileTree("src/platform-api/java").visit { + if (!isDirectory) { + if (path.endsWith(".java")) { + exclude(path.substring(0, path.length - 5) + ".class") + } + } + } +} diff --git a/desktopRuntime/Java-WebSocket-1.5.1-with-dependencies.jar b/desktopRuntime/Java-WebSocket-1.5.1-with-dependencies.jar deleted file mode 100755 index 3600c46d..00000000 Binary files a/desktopRuntime/Java-WebSocket-1.5.1-with-dependencies.jar and /dev/null differ diff --git a/desktopRuntime/LICENSE b/desktopRuntime/LICENSE index a17ad0af..a983c18a 100755 --- a/desktopRuntime/LICENSE +++ b/desktopRuntime/LICENSE @@ -1,29 +1,29 @@ -Copyright (c) 2012-present Lightweight Java Game Library -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 Lightweight Java Game Library 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 +Copyright (c) 2012-present Lightweight Java Game Library +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 Lightweight Java Game Library 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. \ No newline at end of file diff --git a/desktopRuntime/OpenAL.dll b/desktopRuntime/OpenAL.dll index 74c849d9..4cd966e2 100755 Binary files a/desktopRuntime/OpenAL.dll and b/desktopRuntime/OpenAL.dll differ diff --git a/desktopRuntime/eclipseProject/.classpath b/desktopRuntime/eclipseProject/.classpath deleted file mode 100755 index 6300425f..00000000 --- a/desktopRuntime/eclipseProject/.classpath +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/desktopRuntime/eclipseProject/.project b/desktopRuntime/eclipseProject/.project deleted file mode 100755 index 65e5fa0e..00000000 --- a/desktopRuntime/eclipseProject/.project +++ /dev/null @@ -1,44 +0,0 @@ - - - eclipseProject - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - - - src_main_java - 2 - PARENT-2-PROJECT_LOC../src/main/java - - - src_game_java - 2 - PARENT-2-PROJECT_LOC../src/game/java - - - src_protocol-game_java - 2 - PARENT-2-PROJECT_LOC../src/protocol-game/java - - - src_protocol-relay_java - 2 - PARENT-2-PROJECT_LOC../src/protocol-relay/java - - - src_lwjgl_java - 2 - PARENT-2-PROJECT_LOC../src/lwjgl/java - - - diff --git a/desktopRuntime/eclipseProject/.settings/org.eclipse.jdt.core.prefs b/desktopRuntime/eclipseProject/.settings/org.eclipse.jdt.core.prefs deleted file mode 100755 index c59d0c6a..00000000 --- a/desktopRuntime/eclipseProject/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,15 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning -org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.source=1.8 diff --git a/desktopRuntime/eclipseProject/deps_fix/Java-WebSocket-1.5.1-with-dependencies.jar b/desktopRuntime/eclipseProject/deps_fix/Java-WebSocket-1.5.1-with-dependencies.jar deleted file mode 100755 index 3600c46d..00000000 Binary files a/desktopRuntime/eclipseProject/deps_fix/Java-WebSocket-1.5.1-with-dependencies.jar and /dev/null differ diff --git a/desktopRuntime/eclipseProject/deps_fix/UnsafeMemcpy.jar b/desktopRuntime/eclipseProject/deps_fix/UnsafeMemcpy.jar deleted file mode 100755 index 59fa3597..00000000 Binary files a/desktopRuntime/eclipseProject/deps_fix/UnsafeMemcpy.jar and /dev/null differ diff --git a/desktopRuntime/eclipseProject/deps_fix/codecjorbis-20101023.jar b/desktopRuntime/eclipseProject/deps_fix/codecjorbis-20101023.jar deleted file mode 100755 index e7e1f4a9..00000000 Binary files a/desktopRuntime/eclipseProject/deps_fix/codecjorbis-20101023.jar and /dev/null differ diff --git a/desktopRuntime/eclipseProject/deps_fix/codecwav-20101023.jar b/desktopRuntime/eclipseProject/deps_fix/codecwav-20101023.jar deleted file mode 100755 index 7b6b1bd2..00000000 Binary files a/desktopRuntime/eclipseProject/deps_fix/codecwav-20101023.jar and /dev/null differ diff --git a/desktopRuntime/eclipseProject/deps_fix/lwjgl-egl.jar b/desktopRuntime/eclipseProject/deps_fix/lwjgl-egl.jar deleted file mode 100755 index 34e42c77..00000000 Binary files a/desktopRuntime/eclipseProject/deps_fix/lwjgl-egl.jar and /dev/null differ diff --git a/desktopRuntime/eclipseProject/deps_fix/lwjgl-glfw.jar b/desktopRuntime/eclipseProject/deps_fix/lwjgl-glfw.jar deleted file mode 100755 index 791fe06e..00000000 Binary files a/desktopRuntime/eclipseProject/deps_fix/lwjgl-glfw.jar and /dev/null differ diff --git a/desktopRuntime/eclipseProject/deps_fix/lwjgl-jemalloc.jar b/desktopRuntime/eclipseProject/deps_fix/lwjgl-jemalloc.jar deleted file mode 100755 index 6881c260..00000000 Binary files a/desktopRuntime/eclipseProject/deps_fix/lwjgl-jemalloc.jar and /dev/null differ diff --git a/desktopRuntime/eclipseProject/deps_fix/lwjgl-openal.jar b/desktopRuntime/eclipseProject/deps_fix/lwjgl-openal.jar deleted file mode 100755 index 2a603344..00000000 Binary files a/desktopRuntime/eclipseProject/deps_fix/lwjgl-openal.jar and /dev/null differ diff --git a/desktopRuntime/eclipseProject/deps_fix/lwjgl-opengles.jar b/desktopRuntime/eclipseProject/deps_fix/lwjgl-opengles.jar deleted file mode 100755 index 7bd409f0..00000000 Binary files a/desktopRuntime/eclipseProject/deps_fix/lwjgl-opengles.jar and /dev/null differ diff --git a/desktopRuntime/eclipseProject/deps_fix/lwjgl.jar b/desktopRuntime/eclipseProject/deps_fix/lwjgl.jar deleted file mode 100755 index 2d1fcf91..00000000 Binary files a/desktopRuntime/eclipseProject/deps_fix/lwjgl.jar and /dev/null differ diff --git a/desktopRuntime/eclipseProject/deps_fix/soundsystem-20120107.jar b/desktopRuntime/eclipseProject/deps_fix/soundsystem-20120107.jar deleted file mode 100755 index 52b6468e..00000000 Binary files a/desktopRuntime/eclipseProject/deps_fix/soundsystem-20120107.jar and /dev/null differ diff --git a/desktopRuntime/eclipseProject/deps_fix/webrtc-java-0.8.0.jar b/desktopRuntime/eclipseProject/deps_fix/webrtc-java-0.8.0.jar deleted file mode 100755 index f6eac1aa..00000000 Binary files a/desktopRuntime/eclipseProject/deps_fix/webrtc-java-0.8.0.jar and /dev/null differ diff --git a/desktopRuntime/eclipseProject/eaglercraftDebugRuntime.launch b/desktopRuntime/eclipseProject/eaglercraftDebugRuntime.launch deleted file mode 100755 index c578179b..00000000 --- a/desktopRuntime/eclipseProject/eaglercraftDebugRuntime.launch +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/desktopRuntime/glfw.dll b/desktopRuntime/glfw.dll index 337216c8..1d861da4 100755 Binary files a/desktopRuntime/glfw.dll and b/desktopRuntime/glfw.dll differ diff --git a/desktopRuntime/glfw_license.txt b/desktopRuntime/glfw_license.txt index 8a60e1d8..e7c49d62 100755 --- a/desktopRuntime/glfw_license.txt +++ b/desktopRuntime/glfw_license.txt @@ -1,21 +1,21 @@ -Copyright (c) 2002-2006 Marcus Geelnard -Copyright (c) 2006-2010 Camilla Berglund - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. +Copyright (c) 2002-2006 Marcus Geelnard +Copyright (c) 2006-2010 Camilla Berglund + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. diff --git a/desktopRuntime/javadoc/lwjgl-egl-javadoc.jar b/desktopRuntime/javadoc/lwjgl-egl-javadoc.jar deleted file mode 100755 index d77a3d50..00000000 Binary files a/desktopRuntime/javadoc/lwjgl-egl-javadoc.jar and /dev/null differ diff --git a/desktopRuntime/javadoc/lwjgl-glfw-javadoc.jar b/desktopRuntime/javadoc/lwjgl-glfw-javadoc.jar deleted file mode 100755 index da73b0ef..00000000 Binary files a/desktopRuntime/javadoc/lwjgl-glfw-javadoc.jar and /dev/null differ diff --git a/desktopRuntime/javadoc/lwjgl-javadoc.jar b/desktopRuntime/javadoc/lwjgl-javadoc.jar deleted file mode 100755 index dd18b2c1..00000000 Binary files a/desktopRuntime/javadoc/lwjgl-javadoc.jar and /dev/null differ diff --git a/desktopRuntime/javadoc/lwjgl-jemalloc-javadoc.jar b/desktopRuntime/javadoc/lwjgl-jemalloc-javadoc.jar deleted file mode 100755 index 5db7bdc2..00000000 Binary files a/desktopRuntime/javadoc/lwjgl-jemalloc-javadoc.jar and /dev/null differ diff --git a/desktopRuntime/javadoc/lwjgl-openal-javadoc.jar b/desktopRuntime/javadoc/lwjgl-openal-javadoc.jar deleted file mode 100755 index a8be56f8..00000000 Binary files a/desktopRuntime/javadoc/lwjgl-openal-javadoc.jar and /dev/null differ diff --git a/desktopRuntime/javadoc/lwjgl-opengles-javadoc.jar b/desktopRuntime/javadoc/lwjgl-opengles-javadoc.jar deleted file mode 100755 index 2aaf4d21..00000000 Binary files a/desktopRuntime/javadoc/lwjgl-opengles-javadoc.jar and /dev/null differ diff --git a/desktopRuntime/jemalloc.dll b/desktopRuntime/jemalloc.dll index bc259af6..85b9edf4 100755 Binary files a/desktopRuntime/jemalloc.dll and b/desktopRuntime/jemalloc.dll differ diff --git a/desktopRuntime/jemalloc_license.txt b/desktopRuntime/jemalloc_license.txt index fd579785..81b263a9 100755 --- a/desktopRuntime/jemalloc_license.txt +++ b/desktopRuntime/jemalloc_license.txt @@ -1,27 +1,27 @@ -Unless otherwise specified, files in the jemalloc source distribution are -subject to the following license: --------------------------------------------------------------------------------- -Copyright (C) 2002-2018 Jason Evans . -All rights reserved. -Copyright (C) 2007-2012 Mozilla Foundation. All rights reserved. -Copyright (C) 2009-2018 Facebook, 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: -1. Redistributions of source code must retain the above copyright notice(s), - this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice(s), - 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 HOLDER(S) ``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(S) 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. +Unless otherwise specified, files in the jemalloc source distribution are +subject to the following license: +-------------------------------------------------------------------------------- +Copyright (C) 2002-2018 Jason Evans . +All rights reserved. +Copyright (C) 2007-2012 Mozilla Foundation. All rights reserved. +Copyright (C) 2009-2018 Facebook, 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: +1. Redistributions of source code must retain the above copyright notice(s), + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice(s), + 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 HOLDER(S) ``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(S) 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. -------------------------------------------------------------------------------- \ No newline at end of file diff --git a/desktopRuntime/khronos_license.txt b/desktopRuntime/khronos_license.txt index d7e6e9dd..96c8f0cd 100755 --- a/desktopRuntime/khronos_license.txt +++ b/desktopRuntime/khronos_license.txt @@ -1,22 +1,22 @@ -/* -** Copyright (c) 2013-2014 The Khronos Group Inc. -** -** Permission is hereby granted, free of charge, to any person obtaining a -** copy of this software and/or associated documentation files (the -** "Materials"), to deal in the Materials without restriction, including -** without limitation the rights to use, copy, modify, merge, publish, -** distribute, sublicense, and/or sell copies of the Materials, and to -** permit persons to whom the Materials are furnished to do so, subject to -** the following conditions: -** -** The above copyright notice and this permission notice shall be included -** in all copies or substantial portions of the Materials. -** -** THE MATERIALS 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. -** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +/* +** Copyright (c) 2013-2014 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS 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. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. */ \ No newline at end of file diff --git a/desktopRuntime/libglfw.so b/desktopRuntime/libglfw.so index 69f20433..c52bb715 100755 Binary files a/desktopRuntime/libglfw.so and b/desktopRuntime/libglfw.so differ diff --git a/desktopRuntime/libjemalloc.so b/desktopRuntime/libjemalloc.so index d90d99da..9c943f8e 100755 Binary files a/desktopRuntime/libjemalloc.so and b/desktopRuntime/libjemalloc.so differ diff --git a/desktopRuntime/liblwjgl.so b/desktopRuntime/liblwjgl.so index b2b83088..ab3f1240 100755 Binary files a/desktopRuntime/liblwjgl.so and b/desktopRuntime/liblwjgl.so differ diff --git a/desktopRuntime/liblwjgl_opengles.so b/desktopRuntime/liblwjgl_opengles.so index 7642205e..132ce5fa 100755 Binary files a/desktopRuntime/liblwjgl_opengles.so and b/desktopRuntime/liblwjgl_opengles.so differ diff --git a/desktopRuntime/libopenal.so b/desktopRuntime/libopenal.so index f1add109..3a05c7eb 100755 Binary files a/desktopRuntime/libopenal.so and b/desktopRuntime/libopenal.so differ diff --git a/desktopRuntime/liburing_license.txt b/desktopRuntime/liburing_license.txt index 67bc3301..8c4bad61 100755 --- a/desktopRuntime/liburing_license.txt +++ b/desktopRuntime/liburing_license.txt @@ -1,7 +1,7 @@ -Copyright 2020 Jens Axboe - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - +Copyright 2020 Jens Axboe + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/desktopRuntime/libwebrtc-java.so b/desktopRuntime/libwebrtc-java.so index 4ffd25a3..b3077e17 100755 Binary files a/desktopRuntime/libwebrtc-java.so and b/desktopRuntime/libwebrtc-java.so differ diff --git a/desktopRuntime/lwjgl-egl.jar b/desktopRuntime/lwjgl-egl.jar deleted file mode 100755 index 34e42c77..00000000 Binary files a/desktopRuntime/lwjgl-egl.jar and /dev/null differ diff --git a/desktopRuntime/lwjgl-glfw.jar b/desktopRuntime/lwjgl-glfw.jar deleted file mode 100755 index 791fe06e..00000000 Binary files a/desktopRuntime/lwjgl-glfw.jar and /dev/null differ diff --git a/desktopRuntime/lwjgl-jemalloc.jar b/desktopRuntime/lwjgl-jemalloc.jar deleted file mode 100755 index 6881c260..00000000 Binary files a/desktopRuntime/lwjgl-jemalloc.jar and /dev/null differ diff --git a/desktopRuntime/lwjgl-openal.jar b/desktopRuntime/lwjgl-openal.jar deleted file mode 100755 index 2a603344..00000000 Binary files a/desktopRuntime/lwjgl-openal.jar and /dev/null differ diff --git a/desktopRuntime/lwjgl-opengles.jar b/desktopRuntime/lwjgl-opengles.jar deleted file mode 100755 index 7bd409f0..00000000 Binary files a/desktopRuntime/lwjgl-opengles.jar and /dev/null differ diff --git a/desktopRuntime/lwjgl.dll b/desktopRuntime/lwjgl.dll index ae441145..f4369634 100755 Binary files a/desktopRuntime/lwjgl.dll and b/desktopRuntime/lwjgl.dll differ diff --git a/desktopRuntime/lwjgl.jar b/desktopRuntime/lwjgl.jar deleted file mode 100755 index 2d1fcf91..00000000 Binary files a/desktopRuntime/lwjgl.jar and /dev/null differ diff --git a/desktopRuntime/lwjgl_opengles.dll b/desktopRuntime/lwjgl_opengles.dll index a1b3e94c..cfabf97e 100755 Binary files a/desktopRuntime/lwjgl_opengles.dll and b/desktopRuntime/lwjgl_opengles.dll differ diff --git a/desktopRuntime/openal_soft_license.txt b/desktopRuntime/openal_soft_license.txt index 5bc8fb2c..f4fac144 100755 --- a/desktopRuntime/openal_soft_license.txt +++ b/desktopRuntime/openal_soft_license.txt @@ -1,481 +1,481 @@ - GNU LIBRARY GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1991 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the library GPL. It is - numbered 2 because it goes with version 2 of the ordinary GPL.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Library General Public License, applies to some -specially designated Free Software Foundation software, and to any -other libraries whose authors decide to use it. You can use it for -your libraries, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if -you distribute copies of the library, or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link a program with the library, you must provide -complete object files to the recipients so that they can relink them -with the library, after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - Our method of protecting your rights has two steps: (1) copyright -the library, and (2) offer you this license which gives you legal -permission to copy, distribute and/or modify the library. - - Also, for each distributor's protection, we want to make certain -that everyone understands that there is no warranty for this free -library. If the library is modified by someone else and passed on, we -want its recipients to know that what they have is not the original -version, so that any problems introduced by others will not reflect on -the original authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that companies distributing free -software will individually obtain patent licenses, thus in effect -transforming the program into proprietary software. To prevent this, -we have made it clear that any patent must be licensed for everyone's -free use or not licensed at all. - - Most GNU software, including some libraries, is covered by the ordinary -GNU General Public License, which was designed for utility programs. This -license, the GNU Library General Public License, applies to certain -designated libraries. This license is quite different from the ordinary -one; be sure to read it in full, and don't assume that anything in it is -the same as in the ordinary license. - - The reason we have a separate public license for some libraries is that -they blur the distinction we usually make between modifying or adding to a -program and simply using it. Linking a program with a library, without -changing the library, is in some sense simply using the library, and is -analogous to running a utility program or application program. However, in -a textual and legal sense, the linked executable is a combined work, a -derivative of the original library, and the ordinary General Public License -treats it as such. - - Because of this blurred distinction, using the ordinary General -Public License for libraries did not effectively promote software -sharing, because most developers did not use the libraries. We -concluded that weaker conditions might promote sharing better. - - However, unrestricted linking of non-free programs would deprive the -users of those programs of all benefit from the free status of the -libraries themselves. This Library General Public License is intended to -permit developers of non-free programs to use free libraries, while -preserving your freedom as a user of such programs to change the free -libraries that are incorporated in them. (We have not seen how to achieve -this as regards changes in header files, but we have achieved it as regards -changes in the actual functions of the Library.) The hope is that this -will lead to faster development of free libraries. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, while the latter only -works together with the library. - - Note that it is possible for a library to be covered by the ordinary -General Public License rather than by this special one. - - GNU LIBRARY GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library which -contains a notice placed by the copyright holder or other authorized -party saying it may be distributed under the terms of this Library -General Public License (also called "this License"). Each licensee is -addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also compile or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - c) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - d) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the source code distributed need not include anything that is normally -distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Library General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/desktopRuntime/resources/EPKVersionIdentifier.txt b/desktopRuntime/resources/EPKVersionIdentifier.txt index 6a4763ae..7d42c77c 100755 --- a/desktopRuntime/resources/EPKVersionIdentifier.txt +++ b/desktopRuntime/resources/EPKVersionIdentifier.txt @@ -1 +1 @@ -u50 \ No newline at end of file +u51 \ No newline at end of file diff --git a/desktopRuntime/resources/assets/eagler/CREDITS.txt b/desktopRuntime/resources/assets/eagler/CREDITS.txt index d9b6eeb9..93cdf253 100755 --- a/desktopRuntime/resources/assets/eagler/CREDITS.txt +++ b/desktopRuntime/resources/assets/eagler/CREDITS.txt @@ -6,21 +6,20 @@ - Creator of Eaglercraft - Ported the Minecraft 1.8 src to TeaVM - - Wrote HW accelerated OpenGL 1.3 emulator - - Wrote the default shader pack + - Optimized the Minecraft 1.8 src + - Made the platform abstraction layer + - Made HW accelerated OpenGL 1.3 emulator + - Made the default shader pack - Made the integrated PBR resource pack - Added touch and mobile device support - - Wrote all desktop emulation code - - Wrote EaglercraftXBungee - - Wrote EaglercraftXVelocity - - Wrote WebRTC relay server - - Wrote voice chat server - - Wrote the patch and build system + - Made the multiplayer backends + - Made the shared world relay server + - Made the patch and build system ayunami2000: - Many bug fixes - - WebRTC LAN worlds + - WebRTC shared worlds - WebRTC voice chat - Worked on touch support - Made velocity plugin work @@ -29,6 +28,11 @@ - Added seamless fullscreen - Created the replit + cire3: + + - Created the Gradle plugin + - Proponent of Kotlin DSL + Code used within EaglercraftX diff --git a/desktopRuntime/resources/assets/eagler/glsl/deferred/moon_render.fsh b/desktopRuntime/resources/assets/eagler/glsl/deferred/moon_render.fsh index 4615c211..54fb7e6b 100755 --- a/desktopRuntime/resources/assets/eagler/glsl/deferred/moon_render.fsh +++ b/desktopRuntime/resources/assets/eagler/glsl/deferred/moon_render.fsh @@ -32,34 +32,42 @@ uniform sampler2D u_moonTextures; uniform sampler2D u_cloudsTexture; #define MOON_SURFACE 0.9 -#define MOON_MARGIN 0.0125 +#define MOON_MARGIN 0.025 +#define MOON_MARGIN_1NSQ ((1.0 - MOON_MARGIN) * (1.0 - MOON_MARGIN)) +#define ATMOSPHERE_BIAS 0.02 void main() { gl_FragDepth = 0.0; vec2 coord2f = v_position2f * 2.0 - 1.0; - vec2 texUV = (coord2f * (1.0 / (MOON_SURFACE + MOON_MARGIN))) * 0.5 + 0.5; + vec2 surfaceCoord2f = coord2f * (1.0 / MOON_SURFACE); + float surfaceCoord2fDot = dot(surfaceCoord2f, surfaceCoord2f); + vec3 moonNormal3f; vec4 color4f = vec4(0.0); - if(texUV == clamp(texUV, vec2(0.0), vec2(1.0))) { - color4f = texture(u_moonTextures, texUV); + float NdotV = 0.0; + float atmos = 1.0; + if(surfaceCoord2fDot < MOON_MARGIN_1NSQ) { + color4f = texture(u_moonTextures, surfaceCoord2f * 0.5 + 0.5); + moonNormal3f.xy = color4f.rg * 2.0 - 1.0; + moonNormal3f.z = sqrt(1.0 - dot(moonNormal3f.xy, moonNormal3f.xy)); + NdotV = max(dot(moonNormal3f, u_lightDir3f), 0.0); + atmos = 0.0; } - vec3 moonNormal3f; - moonNormal3f.xy = color4f.rg * 2.0 - 1.0; - moonNormal3f.z = sqrt(1.0 - dot(moonNormal3f.xy, moonNormal3f.xy)); - float NdotV = max(dot(moonNormal3f, u_lightDir3f), 0.0); + float stupid = max(-u_lightDir3f.z - 0.5, 0.0) * 1.25; + stupid *= stupid * stupid; + vec3 viewDir = normalize(v_position3f); - vec2 surfaceCoord2f = coord2f * (1.0 / MOON_SURFACE); - vec3 moonAtmosNormalInner3f = vec3(surfaceCoord2f, sqrt(1.0 - dot(surfaceCoord2f, surfaceCoord2f))); - vec3 moonAtmosNormalOuter3f = vec3(surfaceCoord2f, sqrt(-1.0 + dot(surfaceCoord2f, surfaceCoord2f))); + vec3 moonAtmosNormalInner3f = vec3(surfaceCoord2f, sqrt((MOON_MARGIN_1NSQ + ATMOSPHERE_BIAS) - surfaceCoord2fDot)); + vec3 moonAtmosNormalOuter3f = vec3(surfaceCoord2f, sqrt((-MOON_MARGIN_1NSQ + ATMOSPHERE_BIAS) + surfaceCoord2fDot)); float NdotVInner = max(dot(moonAtmosNormalInner3f, u_lightDir3f), 0.0); - float NdotVOuter = max(dot(moonAtmosNormalOuter3f, u_lightDir3f) + 0.65, 0.0); + float NdotVOuter = max(dot(moonAtmosNormalOuter3f, u_lightDir3f) + 0.35, 0.0); + float atmosInner = max((MOON_SURFACE * 0.2 + stupid) / moonAtmosNormalInner3f.z - 0.2, 0.0); + float atmosOuter = max((MOON_SURFACE * 0.2 + stupid) / moonAtmosNormalOuter3f.z - 0.4, 0.0); + float atmosTotal = (1.0 - atmos) * NdotVInner * atmosInner + atmos * NdotVOuter * atmosOuter; - float atmosInner = max((MOON_SURFACE * 0.2) / moonAtmosNormalInner3f.z - 0.2, 0.0); - float atmosOuter = max((MOON_SURFACE * 0.2) / moonAtmosNormalOuter3f.z - 0.4, 0.0); - - output4f = vec4(u_moonColor3f * (color4f.b * color4f.b * NdotV + (NdotVInner * atmosInner + NdotVOuter * atmosOuter * vec3(0.8, 0.825, 0.9)) * (0.5 - max(u_lightDir3f.z, 0.0) * 0.25)), 0.0); + output4f = vec4(u_moonColor3f * (color4f.b * color4f.b * NdotV + (atmosTotal * vec3(0.42, 0.5, 0.56))), 0.0); if(viewDir.y < 0.01) { return; diff --git a/desktopRuntime/resources/assets/eagler/glsl/deferred/shader_pack_info.json b/desktopRuntime/resources/assets/eagler/glsl/deferred/shader_pack_info.json index 29f5ad14..0180717c 100755 --- a/desktopRuntime/resources/assets/eagler/glsl/deferred/shader_pack_info.json +++ b/desktopRuntime/resources/assets/eagler/glsl/deferred/shader_pack_info.json @@ -1,7 +1,7 @@ { "name": "§eHigh Performance PBR", "desc": "Pack made from scratch specifically for this client, designed to give what I call the best balance between quality and performance possible in a browser but obviously that's just my opinion", - "vers": "1.4.0", + "vers": "1.4.1", "author": "lax1dude", "api_vers": 1, "features": [ diff --git a/desktopRuntime/resources/assets/eagler/glsl/dynamiclights/accel_particle_dynamiclights.fsh b/desktopRuntime/resources/assets/eagler/glsl/dynamiclights/accel_particle_dynamiclights.fsh index 1775d982..6e9a8f63 100755 --- a/desktopRuntime/resources/assets/eagler/glsl/dynamiclights/accel_particle_dynamiclights.fsh +++ b/desktopRuntime/resources/assets/eagler/glsl/dynamiclights/accel_particle_dynamiclights.fsh @@ -54,13 +54,12 @@ void main() { if(u_dynamicLightCount1i > 0) { vec4 worldPosition4f = u_inverseViewMatrix4f * v_position4f; worldPosition4f.xyz /= worldPosition4f.w; - vec3 normalVector3f = normalize(u_inverseViewMatrix4f[2].xyz); int safeLightCount = u_dynamicLightCount1i > 12 ? 0 : u_dynamicLightCount1i; for(int i = 0; i < safeLightCount; ++i) { light = u_dynamicLightArray[i]; light.xyz = light.xyz - worldPosition4f.xyz; len = length(light.xyz); - diffuse += max(dot(light.xyz / len, normalVector3f) * 0.8 + 0.2, 0.0) * max(light.w - len, 0.0); + diffuse += max(light.w - len, 0.0); } blockLight = min(blockLight + diffuse * 0.066667, 1.0); } diff --git a/desktopRuntime/resources/assets/eagler/glsl/dynamiclights/core_dynamiclights.fsh b/desktopRuntime/resources/assets/eagler/glsl/dynamiclights/core_dynamiclights.fsh index 44c20663..985a0618 100755 --- a/desktopRuntime/resources/assets/eagler/glsl/dynamiclights/core_dynamiclights.fsh +++ b/desktopRuntime/resources/assets/eagler/glsl/dynamiclights/core_dynamiclights.fsh @@ -145,12 +145,6 @@ void main() { #endif #endif -#ifdef COMPILE_NORMAL_ATTRIB - vec3 normal = v_normal3f; -#else - vec3 normal = u_uniformNormal3f; -#endif - #ifdef COMPILE_ENABLE_LIGHTMAP float diffuse = 0.0; #ifdef COMPILE_LIGHTMAP_ATTRIB @@ -163,13 +157,12 @@ void main() { if(u_dynamicLightCount1i > 0) { vec4 worldPosition4f = u_inverseViewMatrix4f * v_position4f; worldPosition4f.xyz /= worldPosition4f.w; - vec3 normalVector3f = normalize(mat3(u_inverseViewMatrix4f) * normal); int safeLightCount = u_dynamicLightCount1i > 12 ? 0 : u_dynamicLightCount1i; for(int i = 0; i < safeLightCount; ++i) { light = u_dynamicLightArray[i]; light.xyz = light.xyz - worldPosition4f.xyz; len = length(light.xyz); - diffuse += max(dot(light.xyz / len, normalVector3f) * 0.8 + 0.2, 0.0) * max(light.w - len, 0.0); + diffuse += max(light.w - len, 0.0); } blockLight = min(blockLight + diffuse * 0.066667, 1.0); } @@ -190,6 +183,12 @@ void main() { #endif +#ifdef COMPILE_NORMAL_ATTRIB + vec3 normal = v_normal3f; +#else + vec3 normal = u_uniformNormal3f; +#endif + #ifdef COMPILE_ENABLE_MC_LIGHTING #ifndef COMPILE_ENABLE_LIGHTMAP vec4 light; diff --git a/desktopRuntime/resources/assets/minecraft/lang/en_US.lang b/desktopRuntime/resources/assets/minecraft/lang/en_US.lang index 86cf0886..4655647f 100755 --- a/desktopRuntime/resources/assets/minecraft/lang/en_US.lang +++ b/desktopRuntime/resources/assets/minecraft/lang/en_US.lang @@ -207,8 +207,9 @@ eaglercraft.shaders.gui.option.REALISTIC_WATER.desc.3=OFF: render vanilla water eaglercraft.shaders.gui.option.LIGHT_SHAFTS.label=God Rays eaglercraft.shaders.gui.option.LIGHT_SHAFTS.desc.0=Render god rays (light shafts) for sunlight and moonlight shadows -eaglercraft.shaders.gui.option.LIGHT_SHAFTS.desc.2=ON: render god rays (slower) -eaglercraft.shaders.gui.option.LIGHT_SHAFTS.desc.3=OFF: disable god rays (faster) +eaglercraft.shaders.gui.option.LIGHT_SHAFTS.desc.2=This shader tends to look unpleasent if the render distance is greater than the shadow map size +eaglercraft.shaders.gui.option.LIGHT_SHAFTS.desc.4=ON: render god rays (slower) +eaglercraft.shaders.gui.option.LIGHT_SHAFTS.desc.5=OFF: disable god rays (faster) eaglercraft.shaders.gui.option.SCREEN_SPACE_REFLECTIONS.label=Raytracing @@ -760,12 +761,16 @@ eaglercraft.fallbackWebViewScreen.exited=(exited) eaglercraft.fallbackWebViewScreen.openButton=Open eaglercraft.fallbackWebViewScreen.exitButton=Exit -eaglercraft.webviewPhishingWaring.title=WARNING!!! -eaglercraft.webviewPhishingWaring.text0=If you see a login page, think before you enter a password -eaglercraft.webviewPhishingWaring.text1=Passwords can be stolen by the owner of this server or website -eaglercraft.webviewPhishingWaring.text2=Do not log in to accounts you don't want hackers to steal -eaglercraft.webviewPhishingWaring.dontShowAgain=Do not show this message again -eaglercraft.webviewPhishingWaring.continue=Continue +eaglercraft.webviewPhishingWarning.title=WARNING!!! +eaglercraft.webviewPhishingWarning.text0=If you see a login page, think before you enter a password +eaglercraft.webviewPhishingWarning.text1=Passwords can be stolen by the owner of this server or website +eaglercraft.webviewPhishingWarning.text2=Do not log in to accounts you don't want hackers to steal +eaglercraft.webviewPhishingWarning.dontShowAgain=Do not show this message again +eaglercraft.webviewPhishingWarning.continue=Continue + +eaglercraft.webviewDisplayWarning.title=WebView Warning +eaglercraft.webviewDisplayWarning.text0=This server is attempting to display HTML content +eaglercraft.webviewDisplayWarning.text1=Would you like to continue? eaglercraft.notifications.title=Notifications eaglercraft.notifications.priority=Priority: %s diff --git a/desktopRuntime/resources/plugin_download.zip b/desktopRuntime/resources/plugin_download.zip deleted file mode 100755 index 072ba131..00000000 Binary files a/desktopRuntime/resources/plugin_download.zip and /dev/null differ diff --git a/desktopRuntime/resources/plugin_version.json b/desktopRuntime/resources/plugin_version.json deleted file mode 100755 index a0ab409b..00000000 --- a/desktopRuntime/resources/plugin_version.json +++ /dev/null @@ -1 +0,0 @@ -{"pluginName":"EaglercraftXBungee","pluginVersion":"1.3.6","pluginButton":"Download \"EaglerXBungee-1.3.6.jar\"","pluginFilename":"EaglerXBungee.zip"} \ No newline at end of file diff --git a/desktopRuntime/webrtc-java-0.8.0.jar b/desktopRuntime/webrtc-java-0.8.0.jar deleted file mode 100755 index f6eac1aa..00000000 Binary files a/desktopRuntime/webrtc-java-0.8.0.jar and /dev/null differ diff --git a/desktopRuntime/webrtc-java.dll b/desktopRuntime/webrtc-java.dll index 00b5ad53..73aa420b 100755 Binary files a/desktopRuntime/webrtc-java.dll and b/desktopRuntime/webrtc-java.dll differ diff --git a/gradle.properties b/gradle.properties index a36eb1cc..2a4ae6b8 100755 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,2 @@ -org.gradle.jvmargs=-Xmx2G -Xms2G \ No newline at end of file +org.gradle.jvmargs=-Xmx4G -Xms4G +org.gradle.problems.report=false \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100755 index 00000000..1b5381b6 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,12 @@ +[libraries] +hppc = "com.carrotsearch:hppc:0.10.0" +jsr305 = "com.google.code.findbugs:jsr305:3.0.2" +lang3 = "org.apache.commons:commons-lang3:3.6" +jorbis = "org.jcraft:jorbis:0.0.17" + +[bundles] +common = [ + "hppc", + "jsr305", + "lang3" +] \ No newline at end of file diff --git a/gradle/local-libs/com/resentclient/oss/eaglercraft/build/com.resentclient.oss.eaglercraft.build.gradle.plugin/0.0.0/com.resentclient.oss.eaglercraft.build.gradle.plugin-0.0.0.pom b/gradle/local-libs/com/resentclient/oss/eaglercraft/build/com.resentclient.oss.eaglercraft.build.gradle.plugin/0.0.0/com.resentclient.oss.eaglercraft.build.gradle.plugin-0.0.0.pom new file mode 100755 index 00000000..95057a4c --- /dev/null +++ b/gradle/local-libs/com/resentclient/oss/eaglercraft/build/com.resentclient.oss.eaglercraft.build.gradle.plugin/0.0.0/com.resentclient.oss.eaglercraft.build.gradle.plugin-0.0.0.pom @@ -0,0 +1,15 @@ + + + 4.0.0 + com.resentclient.oss.eaglercraft.build + com.resentclient.oss.eaglercraft.build.gradle.plugin + 0.0.0 + pom + + + com.resentclient.oss.eaglercraft.build + plugin + 0.0.0 + + + diff --git a/gradle/local-libs/com/resentclient/oss/eaglercraft/build/com.resentclient.oss.eaglercraft.build.gradle.plugin/maven-metadata.xml b/gradle/local-libs/com/resentclient/oss/eaglercraft/build/com.resentclient.oss.eaglercraft.build.gradle.plugin/maven-metadata.xml new file mode 100755 index 00000000..c18153a5 --- /dev/null +++ b/gradle/local-libs/com/resentclient/oss/eaglercraft/build/com.resentclient.oss.eaglercraft.build.gradle.plugin/maven-metadata.xml @@ -0,0 +1,13 @@ + + + com.resentclient.oss.eaglercraft.build + com.resentclient.oss.eaglercraft.build.gradle.plugin + + 0.0.0 + 0.0.0 + + 0.0.0 + + 20250517182704 + + diff --git a/gradle/local-libs/com/resentclient/oss/eaglercraft/build/plugin/0.0.0/plugin-0.0.0.jar b/gradle/local-libs/com/resentclient/oss/eaglercraft/build/plugin/0.0.0/plugin-0.0.0.jar new file mode 100755 index 00000000..f35a65ee Binary files /dev/null and b/gradle/local-libs/com/resentclient/oss/eaglercraft/build/plugin/0.0.0/plugin-0.0.0.jar differ diff --git a/gradle/local-libs/com/resentclient/oss/eaglercraft/build/plugin/0.0.0/plugin-0.0.0.module b/gradle/local-libs/com/resentclient/oss/eaglercraft/build/plugin/0.0.0/plugin-0.0.0.module new file mode 100755 index 00000000..300537eb --- /dev/null +++ b/gradle/local-libs/com/resentclient/oss/eaglercraft/build/plugin/0.0.0/plugin-0.0.0.module @@ -0,0 +1,93 @@ +{ + "formatVersion": "1.1", + "component": { + "group": "com.resentclient.oss.eaglercraft.build", + "module": "plugin", + "version": "0.0.0", + "attributes": { + "org.gradle.status": "release" + } + }, + "createdBy": { + "gradle": { + "version": "8.10" + } + }, + "variants": [ + { + "name": "apiElements", + "attributes": { + "org.gradle.category": "library", + "org.gradle.dependency.bundling": "external", + "org.gradle.jvm.environment": "standard-jvm", + "org.gradle.jvm.version": 8, + "org.gradle.libraryelements": "jar", + "org.gradle.usage": "java-api", + "org.jetbrains.kotlin.platform.type": "jvm" + }, + "files": [ + { + "name": "plugin-0.0.0.jar", + "url": "plugin-0.0.0.jar", + "size": 84888, + "sha512": "441fa73bc42f953cef96f58b77a9e1974f41517a19d8dd37818aad73657d4d08a86cbf5cc7a7a5a1d07017845044818a0e45b75c40549b591d418cb88b67ff1e", + "sha256": "41f5f930f9bc3547e35ae35599a8d80de3e006fa6de7ebe88430c647f949ada8", + "sha1": "a092edfdb05dd70020149fac35ec55886b4aab48", + "md5": "f21b11b5c1d7e5120e70b2c98d8a84bf" + } + ] + }, + { + "name": "runtimeElements", + "attributes": { + "org.gradle.category": "library", + "org.gradle.dependency.bundling": "external", + "org.gradle.jvm.environment": "standard-jvm", + "org.gradle.jvm.version": 8, + "org.gradle.libraryelements": "jar", + "org.gradle.usage": "java-runtime", + "org.jetbrains.kotlin.platform.type": "jvm" + }, + "dependencies": [ + { + "group": "com.jcraft", + "module": "jzlib", + "version": { + "requires": "1.1.3" + } + }, + { + "group": "org.tukaani", + "module": "xz", + "version": { + "requires": "1.10" + } + }, + { + "group": "org.jetbrains.kotlin", + "module": "kotlin-stdlib-jdk8" + } + ], + "dependencyConstraints": [ + { + "group": "org.jetbrains.kotlin", + "module": "kotlin-stdlib-jdk8", + "version": { + "requires": "2.1.10" + } + } + ], + "files": [ + { + "name": "plugin-0.0.0.jar", + "url": "plugin-0.0.0.jar", + "size": 84888, + "sha512": "441fa73bc42f953cef96f58b77a9e1974f41517a19d8dd37818aad73657d4d08a86cbf5cc7a7a5a1d07017845044818a0e45b75c40549b591d418cb88b67ff1e", + "sha256": "41f5f930f9bc3547e35ae35599a8d80de3e006fa6de7ebe88430c647f949ada8", + "sha1": "a092edfdb05dd70020149fac35ec55886b4aab48", + "md5": "f21b11b5c1d7e5120e70b2c98d8a84bf" + } + ] + } + ] +} diff --git a/gradle/local-libs/com/resentclient/oss/eaglercraft/build/plugin/0.0.0/plugin-0.0.0.pom b/gradle/local-libs/com/resentclient/oss/eaglercraft/build/plugin/0.0.0/plugin-0.0.0.pom new file mode 100755 index 00000000..d5e26c8a --- /dev/null +++ b/gradle/local-libs/com/resentclient/oss/eaglercraft/build/plugin/0.0.0/plugin-0.0.0.pom @@ -0,0 +1,40 @@ + + + + + + + + 4.0.0 + com.resentclient.oss.eaglercraft.build + plugin + 0.0.0 + + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + 2.1.10 + + + + + + com.jcraft + jzlib + 1.1.3 + runtime + + + org.tukaani + xz + 1.10 + runtime + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + runtime + + + diff --git a/gradle/local-libs/com/resentclient/oss/eaglercraft/build/plugin/maven-metadata.xml b/gradle/local-libs/com/resentclient/oss/eaglercraft/build/plugin/maven-metadata.xml new file mode 100755 index 00000000..06baea8a --- /dev/null +++ b/gradle/local-libs/com/resentclient/oss/eaglercraft/build/plugin/maven-metadata.xml @@ -0,0 +1,13 @@ + + + com.resentclient.oss.eaglercraft.build + plugin + + 0.0.0 + 0.0.0 + + 0.0.0 + + 20250517182708 + + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index cc4fdc29..7f93135c 100755 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6b932e82..ce74f1d7 100755 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 2fe81a7d..1aa94a42 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,78 +17,111 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -97,87 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 24467a14..9618d8d9 100755 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,100 +1,100 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle deleted file mode 100755 index 361c954d..00000000 --- a/settings.gradle +++ /dev/null @@ -1,10 +0,0 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * The settings file is used to specify which projects to include in your build. - * - * Detailed information about configuring a multi-project build in Gradle can be found - * in the user manual at https://docs.gradle.org/6.0/userguide/multi_project_builds.html - */ - -rootProject.name = 'eaglercraft-workspace' diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100755 index 00000000..b9a85620 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,22 @@ +import java.io.File + +pluginManagement { + repositories { + gradlePluginPortal() + maven { + name = "eagler-teavm" + url = uri("https://eaglercraft-teavm-fork.github.io/maven/") + } + maven { + name = "eagler-local" + url = uri(File(rootDir, "gradle/local-libs")) + } + mavenCentral() + } +} + +rootProject.name = "eaglercraft-workspace" + +include("target_lwjgl_desktop") +include("target_teavm_javascript") +include("target_teavm_wasm_gc") diff --git a/src/game/java/net/minecraft/client/LoadingScreenRenderer.java b/src/game/java/net/minecraft/client/LoadingScreenRenderer.java index 11a10df5..03d53287 100755 --- a/src/game/java/net/minecraft/client/LoadingScreenRenderer.java +++ b/src/game/java/net/minecraft/client/LoadingScreenRenderer.java @@ -10,7 +10,6 @@ import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.resources.I18n; import net.minecraft.util.IProgressUpdate; -import net.minecraft.util.MinecraftError; /**+ * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. @@ -70,11 +69,7 @@ public class LoadingScreenRenderer implements IProgressUpdate { private void displayString(String message) { this.currentlyDisplayedText = message; - if (!this.mc.running) { - if (!this.field_73724_e) { - throw new MinecraftError(); - } - } else { + if (this.mc.running) { GlStateManager.clear(GL_DEPTH_BUFFER_BIT); GlStateManager.matrixMode(GL_PROJECTION); GlStateManager.loadIdentity(); @@ -91,11 +86,7 @@ public class LoadingScreenRenderer implements IProgressUpdate { * what is being done currently. */ public void displayLoadingString(String message) { - if (!this.mc.running) { - if (!this.field_73724_e) { - throw new MinecraftError(); - } - } else { + if (this.mc.running) { this.systemTime = 0L; this.message = message; this.setLoadingProgress(-1); @@ -104,11 +95,7 @@ public class LoadingScreenRenderer implements IProgressUpdate { } public void eaglerShow(String line1, String line2) { - if (!this.mc.running) { - if (!this.field_73724_e) { - throw new MinecraftError(); - } - } else { + if (this.mc.running) { this.systemTime = 0L; this.currentlyDisplayedText = line1; this.message = line2; @@ -126,11 +113,7 @@ public class LoadingScreenRenderer implements IProgressUpdate { * specified amount. Args: loadProgress */ public void setLoadingProgress(int progress) { - if (!this.mc.running) { - if (!this.field_73724_e) { - throw new MinecraftError(); - } - } else { + if (this.mc.running) { long i = Minecraft.getSystemTime(); if (i - this.systemTime >= 100L) { this.systemTime = i; diff --git a/src/game/java/net/minecraft/client/Minecraft.java b/src/game/java/net/minecraft/client/Minecraft.java index f201d44e..a8b1cb0b 100755 --- a/src/game/java/net/minecraft/client/Minecraft.java +++ b/src/game/java/net/minecraft/client/Minecraft.java @@ -16,7 +16,6 @@ import net.lax1dude.eaglercraft.v1_8.ClientUUIDLoadingCache; import net.lax1dude.eaglercraft.v1_8.Display; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EagUtils; -import net.lax1dude.eaglercraft.v1_8.EaglerXBungeeVersion; import net.lax1dude.eaglercraft.v1_8.HString; import net.lax1dude.eaglercraft.v1_8.IOUtils; import net.lax1dude.eaglercraft.v1_8.Keyboard; @@ -34,6 +33,7 @@ import net.lax1dude.eaglercraft.v1_8.futures.Executors; import net.lax1dude.eaglercraft.v1_8.futures.FutureTask; import net.lax1dude.eaglercraft.v1_8.futures.ListenableFuture; import net.lax1dude.eaglercraft.v1_8.futures.ListenableFutureTask; +import net.lax1dude.eaglercraft.v1_8.internal.ContextLostError; import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformType; import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC; @@ -67,6 +67,7 @@ import net.lax1dude.eaglercraft.v1_8.profile.SkinPreviewRenderer; import net.lax1dude.eaglercraft.v1_8.socket.AddressResolver; import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; import net.lax1dude.eaglercraft.v1_8.socket.RateLimitTracker; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.client.StateFlags; import net.lax1dude.eaglercraft.v1_8.sp.IntegratedServerState; import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; import net.lax1dude.eaglercraft.v1_8.sp.SkullCommand; @@ -182,7 +183,6 @@ import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.FrameTimer; import net.minecraft.util.IThreadListener; import net.minecraft.util.MathHelper; -import net.minecraft.util.MinecraftError; import net.minecraft.util.MouseHelper; import net.minecraft.util.MovementInputFromOptions; import net.minecraft.util.MovingObjectPosition; @@ -384,9 +384,10 @@ public class Minecraft implements IThreadListener { } this.displayCrashReport(this.crashReporter); + return; } - } catch (MinecraftError var12) { - // ?? + } catch (ContextLostError err) { + throw err; } catch (ReportedException reportedexception) { this.addGraphicsAndWorldToCrashReport(reportedexception.getCrashReport()); logger.fatal("Reported exception thrown!", reportedexception); @@ -842,6 +843,8 @@ public class Minecraft implements IThreadListener { this.shutdown(); } + Display.checkContextLost(); + PointerInputAbstraction.runGameLoop(); this.gameSettings.touchscreen = PointerInputAbstraction.isTouchMode(); @@ -872,27 +875,25 @@ public class Minecraft implements IThreadListener { this.checkGLError("Pre render"); this.mcSoundHandler.setListener(this.thePlayer, this.timer.renderPartialTicks); - if (!Display.contextLost()) { - EaglercraftGPU.optimize(); - _wglBindFramebuffer(0x8D40, null); - GlStateManager.viewport(0, 0, this.displayWidth, this.displayHeight); - GlStateManager.clearColor(0.0f, 0.0f, 0.0f, 1.0f); - GlStateManager.pushMatrix(); - GlStateManager.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - GlStateManager.enableTexture2D(); - if (this.thePlayer != null && this.thePlayer.isEntityInsideOpaqueBlock()) { - this.gameSettings.thirdPersonView = 0; - } - - if (!this.skipRenderWorld) { - this.entityRenderer.func_181560_a(this.timer.renderPartialTicks, i); - } - - this.guiAchievement.updateAchievementWindow(); - this.touchOverlayRenderer.render(displayWidth, displayHeight, scaledResolution); - GlStateManager.popMatrix(); + EaglercraftGPU.optimize(); + _wglBindFramebuffer(0x8D40, null); + GlStateManager.viewport(0, 0, this.displayWidth, this.displayHeight); + GlStateManager.clearColor(0.0f, 0.0f, 0.0f, 1.0f); + GlStateManager.pushMatrix(); + GlStateManager.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GlStateManager.enableTexture2D(); + if (this.thePlayer != null && this.thePlayer.isEntityInsideOpaqueBlock()) { + this.gameSettings.thirdPersonView = 0; } + if (!this.skipRenderWorld) { + this.entityRenderer.func_181560_a(this.timer.renderPartialTicks, i); + } + + this.guiAchievement.updateAchievementWindow(); + this.touchOverlayRenderer.render(displayWidth, displayHeight, scaledResolution); + GlStateManager.popMatrix(); + this.updateDisplay(); this.checkGLError("Post render"); @@ -1220,7 +1221,7 @@ public class Minecraft implements IThreadListener { this.ingameGUI.updateTick(); } - VoiceClientController.tickVoiceClient(this); + VoiceClientController.tickVoiceClient(); this.entityRenderer.getMouseOver(1.0F); if (!this.isGamePaused && this.theWorld != null) { @@ -1699,29 +1700,29 @@ public class Minecraft implements IThreadListener { if (bungeeOutdatedMsgTimer > 0) { if (--bungeeOutdatedMsgTimer == 0 && this.thePlayer.sendQueue != null) { String pluginBrand = this.thePlayer.sendQueue.getNetworkManager().getPluginBrand(); - String pluginVersion = this.thePlayer.sendQueue.getNetworkManager().getPluginVersion(); - if (pluginBrand != null && pluginVersion != null - && EaglerXBungeeVersion.isUpdateToPluginAvailable(pluginBrand, pluginVersion)) { + if (pluginBrand != null && ("EaglercraftXBungee".equals(pluginBrand) + || "EaglercraftXVelocity".equals(pluginBrand))) { String pfx = EnumChatFormatting.GOLD + "[EagX]" + EnumChatFormatting.AQUA; ingameGUI.getChatGUI().printChatMessage( new ChatComponentText(pfx + " ---------------------------------------")); + ingameGUI.getChatGUI().printChatMessage(new ChatComponentText(pfx + " This server is running " + + EnumChatFormatting.YELLOW + pluginBrand + EnumChatFormatting.AQUA + ",")); ingameGUI.getChatGUI().printChatMessage( - new ChatComponentText(pfx + " This server appears to be using version " - + EnumChatFormatting.YELLOW + pluginVersion)); + new ChatComponentText(pfx + " which has been discontinued by lax1dude.")); + ingameGUI.getChatGUI().printChatMessage(new ChatComponentText(pfx)); ingameGUI.getChatGUI().printChatMessage( - new ChatComponentText(pfx + " of the EaglerXBungee plugin which is outdated")); + new ChatComponentText(pfx + " If you are the admin of this server, please")); + ingameGUI.getChatGUI().printChatMessage( + new ChatComponentText(pfx + " upgrade to EaglercraftXServer if you want to")); + ingameGUI.getChatGUI().printChatMessage( + new ChatComponentText(pfx + " to continue to receive support and bugfixes.")); ingameGUI.getChatGUI().printChatMessage(new ChatComponentText(pfx)); ingameGUI.getChatGUI() - .printChatMessage(new ChatComponentText(pfx + " If you are the admin update to " - + EnumChatFormatting.YELLOW + EaglerXBungeeVersion.getPluginVersion() - + EnumChatFormatting.AQUA + " or newer")); - ingameGUI.getChatGUI().printChatMessage(new ChatComponentText(pfx)); - ingameGUI.getChatGUI().printChatMessage((new ChatComponentText(pfx + " Click: ")) - .appendSibling((new ChatComponentText("" + EnumChatFormatting.GREEN - + EnumChatFormatting.UNDERLINE + EaglerXBungeeVersion.getPluginButton())) - .setChatStyle((new ChatStyle()).setChatClickEvent( - new ClickEvent(ClickEvent.Action.EAGLER_PLUGIN_DOWNLOAD, - "plugin_download.zip"))))); + .printChatMessage((new ChatComponentText(pfx + " " + EnumChatFormatting.GREEN + + EnumChatFormatting.UNDERLINE + "https://lax1dude.net/eaglerxserver")) + .setChatStyle((new ChatStyle()) + .setChatClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, + "https://lax1dude.net/eaglerxserver")))); ingameGUI.getChatGUI().printChatMessage( new ChatComponentText(pfx + " ---------------------------------------")); } @@ -1877,7 +1878,7 @@ public class Minecraft implements IThreadListener { EaglerProfile.clearServerSkinOverride(); PauseMenuCustomizeState.reset(); ClientUUIDLoadingCache.flushRequestCache(); - ClientUUIDLoadingCache.resetFlags(); + StateFlags.reset(); WebViewOverlayController.setPacketSendCallback(null); this.guiAchievement.clearAchievements(); @@ -2068,7 +2069,7 @@ public class Minecraft implements IThreadListener { object = Items.spawn_egg; i = EntityList.getEntityID(this.objectMouseOver.entityHit); flag1 = true; - if (!EntityList.entityEggs.containsKey(Integer.valueOf(i))) { + if (!EntityList.entityEggs.containsKey(i)) { return; } } diff --git a/src/game/java/net/minecraft/client/gui/GuiMultiplayer.java b/src/game/java/net/minecraft/client/gui/GuiMultiplayer.java index 0ab52839..6c7fea95 100755 --- a/src/game/java/net/minecraft/client/gui/GuiMultiplayer.java +++ b/src/game/java/net/minecraft/client/gui/GuiMultiplayer.java @@ -6,7 +6,6 @@ import com.google.common.base.Splitter; import com.google.common.collect.Lists; import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.EaglerXBungeeVersion; import net.lax1dude.eaglercraft.v1_8.Keyboard; import net.lax1dude.eaglercraft.v1_8.Mouse; import net.lax1dude.eaglercraft.v1_8.cookie.ServerCookieDataStore; @@ -358,7 +357,7 @@ public class GuiMultiplayer extends GuiScreen implements GuiYesNoCallback { GlStateManager.scale(0.75f, 0.75f, 0.75f); GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); - String text = EaglerXBungeeVersion.getPluginButton(); + String text = "Download EaglerXServer"; int w = mc.fontRendererObj.getStringWidth(text); boolean hover = xx > width - 5 - (w + 5) * 3 / 4 && yy > 1 && xx < width - 2 && yy < 12; if (hover) { @@ -428,12 +427,12 @@ public class GuiMultiplayer extends GuiScreen implements GuiYesNoCallback { relaysButton.mouseClicked(parInt1, parInt2, parInt3); super.mouseClicked(parInt1, parInt2, parInt3); this.serverListSelector.mouseClicked(parInt1, parInt2, parInt3); - String text = EaglerXBungeeVersion.getPluginButton(); + String text = "Download EaglerXServer"; int w = mc.fontRendererObj.getStringWidth(text); if (parInt1 > width - 5 - (w + 5) * 3 / 4 && parInt2 > 1 && parInt1 < width - 2 && parInt2 < 12) { this.mc.getSoundHandler() .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); - EaglerXBungeeVersion.startPluginDownload(); + EagRuntime.openLink("https://lax1dude.net/eaglerxserver"); } } diff --git a/src/game/java/net/minecraft/client/gui/GuiScreen.java b/src/game/java/net/minecraft/client/gui/GuiScreen.java index ac89da4b..81ae26ff 100755 --- a/src/game/java/net/minecraft/client/gui/GuiScreen.java +++ b/src/game/java/net/minecraft/client/gui/GuiScreen.java @@ -18,7 +18,6 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.EaglerXBungeeVersion; import net.lax1dude.eaglercraft.v1_8.Keyboard; import net.lax1dude.eaglercraft.v1_8.Mouse; import net.lax1dude.eaglercraft.v1_8.PauseMenuCustomizeState; @@ -424,13 +423,6 @@ public abstract class GuiScreen extends Gui implements GuiYesNoCallback { * GuiTwitchUserMode(this.mc.getTwitchStream(), chatuserinfo)); } else { } */ LOGGER.error("Tried to handle twitch user but couldn\'t find them!"); - } else if (clickevent.getAction() == ClickEvent.Action.EAGLER_PLUGIN_DOWNLOAD) { - if (EaglerXBungeeVersion.pluginFileEPK.equals(clickevent.getValue())) { - EaglerXBungeeVersion.startPluginDownload(); - } else { - LOGGER.error("Invalid plugin download from EPK was blocked: {}", - EaglerXBungeeVersion.pluginFileEPK); - } } else { LOGGER.error("Don\'t know how to handle " + clickevent); } diff --git a/src/game/java/net/minecraft/client/gui/spectator/PlayerMenuObject.java b/src/game/java/net/minecraft/client/gui/spectator/PlayerMenuObject.java index 583d5bdd..87ee03ec 100755 --- a/src/game/java/net/minecraft/client/gui/spectator/PlayerMenuObject.java +++ b/src/game/java/net/minecraft/client/gui/spectator/PlayerMenuObject.java @@ -45,7 +45,7 @@ public class PlayerMenuObject implements ISpectatorMenuObject { public void func_178663_a(float alpha, int parInt1) { Minecraft.getMinecraft().getTextureManager().bindTexture( - Minecraft.getMinecraft().getNetHandler().getSkinCache().getSkin(profile).getResourceLocation()); + Minecraft.getMinecraft().getNetHandler().getTextureCache().getPlayerSkin(profile).getLocation()); GlStateManager.color(1.0F, 1.0F, 1.0F, (float) parInt1 / 255.0F); Gui.drawScaledCustomSizeModalRect(2, 2, 8.0F, 8.0F, 8, 8, 12, 12, 64.0F, 64.0F); Gui.drawScaledCustomSizeModalRect(2, 2, 40.0F, 8.0F, 8, 8, 12, 12, 64.0F, 64.0F); diff --git a/src/game/java/net/minecraft/client/multiplayer/GuiConnecting.java b/src/game/java/net/minecraft/client/multiplayer/GuiConnecting.java index 5de9d33e..2c2b7051 100755 --- a/src/game/java/net/minecraft/client/multiplayer/GuiConnecting.java +++ b/src/game/java/net/minecraft/client/multiplayer/GuiConnecting.java @@ -1,6 +1,5 @@ package net.minecraft.client.multiplayer; -import java.io.IOException; import java.util.List; import net.lax1dude.eaglercraft.v1_8.EagRuntime; @@ -11,22 +10,15 @@ import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame; import net.lax1dude.eaglercraft.v1_8.internal.PlatformNetworking; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; import net.lax1dude.eaglercraft.v1_8.socket.AddressResolver; -import net.lax1dude.eaglercraft.v1_8.socket.ConnectionHandshake; -import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; import net.lax1dude.eaglercraft.v1_8.socket.RateLimitTracker; -import net.lax1dude.eaglercraft.v1_8.socket.WebSocketNetworkManager; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageConstants; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.client.GameProtocolMessageController; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.handshake.HandshakerHandler; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiDisconnected; import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.network.NetHandlerPlayClient; import net.minecraft.client.resources.I18n; -import net.minecraft.network.EnumConnectionState; -import net.minecraft.network.play.client.C17PacketCustomPayload; import net.minecraft.util.ChatComponentText; /**+ @@ -52,13 +44,12 @@ import net.minecraft.util.ChatComponentText; public class GuiConnecting extends GuiScreen { private static final Logger logger = LogManager.getLogger(); private IWebSocketClient webSocket; - private EaglercraftNetworkManager networkManager; + private HandshakerHandler handshaker; private String currentAddress; private String currentPassword; private boolean allowPlaintext; private boolean allowCookies; private boolean cancel; - private boolean hasOpened; private final GuiScreen previousGuiScreen; private int timer = 0; @@ -149,10 +140,13 @@ public class GuiConnecting extends GuiScreen { new ChatComponentText("Could not open WebSocket to \"" + currentAddress + "\"!"))); } } else { - if (webSocket.getState() == EnumEaglerConnectionState.CONNECTED) { - if (!hasOpened) { - hasOpened = true; + EnumEaglerConnectionState connState = webSocket.getState(); + if (connState == EnumEaglerConnectionState.CONNECTED) { + if (handshaker == null) { + this.mc.getSession().reset(); + logger.info("Logging in: {}", currentAddress); + byte[] cookieData = null; if (allowCookies) { ServerCookieDataStore.ServerCookie cookie = ServerCookieDataStore @@ -161,61 +155,15 @@ public class GuiConnecting extends GuiScreen { cookieData = cookie.cookie; } } - if (ConnectionHandshake.attemptHandshake(this.mc, webSocket, this, previousGuiScreen, - currentPassword, allowPlaintext, allowCookies, cookieData)) { - logger.info("Handshake Success"); - webSocket.setEnableStringFrames(false); - webSocket.clearStringFrames(); - this.networkManager = new WebSocketNetworkManager(webSocket); - this.networkManager.setPluginInfo(ConnectionHandshake.pluginBrand, - ConnectionHandshake.pluginVersion); - mc.bungeeOutdatedMsgTimer = 80; - mc.clearTitles(); - this.networkManager.setConnectionState(EnumConnectionState.PLAY); - NetHandlerPlayClient netHandler = new NetHandlerPlayClient(this.mc, previousGuiScreen, - this.networkManager, this.mc.getSession().getProfile()); - this.networkManager.setNetHandler(netHandler); - netHandler.setEaglerMessageController(new GameProtocolMessageController( - GamePluginMessageProtocol.getByVersion(ConnectionHandshake.protocolVersion), - GamePluginMessageConstants.CLIENT_TO_SERVER, - GameProtocolMessageController - .createClientHandler(ConnectionHandshake.protocolVersion, netHandler), - (ch, msg) -> netHandler.addToSendQueue(new C17PacketCustomPayload(ch, msg)))); - } else { - if (mc.currentScreen == this) { - checkRatelimit(); - logger.info("Handshake Failure"); - mc.getSession().reset(); - mc.displayGuiScreen( - new GuiDisconnected(previousGuiScreen, "connect.failed", new ChatComponentText( - "Handshake Failure\n\nAre you sure this is an eagler 1.8 server?"))); - } - webSocket.close(); - return; - } - } - if (this.networkManager != null) { - try { - this.networkManager.processReceivedPackets(); - } catch (IOException ex) { - } + + handshaker = new HandshakerHandler(this, webSocket, EaglerProfile.getName(), currentPassword, + allowPlaintext, allowCookies, cookieData); } + handshaker.tick(); } else { - if (webSocket.getState() == EnumEaglerConnectionState.FAILED) { - if (!hasOpened) { - mc.getSession().reset(); - checkRatelimit(); - if (mc.currentScreen == this) { - if (RateLimitTracker.isProbablyLockedOut(currentAddress)) { - mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(previousGuiScreen)); - } else { - mc.displayGuiScreen(new GuiDisconnected(previousGuiScreen, "connect.failed", - new ChatComponentText("Connection Refused"))); - } - } - } - } else { - if (this.networkManager != null && this.networkManager.checkDisconnected()) { + if (handshaker != null) { + handshaker.tick(); + if (connState == EnumEaglerConnectionState.FAILED) { this.mc.getSession().reset(); checkRatelimit(); if (mc.currentScreen == this) { @@ -267,12 +215,7 @@ public class GuiConnecting extends GuiScreen { protected void actionPerformed(GuiButton parGuiButton) { if (parGuiButton.id == 0) { this.cancel = true; - if (this.networkManager != null) { - this.networkManager.closeChannel(new ChatComponentText("Aborted")); - } else if (this.webSocket != null) { - this.webSocket.close(); - } - + this.webSocket.close(); this.mc.displayGuiScreen(this.previousGuiScreen); } @@ -284,7 +227,7 @@ public class GuiConnecting extends GuiScreen { */ public void drawScreen(int i, int j, float f) { this.drawDefaultBackground(); - if (this.networkManager == null || !this.networkManager.isChannelOpen()) { + if (this.handshaker == null) { this.drawCenteredString(this.fontRendererObj, I18n.format("connect.connecting", new Object[0]), this.width / 2, this.height / 2 - 50, 16777215); } else { @@ -319,4 +262,13 @@ public class GuiConnecting extends GuiScreen { public boolean canCloseGui() { return false; } + + public static Minecraft getMC(GuiConnecting gui) { + return gui.mc; + } + + public static GuiScreen getPrevScreen(GuiConnecting gui) { + return gui.previousGuiScreen; + } + } \ No newline at end of file diff --git a/src/game/java/net/minecraft/client/multiplayer/PlayerControllerMP.java b/src/game/java/net/minecraft/client/multiplayer/PlayerControllerMP.java index 223eae8d..e5bd78b2 100755 --- a/src/game/java/net/minecraft/client/multiplayer/PlayerControllerMP.java +++ b/src/game/java/net/minecraft/client/multiplayer/PlayerControllerMP.java @@ -313,8 +313,7 @@ public class PlayerControllerMP { this.netClientHandler.getNetworkManager() .closeChannel(new ChatComponentText("Exception thrown: " + ex.toString())); } - this.netClientHandler.getSkinCache().flush(); - this.netClientHandler.getCapeCache().flush(); + this.netClientHandler.getTextureCache().runTick(); this.netClientHandler.getNotifManager().runTick(); ClientUUIDLoadingCache.update(); } else { diff --git a/src/game/java/net/minecraft/client/network/NetHandlerPlayClient.java b/src/game/java/net/minecraft/client/network/NetHandlerPlayClient.java index a96a6ae0..b1538461 100755 --- a/src/game/java/net/minecraft/client/network/NetHandlerPlayClient.java +++ b/src/game/java/net/minecraft/client/network/NetHandlerPlayClient.java @@ -14,11 +14,15 @@ import com.google.common.collect.Maps; import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; import net.lax1dude.eaglercraft.v1_8.notifications.ServerNotificationManager; -import net.lax1dude.eaglercraft.v1_8.profile.ServerCapeCache; -import net.lax1dude.eaglercraft.v1_8.profile.ServerSkinCache; +import net.lax1dude.eaglercraft.v1_8.skin_cache.ServerTextureCache; import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageConstants; import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.client.GameProtocolMessageController; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.client.ClientMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.handshake.StandardCaps; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.message.InjectedMessageController; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.message.LegacyMessageController; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.message.MessageController; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; import net.lax1dude.eaglercraft.v1_8.sp.lan.LANClientNetworkManager; import net.lax1dude.eaglercraft.v1_8.sp.socket.ClientIntegratedServerNetworkManager; @@ -257,26 +261,37 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { * particlespawn offset and velocity */ private final EaglercraftRandom avRandomizer = new EaglercraftRandom(); - private final ServerSkinCache skinCache; - private final ServerCapeCache capeCache; + private final ServerTextureCache textureCache; private final ServerNotificationManager notifManager; public boolean currentFNAWSkinAllowedState = true; public boolean currentFNAWSkinForcedState = true; - private GameProtocolMessageController eaglerMessageController = null; - public boolean hasRequestedServerInfo = false; + private final MessageController eaglerMessageController; + public byte[] cachedServerInfoHash = null; public byte[] cachedServerInfoData = null; + public boolean allowedDisplayWebview = false; + public boolean allowedDisplayWebviewYes = false; public NetHandlerPlayClient(Minecraft mcIn, GuiScreen parGuiScreen, EaglercraftNetworkManager parNetworkManager, - GameProfile parGameProfile) { + GameProfile parGameProfile, GamePluginMessageProtocol eaglerProtocol) { this.gameController = mcIn; this.guiScreenServer = parGuiScreen; this.netManager = parNetworkManager; + this.netManager.setNetHandler(this); this.profile = parGameProfile; - this.skinCache = new ServerSkinCache(this, mcIn.getTextureManager()); - this.capeCache = new ServerCapeCache(this, mcIn.getTextureManager()); - this.notifManager = new ServerNotificationManager(); this.isIntegratedServer = (parNetworkManager instanceof ClientIntegratedServerNetworkManager) || (parNetworkManager instanceof LANClientNetworkManager); + ClientMessageHandler handler = ClientMessageHandler.createClientHandler(eaglerProtocol.ver, this); + if (eaglerProtocol.ver >= 5) { + this.eaglerMessageController = new InjectedMessageController(eaglerProtocol, handler, + GamePluginMessageConstants.CLIENT_TO_SERVER, parNetworkManager::injectRawFrame); + parNetworkManager.setInjectedMessageController((InjectedMessageController) eaglerMessageController); + } else { + this.eaglerMessageController = new LegacyMessageController(eaglerProtocol, handler, + GamePluginMessageConstants.CLIENT_TO_SERVER, + (ch, msg) -> addToSendQueue(new C17PacketCustomPayload(ch, msg))); + } + this.textureCache = ServerTextureCache.create(this, mcIn.getTextureManager()); + this.notifManager = new ServerNotificationManager(mcIn.getTextureManager()); } /**+ @@ -285,42 +300,28 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { */ public void cleanup() { this.clientWorldController = null; - this.skinCache.destroy(); - this.capeCache.destroy(); + this.textureCache.destroy(); this.notifManager.destroy(); } - public ServerSkinCache getSkinCache() { - return this.skinCache; - } - - public ServerCapeCache getCapeCache() { - return this.capeCache; + public ServerTextureCache getTextureCache() { + return this.textureCache; } public ServerNotificationManager getNotifManager() { return this.notifManager; } - public GameProtocolMessageController getEaglerMessageController() { + public MessageController getEaglerMessageController() { return eaglerMessageController; } - public void setEaglerMessageController(GameProtocolMessageController eaglerMessageController) { - this.eaglerMessageController = eaglerMessageController; - } - public GamePluginMessageProtocol getEaglerMessageProtocol() { - return eaglerMessageController != null ? eaglerMessageController.protocol : null; + return eaglerMessageController != null ? eaglerMessageController.getProtocol() : null; } public void sendEaglerMessage(GameMessagePacket packet) { - try { - eaglerMessageController.sendPacket(packet); - } catch (IOException e) { - logger.error("Failed to send eaglercraft plugin message packet: " + packet); - logger.error(e); - } + eaglerMessageController.sendPacket(packet); } public boolean webViewSendHandler(GameMessagePacket pkt) { @@ -331,7 +332,7 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { logger.error("WebView sent message on a dead handler!"); return false; } - if (eaglerMessageController.protocol.ver >= 4) { + if (eaglerMessageController.getProtocol().ver >= 4) { sendEaglerMessage(pkt); return true; } else { @@ -361,9 +362,18 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { this.netManager.sendPacket(new C17PacketCustomPayload("MC|Brand", (new PacketBuffer(Unpooled.buffer())).writeString(ClientBrandRetriever.getClientModName()))); if (VoiceClientController.isClientSupported()) { - VoiceClientController.initializeVoiceClient(this::sendEaglerMessage, eaglerMessageController.protocol.ver); + if (netManager.getServerCapabilities().hasCapability(StandardCaps.VOICE, 0)) { + VoiceClientController.initializeVoiceClient(this::sendEaglerMessage, + eaglerMessageController.getProtocol().ver); + } else { + VoiceClientController.initializeVoiceClient(null, -1); + } + } + if (netManager.getServerCapabilities().hasCapability(StandardCaps.WEBVIEW, 0)) { + WebViewOverlayController.setPacketSendCallback(this::webViewSendHandler); + } else { + WebViewOverlayController.setPacketSendCallback(null); } - WebViewOverlayController.setPacketSendCallback(this::webViewSendHandler); } /**+ @@ -1466,8 +1476,7 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { if (packetIn.func_179768_b() == S38PacketPlayerListItem.Action.REMOVE_PLAYER) { EaglercraftUUID uuid = s38packetplayerlistitem$addplayerdata.getProfile().getId(); this.playerInfoMap.remove(uuid); - this.skinCache.evictSkin(uuid); - this.capeCache.evictCape(uuid); + this.textureCache.evictPlayer(uuid); ClientUUIDLoadingCache.evict(uuid); } else { NetworkPlayerInfo networkplayerinfo = (NetworkPlayerInfo) this.playerInfoMap @@ -1636,9 +1645,10 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { this.gameController .displayGuiScreen(new GuiScreenBook(this.gameController.thePlayer, itemstack, false)); } - } else { + } else if (eaglerMessageController instanceof LegacyMessageController) { try { - eaglerMessageController.handlePacket(packetIn.getChannelName(), packetIn.getBufferData()); + ((LegacyMessageController) eaglerMessageController).handlePacket(packetIn.getChannelName(), + packetIn.getBufferData()); } catch (IOException e) { logger.error("Couldn't read \"{}\" packet as an eaglercraft plugin message!", packetIn.getChannelName()); diff --git a/src/game/java/net/minecraft/client/network/NetworkPlayerInfo.java b/src/game/java/net/minecraft/client/network/NetworkPlayerInfo.java index 3fc927a7..3e59fad0 100755 --- a/src/game/java/net/minecraft/client/network/NetworkPlayerInfo.java +++ b/src/game/java/net/minecraft/client/network/NetworkPlayerInfo.java @@ -85,21 +85,19 @@ public class NetworkPlayerInfo { } public String getSkinType() { - return Minecraft.getMinecraft().getNetHandler().getSkinCache().getSkin(this.gameProfile) - .getSkinModel().profileSkinType; + return getEaglerSkinModel().profileSkinType; } public SkinModel getEaglerSkinModel() { - return Minecraft.getMinecraft().getNetHandler().getSkinCache().getSkin(this.gameProfile).getSkinModel(); + return Minecraft.getMinecraft().getNetHandler().getTextureCache().getPlayerSkin(this.gameProfile).getModel(); } public ResourceLocation getLocationSkin() { - return Minecraft.getMinecraft().getNetHandler().getSkinCache().getSkin(this.gameProfile).getResourceLocation(); + return Minecraft.getMinecraft().getNetHandler().getTextureCache().getPlayerSkin(this.gameProfile).getLocation(); } public ResourceLocation getLocationCape() { - return Minecraft.getMinecraft().getNetHandler().getCapeCache().getCape(this.gameProfile.getId()) - .getResourceLocation(); + return Minecraft.getMinecraft().getNetHandler().getTextureCache().getPlayerCape(this.gameProfile); } public ScorePlayerTeam getPlayerTeam() { diff --git a/src/game/java/net/minecraft/client/renderer/BlockFluidRenderer.java b/src/game/java/net/minecraft/client/renderer/BlockFluidRenderer.java index d3bd7963..b407f171 100755 --- a/src/game/java/net/minecraft/client/renderer/BlockFluidRenderer.java +++ b/src/game/java/net/minecraft/client/renderer/BlockFluidRenderer.java @@ -4,7 +4,6 @@ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.BlockVertexIDs; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.DeferredStateManager; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.dynamiclights.DynamicLightsStateManager; import net.minecraft.block.Block; import net.minecraft.block.BlockLiquid; import net.minecraft.block.material.Material; @@ -56,7 +55,7 @@ public class BlockFluidRenderer { WorldRenderer worldRendererIn) { BlockPos tmp = new BlockPos(0, 0, 0); boolean deferred = DeferredStateManager.isDeferredRenderer(); - boolean isDynamicLights = deferred || DynamicLightsStateManager.isDynamicLightsRender(); + boolean isDynamicLights = deferred;// || DynamicLightsStateManager.isDynamicLightsRender(); BlockLiquid blockliquid = (BlockLiquid) blockStateIn.getBlock(); boolean lava = blockliquid.getMaterial() == Material.lava; boolean realistic = !lava && DeferredStateManager.isRenderingRealisticWater(); diff --git a/src/game/java/net/minecraft/client/renderer/BlockModelRenderer.java b/src/game/java/net/minecraft/client/renderer/BlockModelRenderer.java index 742be566..941b05df 100755 --- a/src/game/java/net/minecraft/client/renderer/BlockModelRenderer.java +++ b/src/game/java/net/minecraft/client/renderer/BlockModelRenderer.java @@ -7,7 +7,6 @@ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.DeferredStateManager; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.VertexMarkerState; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.dynamiclights.DynamicLightsStateManager; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; @@ -229,7 +228,7 @@ public class BlockModelRenderer { private void renderModelAmbientOcclusionQuads(IBlockAccess blockAccessIn, IBlockState blockStateIn, BlockPos blockPosIn, WorldRenderer worldRendererIn, List listQuadsIn, RenderEnv renderenv) { boolean isDeferred = DeferredStateManager.isDeferredRenderer(); - boolean isDynamicLights = isDeferred || DynamicLightsStateManager.isDynamicLightsRender(); + boolean isDynamicLights = isDeferred;// || DynamicLightsStateManager.isDynamicLightsRender(); float[] quadBounds = renderenv.getQuadBounds(); BitSet boundsFlags = renderenv.getBoundsFlags(); BlockModelRenderer.AmbientOcclusionFace aoFaceIn = renderenv.getAoFace(); @@ -368,7 +367,7 @@ public class BlockModelRenderer { EnumFacing faceIn, int brightnessIn, boolean ownBrightness, WorldRenderer worldRendererIn, List listQuadsIn, RenderEnv renderenv) { boolean isDeferred = DeferredStateManager.isDeferredRenderer(); - boolean isDynamicLights = isDeferred || DynamicLightsStateManager.isDynamicLightsRender(); + boolean isDynamicLights = isDeferred;// || DynamicLightsStateManager.isDynamicLightsRender(); BitSet boundsFlags = renderenv.getBoundsFlags(); float[] quadBounds = renderenv.getQuadBounds(); double d0 = (double) blockPosIn.getX(); diff --git a/src/game/java/net/minecraft/client/renderer/RenderGlobal.java b/src/game/java/net/minecraft/client/renderer/RenderGlobal.java index 47e178b1..df699f4b 100755 --- a/src/game/java/net/minecraft/client/renderer/RenderGlobal.java +++ b/src/game/java/net/minecraft/client/renderer/RenderGlobal.java @@ -1665,9 +1665,10 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene this.renderEngine.bindTexture(TextureMap.locationBlocksTexture); this.preRenderDamagedBlocks(); worldRendererIn.begin(7, - (DeferredStateManager.isDeferredRenderer() || DynamicLightsStateManager.isDynamicLightsRender()) - ? VertexFormat.BLOCK_SHADERS - : DefaultVertexFormats.BLOCK); + (DeferredStateManager + .isDeferredRenderer() /* || DynamicLightsStateManager.isDynamicLightsRender() */) + ? VertexFormat.BLOCK_SHADERS + : DefaultVertexFormats.BLOCK); worldRendererIn.setTranslation(-d0, -d1, -d2); worldRendererIn.markDirty(); Iterator iterator = this.damagedBlocks.values().iterator(); diff --git a/src/game/java/net/minecraft/client/renderer/chunk/RenderChunk.java b/src/game/java/net/minecraft/client/renderer/chunk/RenderChunk.java index 05fb6b6e..2e96f9bb 100755 --- a/src/game/java/net/minecraft/client/renderer/chunk/RenderChunk.java +++ b/src/game/java/net/minecraft/client/renderer/chunk/RenderChunk.java @@ -10,7 +10,6 @@ import com.google.common.collect.Sets; import net.lax1dude.eaglercraft.v1_8.opengl.VertexFormat; import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.DeferredStateManager; -import net.lax1dude.eaglercraft.v1_8.opengl.ext.dynamiclights.DynamicLightsStateManager; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; @@ -245,7 +244,7 @@ public class RenderChunk { private void preRenderBlocks(WorldRenderer worldRendererIn, BlockPos pos) { worldRendererIn.begin(7, - (DeferredStateManager.isDeferredRenderer() || DynamicLightsStateManager.isDynamicLightsRender()) + (DeferredStateManager.isDeferredRenderer() /* || DynamicLightsStateManager.isDynamicLightsRender() */) ? VertexFormat.BLOCK_SHADERS : DefaultVertexFormats.BLOCK); worldRendererIn.setTranslation((double) (-pos.getX()), (double) (-pos.getY()), (double) (-pos.getZ())); diff --git a/src/game/java/net/minecraft/client/renderer/entity/RenderFallingBlock.java b/src/game/java/net/minecraft/client/renderer/entity/RenderFallingBlock.java index 5121efdf..d4d2da97 100755 --- a/src/game/java/net/minecraft/client/renderer/entity/RenderFallingBlock.java +++ b/src/game/java/net/minecraft/client/renderer/entity/RenderFallingBlock.java @@ -67,10 +67,9 @@ public class RenderFallingBlock extends Render { GlStateManager.disableLighting(); Tessellator tessellator = Tessellator.getInstance(); WorldRenderer worldrenderer = tessellator.getWorldRenderer(); - worldrenderer.begin(7, - (DeferredStateManager.isDeferredRenderer() - || DynamicLightsStateManager.isDynamicLightsRender()) ? VertexFormat.BLOCK_SHADERS - : DefaultVertexFormats.BLOCK); + worldrenderer.begin(7, (DeferredStateManager.isDeferredRenderer() + /* || DynamicLightsStateManager.isDynamicLightsRender() */) ? VertexFormat.BLOCK_SHADERS + : DefaultVertexFormats.BLOCK); int i = blockpos.getX(); int j = blockpos.getY(); int k = blockpos.getZ(); diff --git a/src/game/java/net/minecraft/client/renderer/tileentity/TileEntitySkullRenderer.java b/src/game/java/net/minecraft/client/renderer/tileentity/TileEntitySkullRenderer.java index cf1880d4..1c744f73 100755 --- a/src/game/java/net/minecraft/client/renderer/tileentity/TileEntitySkullRenderer.java +++ b/src/game/java/net/minecraft/client/renderer/tileentity/TileEntitySkullRenderer.java @@ -88,7 +88,7 @@ public class TileEntitySkullRenderer extends TileEntitySpecialRenderer cur : this.activePotionsMap) { - int integer = cur.key; - PotionEffect potioneffect = cur.value; - if (!potioneffect.onUpdate(this)) { - if (!this.worldObj.isRemote) { - if (deadPotionEffects == null) - deadPotionEffects = new IntArrayList(4); - deadPotionEffects.add(integer); + if (!this.worldObj.isRemote) { + this.activePotionsMap.removeAll((integer, potioneffect) -> { + if (!potioneffect.onUpdate(this)) { this.onFinishedPotionEffect(potioneffect); + return true; + } else if (potioneffect.getDuration() % 600 == 0) { + this.onChangedPotionEffect(potioneffect, false); } - } else if (potioneffect.getDuration() % 600 == 0) { - this.onChangedPotionEffect(potioneffect, false); - } - } - - if (deadPotionEffects != null) { - this.activePotionsMap.removeAll(deadPotionEffects); + return false; + }); + } else { + this.activePotionsMap.forEach((IntObjectProcedure) (integer, potioneffect) -> { + if (potioneffect.onUpdate(this) && potioneffect.getDuration() % 600 == 0) { + this.onChangedPotionEffect(potioneffect, false); + } + }); } if (this.potionsNeedUpdate) { diff --git a/src/game/java/net/minecraft/entity/player/EntityPlayerMP.java b/src/game/java/net/minecraft/entity/player/EntityPlayerMP.java index c4a5ac16..72c57f88 100755 --- a/src/game/java/net/minecraft/entity/player/EntityPlayerMP.java +++ b/src/game/java/net/minecraft/entity/player/EntityPlayerMP.java @@ -10,6 +10,8 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.lax1dude.eaglercraft.v1_8.sp.server.skins.PlayerTextureData; + import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -176,6 +178,7 @@ public class EntityPlayerMP extends EntityPlayer implements ICrafting { public int ping; public boolean playerConqueredTheEnd; public byte[] updateCertificate = null; + public PlayerTextureData textureData = null; public EaglercraftUUID clientBrandUUID = null; public EntityPlayerMP(MinecraftServer server, WorldServer worldIn, GameProfile profile, diff --git a/src/game/java/net/minecraft/event/ClickEvent.java b/src/game/java/net/minecraft/event/ClickEvent.java index 5a33f67c..95c88929 100755 --- a/src/game/java/net/minecraft/event/ClickEvent.java +++ b/src/game/java/net/minecraft/event/ClickEvent.java @@ -85,7 +85,7 @@ public class ClickEvent { public static enum Action { OPEN_URL("open_url", true), OPEN_FILE("open_file", false), RUN_COMMAND("run_command", true), TWITCH_USER_INFO("twitch_user_info", false), SUGGEST_COMMAND("suggest_command", true), - CHANGE_PAGE("change_page", true), EAGLER_PLUGIN_DOWNLOAD("eagler_plugin_download", true); + CHANGE_PAGE("change_page", true); private static final Map nameMapping = Maps.newHashMap(); private final boolean allowedInChat; diff --git a/src/game/java/net/minecraft/network/NetHandlerPlayServer.java b/src/game/java/net/minecraft/network/NetHandlerPlayServer.java index b1005872..f87bf9b8 100755 --- a/src/game/java/net/minecraft/network/NetHandlerPlayServer.java +++ b/src/game/java/net/minecraft/network/NetHandlerPlayServer.java @@ -5,8 +5,12 @@ import com.carrotsearch.hppc.IntShortMap; import com.google.common.collect.Lists; import com.google.common.primitives.Doubles; import com.google.common.primitives.Floats; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageConstants; import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.client.GameProtocolMessageController; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.message.InjectedMessageController; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.message.LegacyMessageController; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.message.MessageController; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketUpdateCertEAG; @@ -75,6 +79,7 @@ import net.minecraft.network.play.server.S23PacketBlockChange; import net.minecraft.network.play.server.S2FPacketSetSlot; import net.minecraft.network.play.server.S32PacketConfirmTransaction; import net.minecraft.network.play.server.S3APacketTabComplete; +import net.minecraft.network.play.server.S3FPacketCustomPayload; import net.minecraft.network.play.server.S40PacketDisconnect; import net.minecraft.server.MinecraftServer; import net.minecraft.stats.AchievementList; @@ -93,6 +98,7 @@ import net.minecraft.util.ITickable; import net.minecraft.util.ReportedException; import net.minecraft.world.WorldServer; import net.lax1dude.eaglercraft.v1_8.sp.server.socket.IntegratedServerPlayerNetworkManager; +import net.lax1dude.eaglercraft.v1_8.sp.server.socket.protocol.ServerMessageHandler; import org.apache.commons.lang3.StringUtils; @@ -141,36 +147,41 @@ public class NetHandlerPlayServer implements INetHandlerPlayServer, ITickable { private double lastPosZ; private boolean hasMoved = true; private boolean hasDisconnected = false; - private GameProtocolMessageController eaglerMessageController = null; + private MessageController eaglerMessageController = null; public NetHandlerPlayServer(MinecraftServer server, IntegratedServerPlayerNetworkManager networkManagerIn, - EntityPlayerMP playerIn) { + EntityPlayerMP playerIn, GamePluginMessageProtocol eaglerProtocol) { this.serverController = server; this.netManager = networkManagerIn; networkManagerIn.setNetHandler(this); this.playerEntity = playerIn; playerIn.playerNetServerHandler = this; + ServerMessageHandler handler = ServerMessageHandler.createServerHandler(eaglerProtocol.ver, this); + if (eaglerProtocol.ver >= 5) { + this.eaglerMessageController = new InjectedMessageController(eaglerProtocol, handler, + GamePluginMessageConstants.SERVER_TO_CLIENT, networkManagerIn::injectRawFrame); + networkManagerIn.setInjectedMessageController((InjectedMessageController) eaglerMessageController); + } else { + this.eaglerMessageController = new LegacyMessageController(eaglerProtocol, handler, + GamePluginMessageConstants.SERVER_TO_CLIENT, + (ch, msg) -> sendPacket(new S3FPacketCustomPayload(ch, msg))); + } } - public GameProtocolMessageController getEaglerMessageController() { + public MessageController getEaglerMessageController() { return eaglerMessageController; } - public void setEaglerMessageController(GameProtocolMessageController eaglerMessageController) { + public void setEaglerMessageController(MessageController eaglerMessageController) { this.eaglerMessageController = eaglerMessageController; } public GamePluginMessageProtocol getEaglerMessageProtocol() { - return eaglerMessageController != null ? eaglerMessageController.protocol : null; + return eaglerMessageController != null ? eaglerMessageController.getProtocol() : null; } public void sendEaglerMessage(GameMessagePacket packet) { - try { - eaglerMessageController.sendPacket(packet); - } catch (IOException e) { - logger.error("Failed to send eaglercraft plugin message packet: " + packet); - logger.error(e); - } + eaglerMessageController.sendPacket(packet); } /**+ @@ -1277,10 +1288,10 @@ public class NetHandlerPlayServer implements INetHandlerPlayServer, ITickable { } } } - } else { + } else if (eaglerMessageController instanceof LegacyMessageController) { try { - eaglerMessageController.handlePacket(c17packetcustompayload.getChannelName(), - c17packetcustompayload.getBufferData()); + ((LegacyMessageController) eaglerMessageController) + .handlePacket(c17packetcustompayload.getChannelName(), c17packetcustompayload.getBufferData()); } catch (IOException e) { logger.error("Couldn't read \"{}\" packet as an eaglercraft plugin message!", c17packetcustompayload.getChannelName()); diff --git a/src/game/java/net/minecraft/network/PacketBuffer.java b/src/game/java/net/minecraft/network/PacketBuffer.java index a7d35846..62b294b8 100755 --- a/src/game/java/net/minecraft/network/PacketBuffer.java +++ b/src/game/java/net/minecraft/network/PacketBuffer.java @@ -848,4 +848,12 @@ public class PacketBuffer extends ByteBuf { return this.buf.toString(); } + public byte[] toBytes() { + int readerIndex = buf.readerIndex(); + int writerIndex = buf.writerIndex(); + byte[] bytes = new byte[writerIndex - readerIndex]; + buf.getBytes(readerIndex, bytes); + return bytes; + } + } \ No newline at end of file diff --git a/src/game/java/net/minecraft/server/management/ServerConfigurationManager.java b/src/game/java/net/minecraft/server/management/ServerConfigurationManager.java index e929b97a..5782e77b 100755 --- a/src/game/java/net/minecraft/server/management/ServerConfigurationManager.java +++ b/src/game/java/net/minecraft/server/management/ServerConfigurationManager.java @@ -61,12 +61,11 @@ import net.minecraft.world.border.WorldBorder; import net.minecraft.world.demo.DemoWorldManager; import net.minecraft.world.storage.IPlayerFileData; import net.minecraft.world.storage.WorldInfo; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageConstants; import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.client.GameProtocolMessageController; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketUpdateCertEAG; import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerMinecraftServer; import net.lax1dude.eaglercraft.v1_8.sp.server.WorldsDB; +import net.lax1dude.eaglercraft.v1_8.sp.server.skins.PlayerTextureData; import net.lax1dude.eaglercraft.v1_8.sp.server.socket.IntegratedServerPlayerNetworkManager; import net.lax1dude.eaglercraft.v1_8.sp.server.voice.IntegratedVoiceService; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; @@ -120,9 +119,9 @@ public abstract class ServerConfigurationManager { } public void initializeConnectionToPlayer(IntegratedServerPlayerNetworkManager netManager, EntityPlayerMP playerIn, - int protocolVersion, EaglercraftUUID clientBrandUUID) { + GamePluginMessageProtocol protocolVersion, PlayerTextureData textureData, EaglercraftUUID clientBrandUUID) { + playerIn.textureData = textureData; playerIn.clientBrandUUID = clientBrandUUID; - GameProfile gameprofile1 = playerIn.getGameProfile(); NBTTagCompound nbttagcompound = this.readPlayerDataFromFile(playerIn); playerIn.setWorld(this.mcServer.worldServerForDimension(playerIn.dimension)); playerIn.theItemInWorldManager.setWorld((WorldServer) playerIn.worldObj); @@ -134,11 +133,8 @@ public abstract class ServerConfigurationManager { WorldInfo worldinfo = worldserver.getWorldInfo(); BlockPos blockpos = worldserver.getSpawnPoint(); this.setPlayerGameTypeBasedOnOther(playerIn, (EntityPlayerMP) null, worldserver); - NetHandlerPlayServer nethandlerplayserver = new NetHandlerPlayServer(this.mcServer, netManager, playerIn); - nethandlerplayserver.setEaglerMessageController(new GameProtocolMessageController( - GamePluginMessageProtocol.getByVersion(protocolVersion), GamePluginMessageConstants.SERVER_TO_CLIENT, - GameProtocolMessageController.createServerHandler(protocolVersion, nethandlerplayserver), - (ch, msg) -> nethandlerplayserver.sendPacket(new S3FPacketCustomPayload(ch, msg)))); + NetHandlerPlayServer nethandlerplayserver = new NetHandlerPlayServer(this.mcServer, netManager, playerIn, + protocolVersion); nethandlerplayserver.sendPacket(new S01PacketJoinGame(playerIn.getEntityId(), playerIn.theItemInWorldManager.getGameType(), worldinfo.isHardcoreModeEnabled(), worldserver.provider.getDimensionId(), worldserver.getDifficulty(), this.getMaxPlayers(), @@ -357,8 +353,6 @@ public abstract class ServerConfigurationManager { this.playerStatFiles.remove(entityplayermp.getName()); } - ((EaglerMinecraftServer) mcServer).getSkinService().unregisterPlayer(uuid); - ((EaglerMinecraftServer) mcServer).getCapeService().unregisterPlayer(uuid); IntegratedVoiceService vcs = ((EaglerMinecraftServer) mcServer).getVoiceService(); if (vcs != null) { vcs.handlePlayerLoggedOut(playerIn); diff --git a/src/game/java/net/minecraft/server/network/NetHandlerLoginServer.java b/src/game/java/net/minecraft/server/network/NetHandlerLoginServer.java index 46fd2c1e..2b3e00bf 100755 --- a/src/game/java/net/minecraft/server/network/NetHandlerLoginServer.java +++ b/src/game/java/net/minecraft/server/network/NetHandlerLoginServer.java @@ -2,11 +2,13 @@ package net.minecraft.server.network; import com.google.common.base.Charsets; import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; import net.lax1dude.eaglercraft.v1_8.ClientUUIDLoadingCache; import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerMinecraftServer; +import net.lax1dude.eaglercraft.v1_8.sp.server.skins.IntegratedTexturePackets; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.network.EnumConnectionState; import net.minecraft.network.login.INetHandlerLoginServer; @@ -80,17 +82,12 @@ public class NetHandlerLoginServer implements INetHandlerLoginServer, ITickable if (entityplayermp == null) { this.currentLoginState = NetHandlerLoginServer.LoginState.READY_TO_ACCEPT; this.server.getConfigurationManager().initializeConnectionToPlayer(this.networkManager, - this.field_181025_l, this.selectedProtocol, this.clientBrandUUID); - ((EaglerMinecraftServer) field_181025_l.mcServer).getSkinService() - .processLoginPacket(this.loginSkinPacket, field_181025_l, 3); // singleplayer always sends V3 - // skin in handshake - if (this.loginCapePacket != null) { - ((EaglerMinecraftServer) field_181025_l.mcServer).getCapeService() - .processLoginPacket(this.loginCapePacket, field_181025_l); - } + this.field_181025_l, GamePluginMessageProtocol.getByVersion(this.selectedProtocol), + IntegratedTexturePackets.handleTextureData(this.loginSkinPacket, this.loginCapePacket), + this.clientBrandUUID); IntegratedVoiceService svc = ((EaglerMinecraftServer) field_181025_l.mcServer).getVoiceService(); if (svc != null) { - svc.handlePlayerLoggedIn(entityplayermp); + svc.handlePlayerLoggedIn(this.field_181025_l); } this.field_181025_l = null; } @@ -130,14 +127,9 @@ public class NetHandlerLoginServer implements INetHandlerLoginServer, ITickable } else { entityplayermp = this.server.getConfigurationManager().createPlayerForUser(this.loginGameProfile); this.server.getConfigurationManager().initializeConnectionToPlayer(this.networkManager, entityplayermp, - this.selectedProtocol, this.clientBrandUUID); - ((EaglerMinecraftServer) entityplayermp.mcServer).getSkinService() - .processLoginPacket(this.loginSkinPacket, entityplayermp, 3); // singleplayer always sends V3 - // skin in handshake - if (this.loginCapePacket != null) { - ((EaglerMinecraftServer) entityplayermp.mcServer).getCapeService() - .processLoginPacket(this.loginCapePacket, entityplayermp); - } + GamePluginMessageProtocol.getByVersion(this.selectedProtocol), + IntegratedTexturePackets.handleTextureData(this.loginSkinPacket, this.loginCapePacket), + this.clientBrandUUID); IntegratedVoiceService svc = ((EaglerMinecraftServer) entityplayermp.mcServer).getVoiceService(); if (svc != null) { svc.handlePlayerLoggedIn(entityplayermp); @@ -171,7 +163,7 @@ public class NetHandlerLoginServer implements INetHandlerLoginServer, ITickable int protocolCount = dis.readUnsignedShort(); for (int i = 0; i < protocolCount; ++i) { int p = dis.readUnsignedShort(); - if ((p == 3 || p == 4) && p > maxSupported) { + if ((p == 3 || p == 4 || p == 5) && p > maxSupported) { maxSupported = p; } } diff --git a/src/game/java/net/minecraft/util/StringTranslate.java b/src/game/java/net/minecraft/util/StringTranslate.java index f7865e92..a562a601 100755 --- a/src/game/java/net/minecraft/util/StringTranslate.java +++ b/src/game/java/net/minecraft/util/StringTranslate.java @@ -66,7 +66,6 @@ public class StringTranslate { initServer(IOUtils.readLines(inputstream, StandardCharsets.UTF_8)); fallbackInstance = new StringTranslate(); fallbackInstance.replaceWith(instance.languageList); - SingleplayerServerController.updateLocale(dump()); } catch (IOException e) { EagRuntime.debugPrintStackTrace(e); } @@ -107,6 +106,7 @@ public class StringTranslate { instance.languageList.clear(); instance.languageList.putAll(parMap); instance.lastUpdateTimeInMilliseconds = EagRuntime.steadyTimeMillis(); + SingleplayerServerController.updateLocale(dump()); } /**+ diff --git a/src/game/java/net/minecraft/world/SpawnerAnimals.java b/src/game/java/net/minecraft/world/SpawnerAnimals.java index 1625ec7e..8ce1c697 100755 --- a/src/game/java/net/minecraft/world/SpawnerAnimals.java +++ b/src/game/java/net/minecraft/world/SpawnerAnimals.java @@ -75,8 +75,7 @@ public final class SpawnerAnimals { int cx = l + j; int cz = i1 + k; long chunkcoordintpair = ChunkCoordIntPair.chunkXZ2Int(cx, cz); - if (!this.eligibleChunksForSpawning.contains(chunkcoordintpair) - && spawnHostileMobs.theChunkProviderServer.chunkExists(cx, cz)) { + if (!this.eligibleChunksForSpawning.contains(chunkcoordintpair)) { ++i; if (!flag && spawnHostileMobs.getWorldBorder().contains(chunkcoordintpair)) { this.eligibleChunksForSpawning.add(chunkcoordintpair); diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java index 5490046e..fef482c0 100755 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java @@ -40,6 +40,7 @@ class OpenGLObjects { static class VertexArrayGL implements IVertexArrayGL { final int ptr; + int enabled; VertexArrayGL(int ptr) { this.ptr = ptr; @@ -53,7 +54,22 @@ class OpenGLObjects { public void free() { PlatformOpenGL._wglDeleteVertexArrays(this); } - + + @Override + public int getBits() { + return enabled; + } + + @Override + public void setBit(int bit) { + enabled |= bit; + } + + @Override + public void unsetBit(int bit) { + enabled &= ~bit; + } + } static class TextureGL implements ITextureGL { diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java index ee570426..4729f5d2 100755 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java @@ -580,7 +580,7 @@ public class PlatformInput { } public static boolean contextLost() { - return glfwGetWindowAttrib(win, GLFW_ICONIFIED) == GLFW_TRUE; + return false; } public static void setFunctionKeyModifier(int key) { diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java index 39904074..dfea3f82 100755 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java @@ -101,7 +101,11 @@ public class PlatformRuntime { IEaglerFilesystem resourcePackFilesystem = Filesystem.getHandleFor(getClientConfigAdapter().getResourcePacksDB()); VFile2.setPrimaryFilesystem(resourcePackFilesystem); EaglerFolderResourcePack.setSupported(true); - + + if (glfwPlatformSupported(GLFW_PLATFORM_X11)) { + glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11); + } + if(requestedANGLEPlatform != EnumPlatformANGLE.DEFAULT) { logger.info("Setting ANGLE Platform: {}", requestedANGLEPlatform.name); glfwInitHint(GLFW_ANGLE_PLATFORM_TYPE, requestedANGLEPlatform.eglEnum); diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java index 6823d881..2b35ca8a 100755 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java @@ -65,6 +65,14 @@ public class PlatformVoiceClient { } + public static void makePeerGlobal(EaglercraftUUID peerId) { + + } + + public static void makePeerProximity(EaglercraftUUID peerId) { + + } + public static void tickVoiceClient() { } diff --git a/src/main/java/com/carrotsearch/hppc/AbstractByteCollection.java b/src/main/java/com/carrotsearch/hppc/AbstractByteCollection.java deleted file mode 100755 index ed184354..00000000 --- a/src/main/java/com/carrotsearch/hppc/AbstractByteCollection.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ByteCursor; -import com.carrotsearch.hppc.predicates.BytePredicate; -import java.util.Arrays; - -/** Common superclass for collections. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "AbstractKTypeCollection.java") -abstract class AbstractByteCollection implements ByteCollection { - /** Default implementation uses a predicate for removal. */ - @Override - public int removeAll(final ByteLookupContainer c) { - return this.removeAll(c::contains); - } - - /** Default implementation uses a predicate for retaining. */ - @Override - public int retainAll(final ByteLookupContainer c) { - // We know c holds sub-types of byte and we're not modifying c, so go unchecked. - return this.removeAll(k -> !c.contains(k)); - } - - /** - * Default implementation redirects to {@link #removeAll(BytePredicate)} and negates the - * predicate. - */ - @Override - public int retainAll(final BytePredicate predicate) { - return removeAll(value -> !predicate.apply(value)); - } - - /** Default implementation of copying to an array. */ - @Override - public byte[] toArray() { - - byte[] array = (new byte[size()]); - int i = 0; - for (ByteCursor c : this) { - array[i++] = c.value; - } - return array; - } - - /** Convert the contents of this container to a human-friendly string. */ - @Override - public String toString() { - return Arrays.toString(this.toArray()); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/AbstractCharCollection.java b/src/main/java/com/carrotsearch/hppc/AbstractCharCollection.java deleted file mode 100755 index 5ecdbb55..00000000 --- a/src/main/java/com/carrotsearch/hppc/AbstractCharCollection.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.CharCursor; -import com.carrotsearch.hppc.predicates.CharPredicate; -import java.util.Arrays; - -/** Common superclass for collections. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "AbstractKTypeCollection.java") -abstract class AbstractCharCollection implements CharCollection { - /** Default implementation uses a predicate for removal. */ - @Override - public int removeAll(final CharLookupContainer c) { - return this.removeAll(c::contains); - } - - /** Default implementation uses a predicate for retaining. */ - @Override - public int retainAll(final CharLookupContainer c) { - // We know c holds sub-types of char and we're not modifying c, so go unchecked. - return this.removeAll(k -> !c.contains(k)); - } - - /** - * Default implementation redirects to {@link #removeAll(CharPredicate)} and negates the - * predicate. - */ - @Override - public int retainAll(final CharPredicate predicate) { - return removeAll(value -> !predicate.apply(value)); - } - - /** Default implementation of copying to an array. */ - @Override - public char[] toArray() { - - char[] array = (new char[size()]); - int i = 0; - for (CharCursor c : this) { - array[i++] = c.value; - } - return array; - } - - /** Convert the contents of this container to a human-friendly string. */ - @Override - public String toString() { - return Arrays.toString(this.toArray()); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/AbstractDoubleCollection.java b/src/main/java/com/carrotsearch/hppc/AbstractDoubleCollection.java deleted file mode 100755 index 2bb296ff..00000000 --- a/src/main/java/com/carrotsearch/hppc/AbstractDoubleCollection.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.DoubleCursor; -import com.carrotsearch.hppc.predicates.DoublePredicate; -import java.util.Arrays; - -/** Common superclass for collections. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "AbstractKTypeCollection.java") -abstract class AbstractDoubleCollection implements DoubleCollection { - /** Default implementation uses a predicate for removal. */ - @Override - public int removeAll(final DoubleLookupContainer c) { - return this.removeAll(c::contains); - } - - /** Default implementation uses a predicate for retaining. */ - @Override - public int retainAll(final DoubleLookupContainer c) { - // We know c holds sub-types of double and we're not modifying c, so go unchecked. - return this.removeAll(k -> !c.contains(k)); - } - - /** - * Default implementation redirects to {@link #removeAll(DoublePredicate)} and negates the - * predicate. - */ - @Override - public int retainAll(final DoublePredicate predicate) { - return removeAll(value -> !predicate.apply(value)); - } - - /** Default implementation of copying to an array. */ - @Override - public double[] toArray() { - - double[] array = (new double[size()]); - int i = 0; - for (DoubleCursor c : this) { - array[i++] = c.value; - } - return array; - } - - /** Convert the contents of this container to a human-friendly string. */ - @Override - public String toString() { - return Arrays.toString(this.toArray()); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/AbstractFloatCollection.java b/src/main/java/com/carrotsearch/hppc/AbstractFloatCollection.java deleted file mode 100755 index 089086f8..00000000 --- a/src/main/java/com/carrotsearch/hppc/AbstractFloatCollection.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.FloatCursor; -import com.carrotsearch.hppc.predicates.FloatPredicate; -import java.util.Arrays; - -/** Common superclass for collections. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "AbstractKTypeCollection.java") -abstract class AbstractFloatCollection implements FloatCollection { - /** Default implementation uses a predicate for removal. */ - @Override - public int removeAll(final FloatLookupContainer c) { - return this.removeAll(c::contains); - } - - /** Default implementation uses a predicate for retaining. */ - @Override - public int retainAll(final FloatLookupContainer c) { - // We know c holds sub-types of float and we're not modifying c, so go unchecked. - return this.removeAll(k -> !c.contains(k)); - } - - /** - * Default implementation redirects to {@link #removeAll(FloatPredicate)} and negates the - * predicate. - */ - @Override - public int retainAll(final FloatPredicate predicate) { - return removeAll(value -> !predicate.apply(value)); - } - - /** Default implementation of copying to an array. */ - @Override - public float[] toArray() { - - float[] array = (new float[size()]); - int i = 0; - for (FloatCursor c : this) { - array[i++] = c.value; - } - return array; - } - - /** Convert the contents of this container to a human-friendly string. */ - @Override - public String toString() { - return Arrays.toString(this.toArray()); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/AbstractIntCollection.java b/src/main/java/com/carrotsearch/hppc/AbstractIntCollection.java deleted file mode 100755 index e9ae748d..00000000 --- a/src/main/java/com/carrotsearch/hppc/AbstractIntCollection.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.IntCursor; -import com.carrotsearch.hppc.predicates.IntPredicate; -import java.util.Arrays; - -/** Common superclass for collections. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "AbstractKTypeCollection.java") -abstract class AbstractIntCollection implements IntCollection { - /** Default implementation uses a predicate for removal. */ - @Override - public int removeAll(final IntLookupContainer c) { - return this.removeAll(c::contains); - } - - /** Default implementation uses a predicate for retaining. */ - @Override - public int retainAll(final IntLookupContainer c) { - // We know c holds sub-types of int and we're not modifying c, so go unchecked. - return this.removeAll(k -> !c.contains(k)); - } - - /** - * Default implementation redirects to {@link #removeAll(IntPredicate)} and negates the predicate. - */ - @Override - public int retainAll(final IntPredicate predicate) { - return removeAll(value -> !predicate.apply(value)); - } - - /** Default implementation of copying to an array. */ - @Override - public int[] toArray() { - - int[] array = (new int[size()]); - int i = 0; - for (IntCursor c : this) { - array[i++] = c.value; - } - return array; - } - - /** Convert the contents of this container to a human-friendly string. */ - @Override - public String toString() { - return Arrays.toString(this.toArray()); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/AbstractIterator.java b/src/main/java/com/carrotsearch/hppc/AbstractIterator.java deleted file mode 100755 index 78951402..00000000 --- a/src/main/java/com/carrotsearch/hppc/AbstractIterator.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc; - -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** Simplifies the implementation of iterators a bit. Modeled loosely after Google Guava's API. */ -public abstract class AbstractIterator implements Iterator { - private static final int NOT_CACHED = 0; - private static final int CACHED = 1; - private static final int AT_END = 2; - - /** Current iterator state. */ - private int state = NOT_CACHED; - - /** The next element to be returned from {@link #next()} if fetched. */ - private E nextElement; - - /** {@inheritDoc} */ - @Override - public boolean hasNext() { - if (state == NOT_CACHED) { - state = CACHED; - nextElement = fetch(); - } - return state == CACHED; - } - - /** {@inheritDoc} */ - @Override - public E next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - - state = NOT_CACHED; - return nextElement; - } - - /** Default implementation throws {@link UnsupportedOperationException}. */ - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - /** - * Fetch next element. The implementation must return {@link #done()} when all elements have been - * fetched. - * - * @return Returns the next value for the iterator or chain-calls {@link #done()}. - */ - protected abstract E fetch(); - - /** - * Call when done. - * - * @return Returns a unique sentinel value to indicate end-of-iteration. - */ - protected final E done() { - state = AT_END; - return null; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/AbstractLongCollection.java b/src/main/java/com/carrotsearch/hppc/AbstractLongCollection.java deleted file mode 100755 index 914bc36e..00000000 --- a/src/main/java/com/carrotsearch/hppc/AbstractLongCollection.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.LongCursor; -import com.carrotsearch.hppc.predicates.LongPredicate; -import java.util.Arrays; - -/** Common superclass for collections. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "AbstractKTypeCollection.java") -abstract class AbstractLongCollection implements LongCollection { - /** Default implementation uses a predicate for removal. */ - @Override - public int removeAll(final LongLookupContainer c) { - return this.removeAll(c::contains); - } - - /** Default implementation uses a predicate for retaining. */ - @Override - public int retainAll(final LongLookupContainer c) { - // We know c holds sub-types of long and we're not modifying c, so go unchecked. - return this.removeAll(k -> !c.contains(k)); - } - - /** - * Default implementation redirects to {@link #removeAll(LongPredicate)} and negates the - * predicate. - */ - @Override - public int retainAll(final LongPredicate predicate) { - return removeAll(value -> !predicate.apply(value)); - } - - /** Default implementation of copying to an array. */ - @Override - public long[] toArray() { - - long[] array = (new long[size()]); - int i = 0; - for (LongCursor c : this) { - array[i++] = c.value; - } - return array; - } - - /** Convert the contents of this container to a human-friendly string. */ - @Override - public String toString() { - return Arrays.toString(this.toArray()); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/AbstractObjectCollection.java b/src/main/java/com/carrotsearch/hppc/AbstractObjectCollection.java deleted file mode 100755 index 1a841f62..00000000 --- a/src/main/java/com/carrotsearch/hppc/AbstractObjectCollection.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ObjectCursor; -import com.carrotsearch.hppc.predicates.ObjectPredicate; -import java.util.Arrays; - -/** Common superclass for collections. */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "AbstractKTypeCollection.java") -abstract class AbstractObjectCollection implements ObjectCollection { - /** Default implementation uses a predicate for removal. */ - @Override - public int removeAll(final ObjectLookupContainer c) { - return this.removeAll(c::contains); - } - - /** Default implementation uses a predicate for retaining. */ - @Override - public int retainAll(final ObjectLookupContainer c) { - // We know c holds sub-types of Object and we're not modifying c, so go unchecked. - return this.removeAll(k -> !c.contains(k)); - } - - /** - * Default implementation redirects to {@link #removeAll(ObjectPredicate)} and negates the - * predicate. - */ - @Override - public int retainAll(final ObjectPredicate predicate) { - return removeAll(value -> !predicate.apply(value)); - } - - /** Default implementation of copying to an array. */ - @Override - public Object[] toArray() { - - KType[] array = ((KType[]) new Object[size()]); - int i = 0; - for (ObjectCursor c : this) { - array[i++] = c.value; - } - return array; - } - - public T[] toArray(Class componentClass) { - final int size = size(); - final T[] array = (T[]) java.lang.reflect.Array.newInstance(componentClass, size); - int i = 0; - for (ObjectCursor c : this) { - array[i++] = (T) c.value; - } - return array; - } - - /** Convert the contents of this container to a human-friendly string. */ - @Override - public String toString() { - return Arrays.toString(this.toArray()); - } - - protected boolean equals(Object v1, Object v2) { - return (v1 == v2) || (v1 != null && v1.equals(v2)); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/AbstractShortCollection.java b/src/main/java/com/carrotsearch/hppc/AbstractShortCollection.java deleted file mode 100755 index e78dbe11..00000000 --- a/src/main/java/com/carrotsearch/hppc/AbstractShortCollection.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ShortCursor; -import com.carrotsearch.hppc.predicates.ShortPredicate; -import java.util.Arrays; - -/** Common superclass for collections. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "AbstractKTypeCollection.java") -abstract class AbstractShortCollection implements ShortCollection { - /** Default implementation uses a predicate for removal. */ - @Override - public int removeAll(final ShortLookupContainer c) { - return this.removeAll(c::contains); - } - - /** Default implementation uses a predicate for retaining. */ - @Override - public int retainAll(final ShortLookupContainer c) { - // We know c holds sub-types of short and we're not modifying c, so go unchecked. - return this.removeAll(k -> !c.contains(k)); - } - - /** - * Default implementation redirects to {@link #removeAll(ShortPredicate)} and negates the - * predicate. - */ - @Override - public int retainAll(final ShortPredicate predicate) { - return removeAll(value -> !predicate.apply(value)); - } - - /** Default implementation of copying to an array. */ - @Override - public short[] toArray() { - - short[] array = (new short[size()]); - int i = 0; - for (ShortCursor c : this) { - array[i++] = c.value; - } - return array; - } - - /** Convert the contents of this container to a human-friendly string. */ - @Override - public String toString() { - return Arrays.toString(this.toArray()); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/Accountable.java b/src/main/java/com/carrotsearch/hppc/Accountable.java deleted file mode 100755 index 44aefc9b..00000000 --- a/src/main/java/com/carrotsearch/hppc/Accountable.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc; - -/** - * Anything that could be accounted for memory usage - * - *

Partly forked from Lucene tag releases/lucene-solr/8.5.1 - */ -public interface Accountable { - /** - * Allocated memory estimation - * - * @return Ram allocated in bytes - */ - long ramBytesAllocated(); - - /** - * Bytes that is actually been used - * - * @return Ram used in bytes - */ - long ramBytesUsed(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ArraySizingStrategy.java b/src/main/java/com/carrotsearch/hppc/ArraySizingStrategy.java deleted file mode 100755 index b541c56f..00000000 --- a/src/main/java/com/carrotsearch/hppc/ArraySizingStrategy.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc; - -/** Resizing (growth) strategy for array-backed buffers. */ -public interface ArraySizingStrategy extends Accountable { - /** - * @param currentBufferLength Current size of the array (buffer). This number should comply with - * the strategy's policies (it is a result of initial rounding or further growCalls). It can - * also be zero, indicating the growth from an empty buffer. - * @param elementsCount Number of elements stored in the buffer. - * @param expectedAdditions Expected number of additions (resize hint). - * @return Must return a new size at least as big as to hold - * elementsCount + expectedAdditions. - * @throws BufferAllocationException If the sizing strategy cannot grow the buffer (for example - * due to constraints or memory limits). - */ - int grow(int currentBufferLength, int elementsCount, int expectedAdditions) - throws BufferAllocationException; -} diff --git a/src/main/java/com/carrotsearch/hppc/BitMixer.java b/src/main/java/com/carrotsearch/hppc/BitMixer.java deleted file mode 100755 index 136c12f9..00000000 --- a/src/main/java/com/carrotsearch/hppc/BitMixer.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc; - -/** - * Bit mixing utilities. The purpose of these methods is to evenly distribute key space over int32 - * range. - */ -public final class BitMixer { - - // Don't bother mixing very small key domains much. - public static int mix(byte key) { - return key * PHI_C32; - } - - public static int mix(short key) { - return mixPhi(key); - } - - public static int mix(char key) { - return mixPhi(key); - } - - // Better mix for larger key domains. - public static int mix(int key) { - return mix32(key); - } - - public static int mix(float key) { - return mix32(Float.floatToIntBits(key)); - } - - public static int mix(double key) { - return (int) mix64(Double.doubleToLongBits(key)); - } - - public static int mix(long key) { - return (int) mix64(key); - } - - public static int mix(Object key) { - return key == null ? 0 : mix32(key.hashCode()); - } - - /** MH3's plain finalization step. */ - public static int mix32(int k) { - k = (k ^ (k >>> 16)) * 0x85ebca6b; - k = (k ^ (k >>> 13)) * 0xc2b2ae35; - return k ^ (k >>> 16); - } - - /** - * Computes David Stafford variant 9 of 64bit mix function (MH3 finalization step, with different - * shifts and constants). - * - *

Variant 9 is picked because it contains two 32-bit shifts which could be possibly optimized - * into better machine code. - * - * @see "http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html" - */ - public static long mix64(long z) { - z = (z ^ (z >>> 32)) * 0x4cd6944c5cc20b6dL; - z = (z ^ (z >>> 29)) * 0xfc12c5b19d3259e9L; - return z ^ (z >>> 32); - } - - /* - * Golden ratio bit mixers. - */ - - private static final int PHI_C32 = 0x9e3779b9; - private static final long PHI_C64 = 0x9e3779b97f4a7c15L; - - public static int mixPhi(byte k) { - final int h = k * PHI_C32; - return h ^ (h >>> 16); - } - - public static int mixPhi(char k) { - final int h = k * PHI_C32; - return h ^ (h >>> 16); - } - - public static int mixPhi(short k) { - final int h = k * PHI_C32; - return h ^ (h >>> 16); - } - - public static int mixPhi(int k) { - final int h = k * PHI_C32; - return h ^ (h >>> 16); - } - - public static int mixPhi(float k) { - final int h = Float.floatToIntBits(k) * PHI_C32; - return h ^ (h >>> 16); - } - - public static int mixPhi(double k) { - final long h = Double.doubleToLongBits(k) * PHI_C64; - return (int) (h ^ (h >>> 32)); - } - - public static int mixPhi(long k) { - final long h = k * PHI_C64; - return (int) (h ^ (h >>> 32)); - } - - public static int mixPhi(Object k) { - final int h = (k == null ? 0 : k.hashCode() * PHI_C32); - return h ^ (h >>> 16); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/BitSet.java b/src/main/java/com/carrotsearch/hppc/BitSet.java deleted file mode 100755 index 7f8a94bc..00000000 --- a/src/main/java/com/carrotsearch/hppc/BitSet.java +++ /dev/null @@ -1,952 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.IntCursor; -import com.carrotsearch.hppc.cursors.LongCursor; -import com.carrotsearch.hppc.predicates.IntPredicate; -import com.carrotsearch.hppc.predicates.LongPredicate; -import com.carrotsearch.hppc.procedures.IntProcedure; -import com.carrotsearch.hppc.procedures.LongProcedure; -import java.util.*; - -/** - * An "open" BitSet implementation that allows direct access to the array of words storing the bits. - * - *

Unlike {@link java.util.BitSet}, the fact that bits are packed into an array of longs is part - * of the interface. This allows efficient implementation of other algorithms by someone other than - * the author. It also allows one to efficiently implement alternate serialization or interchange - * formats. - * - *

The index range for a bitset can easily exceed positive int range in Java - * (0x7fffffff), so many methods in this class accept or return a long. There are - * adapter methods that return views compatible with {@link LongLookupContainer} and {@link - * IntLookupContainer} interfaces. - * - * @see #asIntLookupContainer() - * @see #asLongLookupContainer() - */ -public class BitSet implements Cloneable { - /** The initial default number of bits. */ - private static final long DEFAULT_NUM_BITS = 64; - - /** Internal representation of bits in this bit set. */ - public long[] bits; - - /** The number of words (longs) used in the {@link #bits} array. */ - public int wlen; - - /** Constructs a bit set with the default capacity. */ - public BitSet() { - this(DEFAULT_NUM_BITS); - } - - /** - * Constructs an BitSet large enough to hold numBits. - * - * @param numBits Number of bits - */ - public BitSet(long numBits) { - bits = new long[bits2words(numBits)]; - wlen = bits.length; - } - - /** - * Constructs an BitSet from an existing long[]. - * - *

The first 64 bits are in long[0], with bit index 0 at the least significant bit, and bit - * index 63 at the most significant. Given a bit index, the word containing it is long[index/64], - * and it is at bit number index%64 within that word. - * - *

numWords are the number of elements in the array that contain set bits (non-zero longs). - * numWords should be <= bits.length, and any existing words in the array at position >= - * numWords should be zero. - * - * @param bits underlying bits buffer - * @param numWords the number of elements in the array that contain set bits - */ - public BitSet(long[] bits, int numWords) { - this.bits = bits; - this.wlen = numWords; - } - - /** - * Static constructor-like method similar to other (generic) collections. - * - * @return New instance. - */ - public static BitSet newInstance() { - return new BitSet(); - } - - /** - * @return Returns an iterator over all set bits of this bitset. The iterator should be faster - * than using a loop around {@link #nextSetBit(int)}. - */ - public BitSetIterator iterator() { - return new BitSetIterator(bits, wlen); - } - - /** - * @return Returns the current capacity in bits (1 greater than the index of the last bit). - */ - public long capacity() { - return bits.length << 6; - } - - /** - * @see #cardinality() - * @see java.util.BitSet#size() - * @return Returns the current capacity of this set. Included for compatibility. This is - * not equal to {@link #cardinality}. - */ - public long size() { - return capacity(); - } - - /** - * @see java.util.BitSet#length() - * @return Returns the "logical size" of this {@code BitSet}: the index of the highest set bit in - * the {@code BitSet} plus one. - */ - public long length() { - trimTrailingZeros(); - if (wlen == 0) return 0; - return (((long) wlen - 1) << 6) + (64 - Long.numberOfLeadingZeros(bits[wlen - 1])); - } - - /** - * @return Returns true if there are no set bits - */ - public boolean isEmpty() { - return cardinality() == 0; - } - - /** - * @param index The index. - * @return Returns true or false for the specified bit index. - */ - public boolean get(int index) { - int i = index >> 6; // div 64 - // signed shift will keep a negative index and force an - // array-index-out-of-bounds-exception, removing the need for an explicit check. - if (i >= bits.length) return false; - - int bit = index & 0x3f; // mod 64 - long bitmask = 1L << bit; - return (bits[i] & bitmask) != 0; - } - - /** - * @param index The index. - * @return Returns true or false for the specified bit index. - */ - public boolean get(long index) { - int i = (int) (index >> 6); // div 64 - if (i >= bits.length) return false; - int bit = (int) index & 0x3f; // mod 64 - long bitmask = 1L << bit; - return (bits[i] & bitmask) != 0; - } - - /** - * Sets a bit, expanding the set size if necessary. - * - * @param index the index to set - */ - public void set(long index) { - int wordNum = expandingWordNum(index); - int bit = (int) index & 0x3f; - long bitmask = 1L << bit; - bits[wordNum] |= bitmask; - } - - /** - * Sets a range of bits, expanding the set size if necessary - * - * @param startIndex lower index - * @param endIndex one-past the last bit to set - */ - public void set(long startIndex, long endIndex) { - if (endIndex <= startIndex) return; - - int startWord = (int) (startIndex >> 6); - - // since endIndex is one past the end, this is index of the last - // word to be changed. - int endWord = expandingWordNum(endIndex - 1); - - long startmask = -1L << startIndex; - long endmask = -1L >>> -endIndex; // 64-(endIndex&0x3f) is the same as -endIndex - // due to wrap - - if (startWord == endWord) { - bits[startWord] |= (startmask & endmask); - return; - } - - bits[startWord] |= startmask; - Arrays.fill(bits, startWord + 1, endWord, -1L); - bits[endWord] |= endmask; - } - - protected int expandingWordNum(long index) { - int wordNum = (int) (index >> 6); - if (wordNum >= wlen) { - ensureCapacity(index + 1); - wlen = wordNum + 1; - } - return wordNum; - } - - /** Clears all bits. */ - public void clear() { - Arrays.fill(bits, 0); - this.wlen = 0; - } - - /** - * clears a bit, allowing access beyond the current set size without changing the size. - * - * @param index the index to clear - */ - public void clear(long index) { - int wordNum = (int) (index >> 6); // div 64 - if (wordNum >= wlen) return; - int bit = (int) index & 0x3f; // mod 64 - long bitmask = 1L << bit; - bits[wordNum] &= ~bitmask; - } - - /** - * Clears a range of bits. Clearing past the end does not change the size of the set. - * - * @param startIndex lower index - * @param endIndex one-past the last bit to clear - */ - public void clear(int startIndex, int endIndex) { - if (endIndex <= startIndex) return; - - int startWord = (startIndex >> 6); - if (startWord >= wlen) return; - - // since endIndex is one past the end, this is index of the last - // word to be changed. - int endWord = ((endIndex - 1) >> 6); - - long startmask = -1L << startIndex; - long endmask = -1L >>> -endIndex; // 64-(endIndex&0x3f) is the same as -endIndex - // due to wrap - - // invert masks since we are clearing - startmask = ~startmask; - endmask = ~endmask; - - if (startWord == endWord) { - bits[startWord] &= (startmask | endmask); - return; - } - - bits[startWord] &= startmask; - - int middle = Math.min(wlen, endWord); - Arrays.fill(bits, startWord + 1, middle, 0L); - if (endWord < wlen) { - bits[endWord] &= endmask; - } - } - - /** - * Clears a range of bits. Clearing past the end does not change the size of the set. - * - * @param startIndex lower index - * @param endIndex one-past the last bit to clear - */ - public void clear(long startIndex, long endIndex) { - if (endIndex <= startIndex) return; - - int startWord = (int) (startIndex >> 6); - if (startWord >= wlen) return; - - // since endIndex is one past the end, this is index of the last - // word to be changed. - int endWord = (int) ((endIndex - 1) >> 6); - - long startmask = -1L << startIndex; - long endmask = -1L >>> -endIndex; // 64-(endIndex&0x3f) is the same as -endIndex - // due to wrap - - // invert masks since we are clearing - startmask = ~startmask; - endmask = ~endmask; - - if (startWord == endWord) { - bits[startWord] &= (startmask | endmask); - return; - } - - bits[startWord] &= startmask; - - int middle = Math.min(wlen, endWord); - Arrays.fill(bits, startWord + 1, middle, 0L); - if (endWord < wlen) { - bits[endWord] &= endmask; - } - } - - /** - * Sets a bit and returns the previous value. The index should be less than the BitSet size. - * - * @param index the index to set - * @return previous state of the index - */ - public boolean getAndSet(int index) { - int wordNum = index >> 6; // div 64 - int bit = index & 0x3f; // mod 64 - long bitmask = 1L << bit; - boolean val = (bits[wordNum] & bitmask) != 0; - bits[wordNum] |= bitmask; - return val; - } - - /** - * Sets a bit and returns the previous value. The index should be less than the BitSet size. - * - * @param index the index to set - * @return previous state of the index - */ - public boolean getAndSet(long index) { - int wordNum = (int) (index >> 6); // div 64 - int bit = (int) index & 0x3f; // mod 64 - long bitmask = 1L << bit; - boolean val = (bits[wordNum] & bitmask) != 0; - bits[wordNum] |= bitmask; - return val; - } - - /** - * Flips a bit, expanding the set size if necessary. - * - * @param index the index to flip - */ - public void flip(long index) { - int wordNum = expandingWordNum(index); - int bit = (int) index & 0x3f; // mod 64 - long bitmask = 1L << bit; - bits[wordNum] ^= bitmask; - } - - /** - * flips a bit and returns the resulting bit value. The index should be less than the BitSet size. - * - * @param index the index to flip - * @return previous state of the index - */ - public boolean flipAndGet(int index) { - int wordNum = index >> 6; // div 64 - int bit = index & 0x3f; // mod 64 - long bitmask = 1L << bit; - bits[wordNum] ^= bitmask; - return (bits[wordNum] & bitmask) != 0; - } - - /** - * flips a bit and returns the resulting bit value. The index should be less than the BitSet size. - * - * @param index the index to flip - * @return previous state of the index - */ - public boolean flipAndGet(long index) { - int wordNum = (int) (index >> 6); // div 64 - int bit = (int) index & 0x3f; // mod 64 - long bitmask = 1L << bit; - bits[wordNum] ^= bitmask; - return (bits[wordNum] & bitmask) != 0; - } - - /** - * Flips a range of bits, expanding the set size if necessary - * - * @param startIndex lower index - * @param endIndex one-past the last bit to flip - */ - public void flip(long startIndex, long endIndex) { - if (endIndex <= startIndex) return; - int startWord = (int) (startIndex >> 6); - - // since endIndex is one past the end, this is index of the last - // word to be changed. - int endWord = expandingWordNum(endIndex - 1); - - long startmask = -1L << startIndex; - long endmask = -1L >>> -endIndex; // 64-(endIndex&0x3f) is the same as -endIndex - // due to wrap - - if (startWord == endWord) { - bits[startWord] ^= (startmask & endmask); - return; - } - - bits[startWord] ^= startmask; - - for (int i = startWord + 1; i < endWord; i++) { - bits[i] = ~bits[i]; - } - - bits[endWord] ^= endmask; - } - - /** - * @return the number of set bits - */ - public long cardinality() { - return BitUtil.pop_array(bits, 0, wlen); - } - - /** - * @param a The first set - * @param b The second set - * @return Returns the popcount or cardinality of the intersection of the two sets. Neither set is - * modified. - */ - public static long intersectionCount(BitSet a, BitSet b) { - return BitUtil.pop_intersect(a.bits, b.bits, 0, Math.min(a.wlen, b.wlen)); - } - - /** - * @param a The first set - * @param b The second set - * @return Returns the popcount or cardinality of the union of the two sets. Neither set is - * modified. - */ - public static long unionCount(BitSet a, BitSet b) { - long tot = BitUtil.pop_union(a.bits, b.bits, 0, Math.min(a.wlen, b.wlen)); - if (a.wlen < b.wlen) { - tot += BitUtil.pop_array(b.bits, a.wlen, b.wlen - a.wlen); - } else if (a.wlen > b.wlen) { - tot += BitUtil.pop_array(a.bits, b.wlen, a.wlen - b.wlen); - } - return tot; - } - - /** - * @param a The first set - * @param b The second set - * @return Returns the popcount or cardinality of "a and not b" or "intersection(a, not(b))". - * Neither set is modified. - */ - public static long andNotCount(BitSet a, BitSet b) { - long tot = BitUtil.pop_andnot(a.bits, b.bits, 0, Math.min(a.wlen, b.wlen)); - if (a.wlen > b.wlen) { - tot += BitUtil.pop_array(a.bits, b.wlen, a.wlen - b.wlen); - } - return tot; - } - - /** - * @param a The first set - * @param b The second set - * @return Returns the popcount or cardinality of the exclusive-or of the two sets. Neither set is - * modified. - */ - public static long xorCount(BitSet a, BitSet b) { - long tot = BitUtil.pop_xor(a.bits, b.bits, 0, Math.min(a.wlen, b.wlen)); - if (a.wlen < b.wlen) { - tot += BitUtil.pop_array(b.bits, a.wlen, b.wlen - a.wlen); - } else if (a.wlen > b.wlen) { - tot += BitUtil.pop_array(a.bits, b.wlen, a.wlen - b.wlen); - } - return tot; - } - - /** - * @param index The index to start scanning from, inclusive. - * @return Returns the index of the first set bit starting at the index specified. -1 is returned - * if there are no more set bits. - */ - public int nextSetBit(int index) { - int i = index >> 6; - if (i >= wlen) return -1; - int subIndex = index & 0x3f; // index within the word - long word = bits[i] >> subIndex; // skip all the bits to the right of index - - if (word != 0) { - return (i << 6) + subIndex + Long.numberOfTrailingZeros(word); - } - - while (++i < wlen) { - word = bits[i]; - if (word != 0) return (i << 6) + Long.numberOfTrailingZeros(word); - } - - return -1; - } - - /** - * @param index The index to start scanning from, inclusive. - * @return Returns the index of the first set bit starting at the index specified. -1 is returned - * if there are no more set bits. - */ - public long nextSetBit(long index) { - int i = (int) (index >>> 6); - if (i >= wlen) return -1; - int subIndex = (int) index & 0x3f; // index within the word - long word = bits[i] >>> subIndex; // skip all the bits to the right of index - - if (word != 0) { - return (((long) i) << 6) + (subIndex + Long.numberOfTrailingZeros(word)); - } - - while (++i < wlen) { - word = bits[i]; - if (word != 0) return (((long) i) << 6) + Long.numberOfTrailingZeros(word); - } - - return -1; - } - - @Override - public Object clone() { - try { - BitSet obs = (BitSet) super.clone(); - obs.bits = (long[]) obs.bits.clone(); // hopefully an array clone is as - // fast(er) than arraycopy - return obs; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** - * this = this AND other - * - * @param other The bitset to intersect with. - */ - public void intersect(BitSet other) { - int newLen = Math.min(this.wlen, other.wlen); - long[] thisArr = this.bits; - long[] otherArr = other.bits; - // testing against zero can be more efficient - int pos = newLen; - while (--pos >= 0) { - thisArr[pos] &= otherArr[pos]; - } - if (this.wlen > newLen) { - // fill zeros from the new shorter length to the old length - Arrays.fill(bits, newLen, this.wlen, 0); - } - this.wlen = newLen; - } - - /** - * this = this OR other - * - * @param other The bitset to union with. - */ - public void union(BitSet other) { - int newLen = Math.max(wlen, other.wlen); - ensureCapacityWords(newLen); - - long[] thisArr = this.bits; - long[] otherArr = other.bits; - int pos = Math.min(wlen, other.wlen); - while (--pos >= 0) { - thisArr[pos] |= otherArr[pos]; - } - if (this.wlen < newLen) { - System.arraycopy(otherArr, this.wlen, thisArr, this.wlen, newLen - this.wlen); - } - this.wlen = newLen; - } - - /** - * Remove all elements set in other: this = this AND_NOT other - * - * @param other The other bitset. - */ - public void remove(BitSet other) { - int idx = Math.min(wlen, other.wlen); - long[] thisArr = this.bits; - long[] otherArr = other.bits; - while (--idx >= 0) { - thisArr[idx] &= ~otherArr[idx]; - } - } - - /** - * this = this XOR other - * - * @param other The other bitset. - */ - public void xor(BitSet other) { - int newLen = Math.max(wlen, other.wlen); - ensureCapacityWords(newLen); - - long[] thisArr = this.bits; - long[] otherArr = other.bits; - int pos = Math.min(wlen, other.wlen); - while (--pos >= 0) { - thisArr[pos] ^= otherArr[pos]; - } - if (this.wlen < newLen) { - System.arraycopy(otherArr, this.wlen, thisArr, this.wlen, newLen - this.wlen); - } - this.wlen = newLen; - } - - // some BitSet compatibility methods - - // ** see {@link intersect} */ - public void and(BitSet other) { - intersect(other); - } - - // ** see {@link union} */ - public void or(BitSet other) { - union(other); - } - - // ** see {@link andNot} */ - public void andNot(BitSet other) { - remove(other); - } - - /** - * @param other The other bitset. - * @return true if the sets have any elements in common - */ - public boolean intersects(BitSet other) { - int pos = Math.min(this.wlen, other.wlen); - long[] thisArr = this.bits; - long[] otherArr = other.bits; - while (--pos >= 0) { - if ((thisArr[pos] & otherArr[pos]) != 0) return true; - } - return false; - } - - /** - * Expand the long[] with the size given as a number of words (64 bit longs). getNumWords() is - * unchanged by this call. - * - * @param numWords The size to expand to (64-bit long words) - */ - public void ensureCapacityWords(int numWords) { - if (bits.length < numWords) { - bits = grow(bits, numWords); - } - } - - public static long[] grow(long[] array, int minSize) { - if (array.length < minSize) { - long[] newArray = new long[getNextSize(minSize)]; - System.arraycopy(array, 0, newArray, 0, array.length); - return newArray; - } else return array; - } - - public static int getNextSize(int targetSize) { - /* - * This over-allocates proportional to the list size, making room for additional - * growth. The over-allocation is mild, but is enough to give linear-time - * amortized behavior over a long sequence of appends() in the presence of a - * poorly-performing system realloc(). The growth pattern is: 0, 4, 8, 16, 25, 35, - * 46, 58, 72, 88, ... - */ - return (targetSize >> 3) + (targetSize < 9 ? 3 : 6) + targetSize; - } - - /** - * Ensure that the long[] is big enough to hold numBits, expanding it if necessary. getNumWords() - * is unchanged by this call. - * - * @param numBits The number of bits to expand to - */ - public void ensureCapacity(long numBits) { - ensureCapacityWords(bits2words(numBits)); - } - - /** Lowers {@link #wlen}, the number of words in use, by checking for trailing zero words. */ - public void trimTrailingZeros() { - int idx = wlen - 1; - while (idx >= 0 && bits[idx] == 0) idx--; - wlen = idx + 1; - } - - /* - * returns the number of 64 bit words it would take to hold numBits - */ - public static int bits2words(long numBits) { - return (int) (((numBits - 1) >>> 6) + 1); - } - - /* returns true if both sets have the same bits set */ - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof BitSet)) return false; - - BitSet a; - BitSet b = (BitSet) o; - - // make a the larger set. - if (b.wlen > this.wlen) { - a = b; - b = this; - } else { - a = this; - } - - // check for any set bits out of the range of b - for (int i = a.wlen - 1; i >= b.wlen; i--) { - if (a.bits[i] != 0) return false; - } - - for (int i = b.wlen - 1; i >= 0; i--) { - if (a.bits[i] != b.bits[i]) return false; - } - - return true; - } - - @Override - public int hashCode() { - // Start with a zero hash and use a mix that results in zero if the input is zero. - // This effectively truncates trailing zeros without an explicit check. - long h = 0; - for (int i = bits.length; --i >= 0; ) { - h ^= bits[i]; - h = (h << 1) | (h >>> 63); // rotate left - } - - // fold leftmost bits into right and add a constant to prevent - // empty sets from returning 0, which is too common. - return (int) ((h >> 32) ^ h) + 0x98761234; - } - - @Override - public String toString() { - long bit = nextSetBit(0); - if (bit < 0) { - return "{}"; - } - - final StringBuilder builder = new StringBuilder(); - builder.append("{"); - - builder.append(Long.toString(bit)); - while ((bit = nextSetBit(bit + 1)) >= 0) { - builder.append(", "); - builder.append(Long.toString(bit)); - } - builder.append("}"); - - return builder.toString(); - } - - /** - * Returns a view over this bitset data compatible with {@link IntLookupContainer}. A new object - * is always returned, but its methods reflect the current state of the bitset (the view is not a - * snapshot). - * - *

Methods of the returned {@link IntLookupContainer} may throw a {@link RuntimeException} if - * the cardinality of this bitset exceeds the int range. - * - * @return The view of this bitset as {@link IntLookupContainer}. - */ - public IntLookupContainer asIntLookupContainer() { - return new IntLookupContainer() { - @Override - public int size() { - return getCurrentCardinality(); - } - - @Override - public boolean isEmpty() { - return BitSet.this.isEmpty(); - } - - @Override - public Iterator iterator() { - return new Iterator() { - private long nextBitSet = BitSet.this.nextSetBit(0); - private final IntCursor cursor = new IntCursor(); - - @Override - public boolean hasNext() { - return nextBitSet >= 0; - } - - @Override - public IntCursor next() { - final long value = nextBitSet; - if (value < 0) throw new NoSuchElementException(); - if (value > Integer.MAX_VALUE) - throw new RuntimeException("BitSet range larger than maximum positive integer."); - - nextBitSet = BitSet.this.nextSetBit(value + 1); - cursor.index = cursor.value = (int) value; - return cursor; - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } - - @Override - public int[] toArray() { - final int[] data = new int[getCurrentCardinality()]; - final BitSetIterator i = BitSet.this.iterator(); - for (int j = 0, bit = i.nextSetBit(); bit >= 0; bit = i.nextSetBit()) { - data[j++] = bit; - } - return data; - } - - @Override - public T forEach(T predicate) { - final BitSetIterator i = BitSet.this.iterator(); - for (int bit = i.nextSetBit(); bit >= 0; bit = i.nextSetBit()) { - if (predicate.apply(bit) == false) break; - } - - return predicate; - } - - @Override - public T forEach(T procedure) { - final BitSetIterator i = BitSet.this.iterator(); - for (int bit = i.nextSetBit(); bit >= 0; bit = i.nextSetBit()) { - procedure.apply(bit); - } - - return procedure; - } - - @Override - public boolean contains(int index) { - return index < 0 || BitSet.this.get(index); - } - - /** - * Rounds the bitset's cardinality to an integer or throws a {@link RuntimeException} if the - * cardinality exceeds maximum int range. - */ - private int getCurrentCardinality() { - long cardinality = BitSet.this.cardinality(); - if (cardinality > Integer.MAX_VALUE) - throw new RuntimeException( - "Bitset is larger than maximum positive integer: " + cardinality); - return (int) cardinality; - } - }; - } - - /** - * Returns a view over this bitset data compatible with {@link LongLookupContainer}. A new object - * is always returned, but its methods reflect the current state of the bitset (the view is not a - * snapshot). - * - * @return The view of this bitset as {@link LongLookupContainer}. - */ - public LongLookupContainer asLongLookupContainer() { - return new LongLookupContainer() { - @Override - public int size() { - return getCurrentCardinality(); - } - - @Override - public boolean isEmpty() { - return BitSet.this.isEmpty(); - } - - @Override - public Iterator iterator() { - return new Iterator() { - private long nextBitSet = BitSet.this.nextSetBit(0); - private final LongCursor cursor = new LongCursor(); - - @Override - public boolean hasNext() { - return nextBitSet >= 0; - } - - @Override - public LongCursor next() { - final long value = nextBitSet; - if (value < 0) throw new NoSuchElementException(); - - nextBitSet = BitSet.this.nextSetBit(value + 1); - cursor.index = (int) value; - cursor.value = value; - return cursor; - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } - - @Override - public long[] toArray() { - final long[] data = new long[getCurrentCardinality()]; - final BitSet bset = BitSet.this; - int j = 0; - for (long bit = bset.nextSetBit((long) 0); bit >= 0; bit = bset.nextSetBit(bit + 1)) { - data[j++] = bit; - } - return data; - } - - @Override - public T forEach(T predicate) { - final BitSet bset = BitSet.this; - for (long bit = bset.nextSetBit((long) 0); bit >= 0; bit = bset.nextSetBit(bit + 1)) { - if (predicate.apply(bit) == false) break; - } - - return predicate; - } - - @Override - public T forEach(T procedure) { - final BitSet bset = BitSet.this; - for (long bit = bset.nextSetBit((long) 0); bit >= 0; bit = bset.nextSetBit(bit + 1)) { - procedure.apply(bit); - } - - return procedure; - } - - @Override - public boolean contains(long index) { - return index < 0 || BitSet.this.get(index); - } - - /** - * Rounds the bitset's cardinality to an integer or throws a {@link RuntimeException} if the - * cardinality exceeds maximum int range. - */ - private int getCurrentCardinality() { - long cardinality = BitSet.this.cardinality(); - if (cardinality > Integer.MAX_VALUE) - throw new RuntimeException( - "Bitset is larger than maximum positive integer: " + cardinality); - return (int) cardinality; - } - }; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/BitSetIterator.java b/src/main/java/com/carrotsearch/hppc/BitSetIterator.java deleted file mode 100755 index 35aa3e06..00000000 --- a/src/main/java/com/carrotsearch/hppc/BitSetIterator.java +++ /dev/null @@ -1,356 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc; - -/** - * An iterator to iterate over set bits in an BitSet. This is faster than nextSetBit() for iterating - * over the complete set of bits, especially when the density of the bits set is high. - */ -public class BitSetIterator { - // The General Idea: instead of having an array per byte that has - // the offsets of the next set bit, that array could be - // packed inside a 32 bit integer (8 4 bit numbers). That - // should be faster than accessing an array for each index, and - // the total array size is kept smaller (256*sizeof(int))=1K - static final int[] bitlist = { - 0x0, - 0x1, - 0x2, - 0x21, - 0x3, - 0x31, - 0x32, - 0x321, - 0x4, - 0x41, - 0x42, - 0x421, - 0x43, - 0x431, - 0x432, - 0x4321, - 0x5, - 0x51, - 0x52, - 0x521, - 0x53, - 0x531, - 0x532, - 0x5321, - 0x54, - 0x541, - 0x542, - 0x5421, - 0x543, - 0x5431, - 0x5432, - 0x54321, - 0x6, - 0x61, - 0x62, - 0x621, - 0x63, - 0x631, - 0x632, - 0x6321, - 0x64, - 0x641, - 0x642, - 0x6421, - 0x643, - 0x6431, - 0x6432, - 0x64321, - 0x65, - 0x651, - 0x652, - 0x6521, - 0x653, - 0x6531, - 0x6532, - 0x65321, - 0x654, - 0x6541, - 0x6542, - 0x65421, - 0x6543, - 0x65431, - 0x65432, - 0x654321, - 0x7, - 0x71, - 0x72, - 0x721, - 0x73, - 0x731, - 0x732, - 0x7321, - 0x74, - 0x741, - 0x742, - 0x7421, - 0x743, - 0x7431, - 0x7432, - 0x74321, - 0x75, - 0x751, - 0x752, - 0x7521, - 0x753, - 0x7531, - 0x7532, - 0x75321, - 0x754, - 0x7541, - 0x7542, - 0x75421, - 0x7543, - 0x75431, - 0x75432, - 0x754321, - 0x76, - 0x761, - 0x762, - 0x7621, - 0x763, - 0x7631, - 0x7632, - 0x76321, - 0x764, - 0x7641, - 0x7642, - 0x76421, - 0x7643, - 0x76431, - 0x76432, - 0x764321, - 0x765, - 0x7651, - 0x7652, - 0x76521, - 0x7653, - 0x76531, - 0x76532, - 0x765321, - 0x7654, - 0x76541, - 0x76542, - 0x765421, - 0x76543, - 0x765431, - 0x765432, - 0x7654321, - 0x8, - 0x81, - 0x82, - 0x821, - 0x83, - 0x831, - 0x832, - 0x8321, - 0x84, - 0x841, - 0x842, - 0x8421, - 0x843, - 0x8431, - 0x8432, - 0x84321, - 0x85, - 0x851, - 0x852, - 0x8521, - 0x853, - 0x8531, - 0x8532, - 0x85321, - 0x854, - 0x8541, - 0x8542, - 0x85421, - 0x8543, - 0x85431, - 0x85432, - 0x854321, - 0x86, - 0x861, - 0x862, - 0x8621, - 0x863, - 0x8631, - 0x8632, - 0x86321, - 0x864, - 0x8641, - 0x8642, - 0x86421, - 0x8643, - 0x86431, - 0x86432, - 0x864321, - 0x865, - 0x8651, - 0x8652, - 0x86521, - 0x8653, - 0x86531, - 0x86532, - 0x865321, - 0x8654, - 0x86541, - 0x86542, - 0x865421, - 0x86543, - 0x865431, - 0x865432, - 0x8654321, - 0x87, - 0x871, - 0x872, - 0x8721, - 0x873, - 0x8731, - 0x8732, - 0x87321, - 0x874, - 0x8741, - 0x8742, - 0x87421, - 0x8743, - 0x87431, - 0x87432, - 0x874321, - 0x875, - 0x8751, - 0x8752, - 0x87521, - 0x8753, - 0x87531, - 0x87532, - 0x875321, - 0x8754, - 0x87541, - 0x87542, - 0x875421, - 0x87543, - 0x875431, - 0x875432, - 0x8754321, - 0x876, - 0x8761, - 0x8762, - 0x87621, - 0x8763, - 0x87631, - 0x87632, - 0x876321, - 0x8764, - 0x87641, - 0x87642, - 0x876421, - 0x87643, - 0x876431, - 0x876432, - 0x8764321, - 0x8765, - 0x87651, - 0x87652, - 0x876521, - 0x87653, - 0x876531, - 0x876532, - 0x8765321, - 0x87654, - 0x876541, - 0x876542, - 0x8765421, - 0x876543, - 0x8765431, - 0x8765432, - 0x87654321 - }; - - /** - * *** the python code that generated bitlist def bits2int(val): arr=0 for shift in range(8,0,-1): - * if val & 0x80: arr = (arr << 4) | shift val = val << 1 return arr - * - *

def int_table(): tbl = [ hex(bits2int(val)).strip('L') for val in range(256) ] return - * ','.join(tbl) **** - */ - - // hmmm, what about an iterator that finds zeros though, - // or a reverse iterator... should they be separate classes - // for efficiency, or have a common root interface? (or - // maybe both? could ask for a SetBitsIterator, etc... - - private final long[] arr; - - private final int words; - private int i = -1; - private long word; - private int wordShift; - private int indexArray; - - public BitSetIterator(BitSet obs) { - this(obs.bits, obs.wlen); - } - - public BitSetIterator(long[] bits, int numWords) { - arr = bits; - words = numWords; - } - - // 64 bit shifts - private void shift() { - if ((int) word == 0) { - wordShift += 32; - word = word >>> 32; - } - if ((word & 0x0000FFFF) == 0) { - wordShift += 16; - word >>>= 16; - } - if ((word & 0x000000FF) == 0) { - wordShift += 8; - word >>>= 8; - } - indexArray = bitlist[(int) word & 0xff]; - } - - public static final int NO_MORE = -1; - - public int nextSetBit() { - if (indexArray == 0) { - if (word != 0) { - word >>>= 8; - wordShift += 8; - } - - while (word == 0) { - if (++i >= words) { - return NO_MORE; - } - word = arr[i]; - wordShift = -1; // loop invariant code motion should move this - } - - // after the first time, should I go with a linear search, or - // stick with the binary search in shift? - shift(); - } - - int bitIndex = (indexArray & 0x0f) + wordShift; - indexArray >>>= 4; - // should i<<6 be cached as a separate variable? - // it would only save one cycle in the best circumstances. - return (i << 6) + bitIndex; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/BitUtil.java b/src/main/java/com/carrotsearch/hppc/BitUtil.java deleted file mode 100755 index 61a628d5..00000000 --- a/src/main/java/com/carrotsearch/hppc/BitUtil.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc; - -/** A variety of high efficiency bit twiddling routines. */ -final class BitUtil { - private BitUtil() {} // no instance - - // The pop methods used to rely on bit-manipulation tricks for speed but it - // turns out that it is faster to use the Long.bitCount method (which is an - // intrinsic since Java 6u18) in a naive loop, see LUCENE-2221 - - /** Returns the number of set bits in an array of longs. */ - public static long pop_array(long[] arr, int wordOffset, int numWords) { - long popCount = 0; - for (int i = wordOffset, end = wordOffset + numWords; i < end; ++i) { - popCount += Long.bitCount(arr[i]); - } - return popCount; - } - - /** - * Returns the popcount or cardinality of the two sets after an intersection. Neither array is - * modified. - */ - public static long pop_intersect(long[] arr1, long[] arr2, int wordOffset, int numWords) { - long popCount = 0; - for (int i = wordOffset, end = wordOffset + numWords; i < end; ++i) { - popCount += Long.bitCount(arr1[i] & arr2[i]); - } - return popCount; - } - - /** Returns the popcount or cardinality of the union of two sets. Neither array is modified. */ - public static long pop_union(long[] arr1, long[] arr2, int wordOffset, int numWords) { - long popCount = 0; - for (int i = wordOffset, end = wordOffset + numWords; i < end; ++i) { - popCount += Long.bitCount(arr1[i] | arr2[i]); - } - return popCount; - } - - /** Returns the popcount or cardinality of A & ~B. Neither array is modified. */ - public static long pop_andnot(long[] arr1, long[] arr2, int wordOffset, int numWords) { - long popCount = 0; - for (int i = wordOffset, end = wordOffset + numWords; i < end; ++i) { - popCount += Long.bitCount(arr1[i] & ~arr2[i]); - } - return popCount; - } - - /** Returns the popcount or cardinality of A ^ B Neither array is modified. */ - public static long pop_xor(long[] arr1, long[] arr2, int wordOffset, int numWords) { - long popCount = 0; - for (int i = wordOffset, end = wordOffset + numWords; i < end; ++i) { - popCount += Long.bitCount(arr1[i] ^ arr2[i]); - } - return popCount; - } - - /** - * returns the next highest power of two, or the current value if it's already a power of two or - * zero - */ - public static int nextHighestPowerOfTwo(int v) { - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; - return v; - } - - /** - * returns the next highest power of two, or the current value if it's already a power of two or - * zero - */ - public static long nextHighestPowerOfTwo(long v) { - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v |= v >> 32; - v++; - return v; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/BoundedProportionalArraySizingStrategy.java b/src/main/java/com/carrotsearch/hppc/BoundedProportionalArraySizingStrategy.java deleted file mode 100755 index 14c5a9ab..00000000 --- a/src/main/java/com/carrotsearch/hppc/BoundedProportionalArraySizingStrategy.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc; - -import java.util.ArrayList; - -/** - * Array resizing proportional to the current buffer size, optionally kept within the given minimum - * and maximum growth limits. Java's {@link ArrayList} uses: - * - *

- * minGrow = 1
- * maxGrow = Integer.MAX_VALUE (unbounded)
- * growRatio = 1.5f
- * 
- */ -public final class BoundedProportionalArraySizingStrategy implements ArraySizingStrategy { - - /** Instance of {@link BoundedProportionalArraySizingStrategy} with default values. */ - public static final BoundedProportionalArraySizingStrategy DEFAULT_INSTANCE = - new BoundedProportionalArraySizingStrategy(); - - /** - * Maximum allocable array length (approximately the largest positive integer decreased by the - * array's object header). - */ - public static final int MAX_ARRAY_LENGTH = - Integer.MAX_VALUE - /* aligned array header + slack */ 32; - - /** Minimum grow count. */ - public static final int DEFAULT_MIN_GROW_COUNT = 10; - - /** Maximum grow count (unbounded). */ - public static final int DEFAULT_MAX_GROW_COUNT = MAX_ARRAY_LENGTH; - - /** Default resize is by half the current buffer's size. */ - public static final float DEFAULT_GROW_RATIO = 1.5f; - - /** Minimum number of elements to grow, if limit exceeded. */ - public final int minGrowCount; - - /** Maximum number of elements to grow, if limit exceeded. */ - public final int maxGrowCount; - - /** - * The current buffer length is multiplied by this ratio to get the first estimate for the new - * size. To double the size of the current buffer, for example, set to 2. - */ - public final float growRatio; - - /** Create the default sizing strategy. */ - public BoundedProportionalArraySizingStrategy() { - this(DEFAULT_MIN_GROW_COUNT, DEFAULT_MAX_GROW_COUNT, DEFAULT_GROW_RATIO); - } - - /** - * Create the sizing strategy with custom policies. - * - * @param minGrow Minimum number of elements to grow by when expanding. - * @param maxGrow Maximum number of elements to grow by when expanding. - * @param ratio The ratio of expansion compared to the previous buffer size. - */ - public BoundedProportionalArraySizingStrategy(int minGrow, int maxGrow, float ratio) { - assert minGrow >= 1 : "Min grow must be >= 1."; - assert maxGrow >= minGrow : "Max grow must be >= min grow."; - assert ratio >= 1f : "Growth ratio must be >= 1 (was " + ratio + ")."; - - this.minGrowCount = minGrow; - this.maxGrowCount = maxGrow; - this.growRatio = ratio - 1.0f; - } - - /** - * Grow according to {@link #growRatio}, {@link #minGrowCount} and {@link #maxGrowCount}. - * - * @param currentBufferLength The current length of the buffer. - * @param elementsCount The number of elements stored in the buffer. - * @param expectedAdditions The number of expected additions to the buffer. - * @return New buffer size. - */ - public int grow(int currentBufferLength, int elementsCount, int expectedAdditions) { - long growBy = (long) ((long) currentBufferLength * growRatio); - growBy = Math.max(growBy, minGrowCount); - growBy = Math.min(growBy, maxGrowCount); - long growTo = Math.min(MAX_ARRAY_LENGTH, growBy + currentBufferLength); - long newSize = Math.max((long) elementsCount + expectedAdditions, growTo); - - if (newSize > MAX_ARRAY_LENGTH) { - throw new BufferAllocationException( - "Java array size exceeded (current length: %d, elements: %d, expected additions: %d)", - currentBufferLength, elementsCount, expectedAdditions); - } - - return (int) newSize; - } - - @Override - public long ramBytesAllocated() { - // int: minGrowCount, maxGrowCount - // float: growRatio - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER + Integer.BYTES * 2 + Float.BYTES; - } - - @Override - public long ramBytesUsed() { - return ramBytesAllocated(); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/BufferAllocationException.java b/src/main/java/com/carrotsearch/hppc/BufferAllocationException.java deleted file mode 100755 index f09ef3c8..00000000 --- a/src/main/java/com/carrotsearch/hppc/BufferAllocationException.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc; - -import java.util.IllegalFormatException; -import java.util.Locale; - -public class BufferAllocationException extends RuntimeException { - public BufferAllocationException(String message) { - super(message); - } - - public BufferAllocationException(String message, Object... args) { - this(message, null, args); - } - - public BufferAllocationException(String message, Throwable t, Object... args) { - super(formatMessage(message, t, args), t); - } - - private static String formatMessage(String message, Throwable t, Object... args) { - try { - return String.format(Locale.ROOT, message, args); - } catch (IllegalFormatException e) { - BufferAllocationException substitute = - new BufferAllocationException(message + " [ILLEGAL FORMAT, ARGS SUPPRESSED]"); - if (t != null) { - substitute.addSuppressed(t); - } - substitute.addSuppressed(e); - throw substitute; - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ByteArrayDeque.java b/src/main/java/com/carrotsearch/hppc/ByteArrayDeque.java deleted file mode 100755 index b9c809c1..00000000 --- a/src/main/java/com/carrotsearch/hppc/ByteArrayDeque.java +++ /dev/null @@ -1,776 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; - -import com.carrotsearch.hppc.cursors.ByteCursor; -import com.carrotsearch.hppc.predicates.BytePredicate; -import com.carrotsearch.hppc.procedures.ByteProcedure; -import java.util.*; - -/** An array-backed {@link ByteDeque}. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeArrayDeque.java") -public class ByteArrayDeque extends AbstractByteCollection - implements ByteDeque, Preallocable, Cloneable, Accountable { - - /** Reuse the same strategy instance. */ - private static final BoundedProportionalArraySizingStrategy DEFAULT_SIZING_STRATEGY = - BoundedProportionalArraySizingStrategy.DEFAULT_INSTANCE; - - /** Internal array for storing elements of the deque. */ - public byte[] buffer = ByteArrayList.EMPTY_ARRAY; - - /** - * The index of the element at the head of the deque or an arbitrary number equal to tail if the - * deque is empty. - */ - public int head; - - /** The index at which the next element would be added to the tail of the deque. */ - public int tail; - - /** Buffer resizing strategy. */ - protected final ArraySizingStrategy resizer; - - /** New instance with sane defaults. */ - public ByteArrayDeque() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ByteArrayDeque(int expectedElements) { - this(expectedElements, DEFAULT_SIZING_STRATEGY); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public ByteArrayDeque(int expectedElements, ArraySizingStrategy resizer) { - assert resizer != null; - this.resizer = resizer; - ensureCapacity(expectedElements); - } - - /** - * Creates a new deque from elements of another container, appending elements at the end of the - * deque in the iteration order. - */ - public ByteArrayDeque(ByteContainer container) { - this(container.size()); - addLast(container); - } - - /** {@inheritDoc} */ - @Override - public void addFirst(byte e1) { - int h = oneLeft(head, buffer.length); - if (h == tail) { - ensureBufferSpace(1); - h = oneLeft(head, buffer.length); - } - buffer[head = h] = e1; - } - - /** - * Vararg-signature method for adding elements at the front of this deque. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - * - * @param elements The elements to add. - */ - public final void addFirst(byte... elements) { - ensureBufferSpace(elements.length); - for (byte k : elements) { - addFirst(k); - } - } - - /** - * Inserts all elements from the given container to the front of this deque. - * - * @param container The container to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addFirst(ByteContainer container) { - int size = container.size(); - ensureBufferSpace(size); - - for (ByteCursor cursor : container) { - addFirst(cursor.value); - } - - return size; - } - - /** - * Inserts all elements from the given iterable to the front of this deque. - * - * @param iterable The iterable to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addFirst(Iterable iterable) { - int size = 0; - for (ByteCursor cursor : iterable) { - addFirst(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public void addLast(byte e1) { - int t = oneRight(tail, buffer.length); - if (head == t) { - ensureBufferSpace(1); - t = oneRight(tail, buffer.length); - } - buffer[tail] = e1; - tail = t; - } - - /** - * Vararg-signature method for adding elements at the end of this deque. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - * - * @param elements The elements to iterate over. - */ - public final void addLast(byte... elements) { - ensureBufferSpace(1); - for (byte k : elements) { - addLast(k); - } - } - - /** - * Inserts all elements from the given container to the end of this deque. - * - * @param container The container to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addLast(ByteContainer container) { - int size = container.size(); - ensureBufferSpace(size); - - for (ByteCursor cursor : container) { - addLast(cursor.value); - } - - return size; - } - - /** - * Inserts all elements from the given iterable to the end of this deque. - * - * @param iterable The iterable to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addLast(Iterable iterable) { - int size = 0; - for (ByteCursor cursor : iterable) { - addLast(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public byte removeFirst() { - assert size() > 0 : "The deque is empty."; - - final byte result = buffer[head]; - buffer[head] = ((byte) 0); - head = oneRight(head, buffer.length); - return result; - } - - /** {@inheritDoc} */ - @Override - public byte removeLast() { - assert size() > 0 : "The deque is empty."; - - tail = oneLeft(tail, buffer.length); - final byte result = buffer[tail]; - buffer[tail] = ((byte) 0); - return result; - } - - /** {@inheritDoc} */ - @Override - public byte getFirst() { - assert size() > 0 : "The deque is empty."; - - return buffer[head]; - } - - /** {@inheritDoc} */ - @Override - public byte getLast() { - assert size() > 0 : "The deque is empty."; - - return buffer[oneLeft(tail, buffer.length)]; - } - - /** {@inheritDoc} */ - @Override - public int removeFirst(byte e1) { - final int index = bufferIndexOf(e1); - if (index >= 0) removeAtBufferIndex(index); - return index; - } - - /** - * Return the index of the first (counting from head) element equal to e1. The index - * points to the {@link #buffer} array. - * - * @param e1 The element to look for. - * @return Returns the index of the first element equal to e1 or -1 if - * not found. - */ - public int bufferIndexOf(byte e1) { - final int last = tail; - final int bufLen = buffer.length; - for (int i = head; i != last; i = oneRight(i, bufLen)) { - if (((e1) == (buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int removeLast(byte e1) { - final int index = lastBufferIndexOf(e1); - if (index >= 0) { - removeAtBufferIndex(index); - } - return index; - } - - /** - * Return the index of the last (counting from tail) element equal to e1. The index - * points to the {@link #buffer} array. - * - * @param e1 The element to look for. - * @return Returns the index of the first element equal to e1 or -1 if - * not found. - */ - public int lastBufferIndexOf(byte e1) { - final int bufLen = buffer.length; - final int last = oneLeft(head, bufLen); - for (int i = oneLeft(tail, bufLen); i != last; i = oneLeft(i, bufLen)) { - if (((e1) == (buffer[i]))) return i; - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(byte e1) { - int removed = 0; - final int last = tail; - final int bufLen = buffer.length; - int from, to; - for (from = to = head; from != last; from = oneRight(from, bufLen)) { - if (((e1) == (buffer[from]))) { - buffer[from] = ((byte) 0); - removed++; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = ((byte) 0); - } - - to = oneRight(to, bufLen); - } - - tail = to; - return removed; - } - - /** - * Removes the element at index in the internal {#link {@link #buffer} array, - * returning its value. - * - * @param index Index of the element to remove. The index must be located between {@link #head} - * and {@link #tail} in modulo {@link #buffer} arithmetic. - */ - public void removeAtBufferIndex(int index) { - assert (head <= tail ? index >= head && index < tail : index >= head || index < tail) - : "Index out of range (head=" + head + ", tail=" + tail + ", index=" + index + ")."; - - // Cache fields in locals (hopefully moved to registers). - final byte[] buffer = this.buffer; - final int bufLen = buffer.length; - final int lastIndex = bufLen - 1; - final int head = this.head; - final int tail = this.tail; - - final int leftChunk = Math.abs(index - head) % bufLen; - final int rightChunk = Math.abs(tail - index) % bufLen; - - if (leftChunk < rightChunk) { - if (index >= head) { - System.arraycopy(buffer, head, buffer, head + 1, leftChunk); - } else { - System.arraycopy(buffer, 0, buffer, 1, index); - buffer[0] = buffer[lastIndex]; - System.arraycopy(buffer, head, buffer, head + 1, lastIndex - head); - } - buffer[head] = ((byte) 0); - this.head = oneRight(head, bufLen); - } else { - if (index < tail) { - System.arraycopy(buffer, index + 1, buffer, index, rightChunk); - } else { - System.arraycopy(buffer, index + 1, buffer, index, lastIndex - index); - buffer[lastIndex] = buffer[0]; - System.arraycopy(buffer, 1, buffer, 0, tail); - } - buffer[tail] = ((byte) 0); - this.tail = oneLeft(tail, bufLen); - } - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int size() { - if (head <= tail) return tail - head; - else return (tail - head + buffer.length); - } - - /** - * {@inheritDoc} - * - *

The internal array buffers are not released as a result of this call. - * - * @see #release() - */ - @Override - public void clear() { - if (head < tail) { - Arrays.fill(buffer, head, tail, ((byte) 0)); - } else { - Arrays.fill(buffer, 0, tail, ((byte) 0)); - Arrays.fill(buffer, head, buffer.length, ((byte) 0)); - } - this.head = tail = 0; - } - - /** Release internal buffers of this deque and reallocate with the default buffer. */ - public void release() { - this.head = tail = 0; - buffer = ByteArrayList.EMPTY_ARRAY; - ensureBufferSpace(0); - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - ensureBufferSpace(expectedElements - size()); - } - - /** - * Ensures the internal buffer has enough free slots to store expectedAdditions. - * Increases internal buffer size if needed. - */ - protected void ensureBufferSpace(int expectedAdditions) { - final int bufferLen = buffer.length; - final int elementsCount = size(); - - if (elementsCount + expectedAdditions >= bufferLen) { - final int emptySlot = 1; // deque invariant: always an empty slot. - final int newSize = resizer.grow(bufferLen, elementsCount + emptySlot, expectedAdditions); - assert newSize >= (elementsCount + expectedAdditions + emptySlot) - : "Resizer failed to" - + " return sensible new size: " - + newSize - + " <= " - + (elementsCount + expectedAdditions); - - try { - final byte[] newBuffer = (new byte[newSize]); - if (bufferLen > 0) { - toArray(newBuffer); - tail = elementsCount; - head = 0; - } - this.buffer = newBuffer; - } catch (OutOfMemoryError e) { - throw new BufferAllocationException( - "Not enough memory to allocate new buffers: %,d -> %,d", e, bufferLen, newSize); - } - } - } - - /** {@inheritDoc} */ - @Override - public byte[] toArray() { - - final int size = size(); - return toArray((new byte[size])); - } - - /** - * Copies elements of this deque to an array. The content of the target array is - * filled from index 0 (head of the queue) to index size() - 1 (tail of the queue). - * - * @param target The target array must be large enough to hold all elements. - * @return Returns the target argument for chaining. - */ - public byte[] toArray(byte[] target) { - assert target.length >= size() : "Target array must be >= " + size(); - - if (head < tail) { - // The contents is not wrapped around. Just copy. - System.arraycopy(buffer, head, target, 0, size()); - } else if (head > tail) { - // The contents is split. Merge elements from the following indexes: - // [head...buffer.length - 1][0, tail - 1] - final int rightCount = buffer.length - head; - System.arraycopy(buffer, head, target, 0, rightCount); - System.arraycopy(buffer, 0, target, rightCount, tail); - } - - return target; - } - - /** - * Clone this object. The returned clone will reuse the same hash function and array resizing - * strategy. - */ - @Override - public ByteArrayDeque clone() { - try { - - ByteArrayDeque cloned = (ByteArrayDeque) super.clone(); - cloned.buffer = buffer.clone(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Move one index to the left, wrapping around buffer. */ - protected static int oneLeft(int index, int modulus) { - if (index >= 1) { - return index - 1; - } - return modulus - 1; - } - - /** Move one index to the right, wrapping around buffer. */ - protected static int oneRight(int index, int modulus) { - if (index + 1 == modulus) { - return 0; - } - return index + 1; - } - - @Override - public long ramBytesAllocated() { - // int: head, tail - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES * 2 - + resizer.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(buffer); - } - - @Override - public long ramBytesUsed() { - // int: head, tail - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES * 2 - + resizer.ramBytesUsed() - + RamUsageEstimator.shallowUsedSizeOfArray(buffer, size()); - } - - /** An iterator implementation for {@link ObjectArrayDeque#iterator}. */ - private final class ValueIterator extends AbstractIterator { - private final ByteCursor cursor; - private int remaining; - - public ValueIterator() { - cursor = new ByteCursor(); - cursor.index = oneLeft(head, buffer.length); - this.remaining = size(); - } - - @Override - protected ByteCursor fetch() { - if (remaining == 0) { - return done(); - } - - remaining--; - cursor.value = buffer[cursor.index = oneRight(cursor.index, buffer.length)]; - return cursor; - } - } - - /** An iterator implementation for {@link ObjectArrayDeque#descendingIterator()}. */ - private final class DescendingValueIterator extends AbstractIterator { - private final ByteCursor cursor; - private int remaining; - - public DescendingValueIterator() { - cursor = new ByteCursor(); - cursor.index = tail; - this.remaining = size(); - } - - @Override - protected ByteCursor fetch() { - if (remaining == 0) return done(); - - remaining--; - cursor.value = buffer[cursor.index = oneLeft(cursor.index, buffer.length)]; - return cursor; - } - } - - /** - * Returns a cursor over the values of this deque (in head to tail order). The iterator is - * implemented as a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()} (to avoid boxing of primitive types). To read the current value (or index in - * the deque's buffer) use the cursor's public fields. An example is shown below. - * - *

-   * for (IntValueCursor c : intDeque) {
-   *   System.out.println("buffer index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator iterator() { - return new ValueIterator(); - } - - /** - * Returns a cursor over the values of this deque (in tail to head order). The iterator is - * implemented as a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()} (to avoid boxing of primitive types). To read the current value (or index in - * the deque's buffer) use the cursor's public fields. An example is shown below. - * - *
-   * for (Iterator<IntCursor> i = intDeque.descendingIterator(); i.hasNext();) {
-   *   final IntCursor c = i.next();
-   *   System.out.println("buffer index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator descendingIterator() { - return new DescendingValueIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - forEach(procedure, head, tail); - return procedure; - } - - /** - * Applies procedure to a slice of the deque, fromIndex, inclusive, to - * toIndex, exclusive. - */ - private void forEach(ByteProcedure procedure, int fromIndex, final int toIndex) { - final byte[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - procedure.apply(buffer[i]); - } - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - int fromIndex = head; - int toIndex = tail; - - final byte[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - if (!predicate.apply(buffer[i])) { - break; - } - } - - return predicate; - } - - /** Applies procedure to all elements of this deque, tail to head. */ - @Override - public T descendingForEach(T procedure) { - descendingForEach(procedure, head, tail); - return procedure; - } - - /** - * Applies procedure to a slice of the deque, toIndex, exclusive, down - * to fromIndex, inclusive. - */ - private void descendingForEach(ByteProcedure procedure, int fromIndex, final int toIndex) { - if (fromIndex == toIndex) return; - - final byte[] buffer = this.buffer; - int i = toIndex; - do { - i = oneLeft(i, buffer.length); - procedure.apply(buffer[i]); - } while (i != fromIndex); - } - - /** {@inheritDoc} */ - @Override - public T descendingForEach(T predicate) { - descendingForEach(predicate, head, tail); - return predicate; - } - - /** - * Applies predicate to a slice of the deque, toIndex, exclusive, down - * to fromIndex, inclusive or until the predicate returns false. - */ - private void descendingForEach(BytePredicate predicate, int fromIndex, final int toIndex) { - if (fromIndex == toIndex) return; - - final byte[] buffer = this.buffer; - int i = toIndex; - do { - i = oneLeft(i, buffer.length); - if (!predicate.apply(buffer[i])) { - break; - } - } while (i != fromIndex); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(BytePredicate predicate) { - final byte[] buffer = this.buffer; - final int last = tail; - final int bufLen = buffer.length; - int removed = 0; - int from, to; - from = to = head; - try { - for (from = to = head; from != last; from = oneRight(from, bufLen)) { - if (predicate.apply(buffer[from])) { - buffer[from] = ((byte) 0); - removed++; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = ((byte) 0); - } - - to = oneRight(to, bufLen); - } - } finally { - // Keep the deque in consistent state even if the predicate throws an exception. - for (; from != last; from = oneRight(from, bufLen)) { - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = ((byte) 0); - } - - to = oneRight(to, bufLen); - } - tail = to; - } - - return removed; - } - - /** {@inheritDoc} */ - @Override - public boolean contains(byte e) { - int fromIndex = head; - int toIndex = tail; - - final byte[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - if (((e) == (buffer[i]))) { - return true; - } - } - - return false; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = 1; - int fromIndex = head; - int toIndex = tail; - - final byte[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - h = 31 * h + BitMixer.mix(this.buffer[i]); - } - return h; - } - - /** - * Returns true only if the other object is an instance of the same class and with - * the same elements. - */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Compare order-aligned elements against another {@link ByteDeque}. */ - protected boolean equalElements(ByteArrayDeque other) { - int max = size(); - if (other.size() != max) { - return false; - } - - Iterator i1 = this.iterator(); - Iterator i2 = other.iterator(); - - while (i1.hasNext() && i2.hasNext()) { - if (!((i1.next().value) == (i2.next().value))) { - return false; - } - } - - return !i1.hasNext() && !i2.hasNext(); - } - - /** Create a new deque by pushing a variable number of arguments to the end of it. */ - public static ByteArrayDeque from(byte... elements) { - final ByteArrayDeque coll = new ByteArrayDeque(elements.length); - coll.addLast(elements); - return coll; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ByteArrayList.java b/src/main/java/com/carrotsearch/hppc/ByteArrayList.java deleted file mode 100755 index 836c6b9d..00000000 --- a/src/main/java/com/carrotsearch/hppc/ByteArrayList.java +++ /dev/null @@ -1,579 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.BytePredicate; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** An array-backed list of bytes. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeArrayList.java") -public class ByteArrayList extends AbstractByteCollection - implements ByteIndexedContainer, Preallocable, Cloneable, Accountable { - /** An immutable empty buffer (array). */ - public static final byte[] EMPTY_ARRAY = new byte[0]; - - ; - - /** Reuse the same strategy instance. */ - private static final BoundedProportionalArraySizingStrategy DEFAULT_SIZING_STRATEGY = - BoundedProportionalArraySizingStrategy.DEFAULT_INSTANCE; - - /** - * Internal array for storing the list. The array may be larger than the current size ({@link - * #size()}). - */ - public byte[] buffer = EMPTY_ARRAY; - - /** Current number of elements stored in {@link #buffer}. */ - public int elementsCount; - - /** Buffer resizing strategy. */ - protected final ArraySizingStrategy resizer; - - /** New instance with sane defaults. */ - public ByteArrayList() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ByteArrayList(int expectedElements) { - this(expectedElements, DEFAULT_SIZING_STRATEGY); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public ByteArrayList(int expectedElements, ArraySizingStrategy resizer) { - assert resizer != null; - this.resizer = resizer; - buffer = Arrays.copyOf(buffer, expectedElements); - } - - /** Creates a new list from the elements of another container in its iteration order. */ - public ByteArrayList(ByteContainer container) { - this(container.size()); - addAll(container); - } - - /** {@inheritDoc} */ - @Override - public void add(byte e1) { - ensureBufferSpace(1); - buffer[elementsCount++] = e1; - } - - /** - * Appends two elements at the end of the list. To add more than two elements, use add - * (vararg-version) or access the buffer directly (tight loop). - */ - public void add(byte e1, byte e2) { - ensureBufferSpace(2); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - } - - /** Add all elements from a range of given array to the list. */ - public void add(byte[] elements, int start, int length) { - assert length >= 0 : "Length must be >= 0"; - - ensureBufferSpace(length); - System.arraycopy(elements, start, buffer, elementsCount, length); - elementsCount += length; - } - - /** - * Vararg-signature method for adding elements at the end of the list. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - */ - public final void add(byte... elements) { - add(elements, 0, elements.length); - } - - /** Adds all elements from another container. */ - public int addAll(ByteContainer container) { - final int size = container.size(); - ensureBufferSpace(size); - - for (ByteCursor cursor : container) { - add(cursor.value); - } - - return size; - } - - /** Adds all elements from another iterable. */ - public int addAll(Iterable iterable) { - int size = 0; - for (ByteCursor cursor : iterable) { - add(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public void insert(int index, byte e1) { - assert (index >= 0 && index <= size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + "]."; - - ensureBufferSpace(1); - System.arraycopy(buffer, index, buffer, index + 1, elementsCount - index); - buffer[index] = e1; - elementsCount++; - } - - /** {@inheritDoc} */ - @Override - public byte get(int index) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - return buffer[index]; - } - - /** {@inheritDoc} */ - @Override - public byte set(int index, byte e1) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - final byte v = buffer[index]; - buffer[index] = e1; - return v; - } - - /** {@inheritDoc} */ - @Override - public byte removeAt(int index) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - final byte v = buffer[index]; - System.arraycopy(buffer, index + 1, buffer, index, --elementsCount - index); - - return v; - } - - /** {@inheritDoc} */ - @Override - public byte removeLast() { - assert elementsCount > 0; - - final byte v = buffer[--elementsCount]; - - return v; - } - - /** {@inheritDoc} */ - @Override - public void removeRange(int fromIndex, int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - System.arraycopy(buffer, toIndex, buffer, fromIndex, elementsCount - toIndex); - final int count = toIndex - fromIndex; - elementsCount -= count; - } - - /** {@inheritDoc} */ - @Override - public boolean removeElement(byte e1) { - return removeFirst(e1) != -1; - } - - /** {@inheritDoc} */ - @Override - public int removeFirst(byte e1) { - final int index = indexOf(e1); - if (index >= 0) removeAt(index); - return index; - } - - /** {@inheritDoc} */ - @Override - public int removeLast(byte e1) { - final int index = lastIndexOf(e1); - if (index >= 0) removeAt(index); - return index; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(byte e1) { - int to = 0; - for (int from = 0; from < elementsCount; from++) { - if (((e1) == (buffer[from]))) { - continue; - } - if (to != from) { - buffer[to] = buffer[from]; - } - to++; - } - final int deleted = elementsCount - to; - this.elementsCount = to; - - return deleted; - } - - /** {@inheritDoc} */ - @Override - public boolean contains(byte e1) { - return indexOf(e1) >= 0; - } - - /** {@inheritDoc} */ - @Override - public int indexOf(byte e1) { - for (int i = 0; i < elementsCount; i++) { - if (((e1) == (buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int lastIndexOf(byte e1) { - for (int i = elementsCount - 1; i >= 0; i--) { - if (((e1) == (buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return elementsCount == 0; - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - final int bufferLen = (buffer == null ? 0 : buffer.length); - if (expectedElements > bufferLen) { - ensureBufferSpace(expectedElements - size()); - } - } - - /** - * Ensures the internal buffer has enough free slots to store expectedAdditions. - * Increases internal buffer size if needed. - */ - protected void ensureBufferSpace(int expectedAdditions) { - final int bufferLen = (buffer == null ? 0 : buffer.length); - if (elementsCount + expectedAdditions > bufferLen) { - final int newSize = resizer.grow(bufferLen, elementsCount, expectedAdditions); - assert newSize >= elementsCount + expectedAdditions - : "Resizer failed to" - + " return sensible new size: " - + newSize - + " <= " - + (elementsCount + expectedAdditions); - - this.buffer = Arrays.copyOf(buffer, newSize); - } - } - - /** - * Truncate or expand the list to the new size. If the list is truncated, the buffer will not be - * reallocated (use {@link #trimToSize()} if you need a truncated buffer), but the truncated - * values will be reset to the default value (zero). If the list is expanded, the elements beyond - * the current size are initialized with JVM-defaults (zero or null values). - */ - public void resize(int newSize) { - if (newSize <= buffer.length) { - if (newSize < elementsCount) { - Arrays.fill(buffer, newSize, elementsCount, ((byte) 0)); - } else { - Arrays.fill(buffer, elementsCount, newSize, ((byte) 0)); - } - } else { - ensureCapacity(newSize); - } - this.elementsCount = newSize; - } - - /** {@inheritDoc} */ - @Override - public int size() { - return elementsCount; - } - - /** Trim the internal buffer to the current size. */ - public void trimToSize() { - if (size() != this.buffer.length) { - this.buffer = toArray(); - } - } - - /** - * Sets the number of stored elements to zero. Releases and initializes the internal storage array - * to default values. To clear the list without cleaning the buffer, simply set the {@link - * #elementsCount} field to zero. - */ - @Override - public void clear() { - Arrays.fill(buffer, 0, elementsCount, ((byte) 0)); - this.elementsCount = 0; - } - - /** Sets the number of stored elements to zero and releases the internal storage array. */ - @Override - public void release() { - this.buffer = EMPTY_ARRAY; - this.elementsCount = 0; - } - - /** - * {@inheritDoc} - * - *

The returned array is sized to match exactly the number of elements of the stack. - */ - @Override - public byte[] toArray() { - - return Arrays.copyOf(buffer, elementsCount); - } - - /** {@inheritDoc} */ - @Override - public ByteIndexedContainer sort() { - Arrays.sort(buffer, 0, elementsCount); - return this; - } - - /** {@inheritDoc} */ - @Override - public ByteIndexedContainer reverse() { - for (int i = 0, mid = elementsCount >> 1, j = elementsCount - 1; i < mid; i++, j--) { - byte tmp = buffer[i]; - buffer[i] = buffer[j]; - buffer[j] = tmp; - } - return this; - } - - /** - * Clone this object. The returned clone will reuse the same hash function and array resizing - * strategy. - */ - @Override - public ByteArrayList clone() { - try { - - final ByteArrayList cloned = (ByteArrayList) super.clone(); - cloned.buffer = buffer.clone(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = 1, max = elementsCount; - for (int i = 0; i < max; i++) { - h = 31 * h + BitMixer.mix(this.buffer[i]); - } - return h; - } - - /** - * Returns true only if the other object is an instance of the same class and with - * the same elements. - */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Compare index-aligned elements against another {@link ByteIndexedContainer}. */ - protected boolean equalElements(ByteArrayList other) { - int max = size(); - if (other.size() != max) { - return false; - } - - for (int i = 0; i < max; i++) { - if (!((get(i)) == (other.get(i)))) { - return false; - } - } - - return true; - } - - @Override - public long ramBytesAllocated() { - // int: elementsCount - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES - + resizer.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(buffer); - } - - @Override - public long ramBytesUsed() { - // int: elementsCount - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES - + resizer.ramBytesUsed() - + RamUsageEstimator.shallowUsedSizeOfArray(buffer, elementsCount); - } - - /** An iterator implementation for {@link ByteArrayList#iterator}. */ - static final class ValueIterator extends AbstractIterator { - private final ByteCursor cursor; - - private final byte[] buffer; - private final int size; - - public ValueIterator(byte[] buffer, int size) { - this.cursor = new ByteCursor(); - this.cursor.index = -1; - this.size = size; - this.buffer = buffer; - } - - @Override - protected ByteCursor fetch() { - if (cursor.index + 1 == size) return done(); - - cursor.value = buffer[++cursor.index]; - return cursor; - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new ValueIterator(buffer, size()); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - return forEach(procedure, 0, size()); - } - - /** - * Applies procedure to a slice of the list, fromIndex, inclusive, to - * toIndex, exclusive. - */ - public T forEach(T procedure, int fromIndex, final int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - final byte[] buffer = this.buffer; - for (int i = fromIndex; i < toIndex; i++) { - procedure.apply(buffer[i]); - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(BytePredicate predicate) { - final byte[] buffer = this.buffer; - final int elementsCount = this.elementsCount; - int to = 0; - int from = 0; - try { - for (; from < elementsCount; from++) { - if (predicate.apply(buffer[from])) { - buffer[from] = ((byte) 0); - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = ((byte) 0); - } - to++; - } - } finally { - // Keep the list in a consistent state, even if the predicate throws an exception. - for (; from < elementsCount; from++) { - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = ((byte) 0); - } - to++; - } - - this.elementsCount = to; - } - - return elementsCount - to; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - return forEach(predicate, 0, size()); - } - - /** - * Applies predicate to a slice of the list, fromIndex, inclusive, to - * toIndex, exclusive, or until predicate returns false. - */ - public T forEach(T predicate, int fromIndex, final int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - final byte[] buffer = this.buffer; - for (int i = fromIndex; i < toIndex; i++) { - if (!predicate.apply(buffer[i])) break; - } - - return predicate; - } - - /** - * Create a list from a variable number of arguments or an array of byte. The - * elements are copied from the argument to the internal buffer. - */ - public static ByteArrayList from(byte... elements) { - final ByteArrayList list = new ByteArrayList(elements.length); - list.add(elements); - return list; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ByteBufferVisualizer.java b/src/main/java/com/carrotsearch/hppc/ByteBufferVisualizer.java deleted file mode 100755 index 0a39142a..00000000 --- a/src/main/java/com/carrotsearch/hppc/ByteBufferVisualizer.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.carrotsearch.hppc; - -/** - * Reused buffer visualization routines. - * - * @see ByteSet#visualizeKeyDistribution(int) - * @see ByteVTypeMap#visualizeKeyDistribution(int) - */ -class ByteBufferVisualizer { - static String visualizeKeyDistribution(byte[] buffer, int max, int characters) { - final StringBuilder b = new StringBuilder(); - final char[] chars = ".123456789X".toCharArray(); - for (int i = 1, start = -1; i <= characters; i++) { - int end = (int) ((long) i * max / characters); - - if (start + 1 <= end) { - int taken = 0; - int slots = 0; - for (int slot = start + 1; slot <= end; slot++, slots++) { - if (!((buffer[slot]) == 0)) { - taken++; - } - } - b.append(chars[Math.min(chars.length - 1, taken * chars.length / slots)]); - start = end; - } - } - while (b.length() < characters) { - b.append(' '); - } - return b.toString(); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ByteCollection.java b/src/main/java/com/carrotsearch/hppc/ByteCollection.java deleted file mode 100755 index 2e89418e..00000000 --- a/src/main/java/com/carrotsearch/hppc/ByteCollection.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.predicates.BytePredicate; - -/** - * A collection allows basic, efficient operations on sets of elements (difference and - * intersection). - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeCollection.java") -public interface ByteCollection extends ByteContainer { - /** - * Removes all occurrences of e from this collection. - * - * @param e Element to be removed from this collection, if present. - * @return The number of removed elements as a result of this call. - */ - public int removeAll(byte e); - - /** - * Removes all elements in this collection that are present in c. - * - * @return Returns the number of removed elements. - */ - public int removeAll(ByteLookupContainer c); - - /** - * Removes all elements in this collection for which the given predicate returns true - * . - * - * @return Returns the number of removed elements. - */ - public int removeAll(BytePredicate predicate); - - /** - * Keeps all elements in this collection that are present in c. Runs in time - * proportional to the number of elements in this collection. Equivalent of sets intersection. - * - * @return Returns the number of removed elements. - */ - public int retainAll(ByteLookupContainer c); - - /** - * Keeps all elements in this collection for which the given predicate returns true. - * - * @return Returns the number of removed elements. - */ - public int retainAll(BytePredicate predicate); - - /** - * Removes all elements from this collection. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ByteContainer.java b/src/main/java/com/carrotsearch/hppc/ByteContainer.java deleted file mode 100755 index 19edc86b..00000000 --- a/src/main/java/com/carrotsearch/hppc/ByteContainer.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ByteCursor; -import com.carrotsearch.hppc.predicates.BytePredicate; -import com.carrotsearch.hppc.procedures.ByteProcedure; -import java.util.Iterator; - -/** A generic container holding bytes. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeContainer.java") -public interface ByteContainer extends Iterable { - /** - * Returns an iterator to a cursor traversing the collection. The order of traversal is not - * defined. More than one cursor may be active at a time. The behavior of iterators is undefined - * if structural changes are made to the underlying collection. - * - *

The iterator is implemented as a cursor and it returns the same cursor instance on - * every call to {@link Iterator#next()} (to avoid boxing of primitive types). To read the current - * list's value (or index in the list) use the cursor's public fields. An example is shown below. - * - *

-   * for (ByteCursor<byte> c : container) {
-   *   System.out.println("index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator iterator(); - - /** - * Lookup a given element in the container. This operation has no speed guarantees (may be linear - * with respect to the size of this container). - * - * @return Returns true if this container has an element equal to e. - */ - public boolean contains(byte e); - - /** - * Return the current number of elements in this container. The time for calculating the - * container's size may take O(n) time, although implementing classes should try to - * maintain the current size and return in constant time. - */ - public int size(); - - /** Shortcut for size() == 0. */ - public boolean isEmpty(); - - /** - * Copies all elements of this container to an array. - * - *

The returned array is always a copy, regardless of the storage used by the container. - */ - public byte[] toArray(); - - /** - * Applies a procedure to all container elements. Returns the argument (any subclass - * of {@link ByteProcedure}. This lets the caller to call methods of the argument by chaining the - * call (even if the argument is an anonymous type) to retrieve computed values, for example - * (IntContainer): - * - *

-   * int count = container.forEach(new IntProcedure() {
-   *   int count; // this is a field declaration in an anonymous class.
-   *
-   *   public void apply(int value) {
-   *     count++;
-   *   }
-   * }).count;
-   * 
- */ - public T forEach(T procedure); - - /** - * Applies a predicate to container elements as long, as the predicate returns - * true. The iteration is interrupted otherwise. - */ - public T forEach(T predicate); -} diff --git a/src/main/java/com/carrotsearch/hppc/ByteDeque.java b/src/main/java/com/carrotsearch/hppc/ByteDeque.java deleted file mode 100755 index 3b8dd7fe..00000000 --- a/src/main/java/com/carrotsearch/hppc/ByteDeque.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ByteCursor; -import com.carrotsearch.hppc.predicates.BytePredicate; -import com.carrotsearch.hppc.procedures.ByteProcedure; -import java.util.Deque; -import java.util.Iterator; - -/** - * A linear collection that supports element insertion and removal at both ends. - * - * @see Deque - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeDeque.java") -public interface ByteDeque extends ByteCollection { - /** - * Removes the first element that equals e. - * - * @return The deleted element's index or -1 if the element was not found. - */ - public int removeFirst(byte e); - - /** - * Removes the last element that equals e. - * - * @return The deleted element's index or -1 if the element was not found. - */ - public int removeLast(byte e); - - /** Inserts the specified element at the front of this deque. */ - public void addFirst(byte e); - - /** Inserts the specified element at the end of this deque. */ - public void addLast(byte e); - - /** - * Retrieves and removes the first element of this deque. - * - * @return the head (first) element of this deque. - */ - public byte removeFirst(); - - /** - * Retrieves and removes the last element of this deque. - * - * @return the tail of this deque. - */ - public byte removeLast(); - - /** - * Retrieves the first element of this deque but does not remove it. - * - * @return the head of this deque. - */ - public byte getFirst(); - - /** - * Retrieves the last element of this deque but does not remove it. - * - * @return the head of this deque. - */ - public byte getLast(); - - /** - * @return An iterator over elements in this deque in tail-to-head order. - */ - public Iterator descendingIterator(); - - /** Applies a procedure to all elements in tail-to-head order. */ - public T descendingForEach(T procedure); - - /** - * Applies a predicate to container elements as long, as the predicate returns - * true. The iteration is interrupted otherwise. - */ - public T descendingForEach(T predicate); -} diff --git a/src/main/java/com/carrotsearch/hppc/ByteIndexedContainer.java b/src/main/java/com/carrotsearch/hppc/ByteIndexedContainer.java deleted file mode 100755 index f70fb895..00000000 --- a/src/main/java/com/carrotsearch/hppc/ByteIndexedContainer.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.carrotsearch.hppc; - -import java.util.RandomAccess; - -/** - * An indexed container provides random access to elements based on an index. Indexes - * are zero-based. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeIndexedContainer.java") -public interface ByteIndexedContainer extends ByteCollection, RandomAccess { - /** - * Removes the first element that equals e1, returning whether an element has been - * removed. - */ - public boolean removeElement(byte e1); - - /** - * Removes the first element that equals e1, returning its deleted position or - * -1 if the element was not found. - */ - public int removeFirst(byte e1); - - /** - * Removes the last element that equals e1, returning its deleted position or - * -1 if the element was not found. - */ - public int removeLast(byte e1); - - /** - * Returns the index of the first occurrence of the specified element in this list, or -1 if this - * list does not contain the element. - */ - public int indexOf(byte e1); - - /** - * Returns the index of the last occurrence of the specified element in this list, or -1 if this - * list does not contain the element. - */ - public int lastIndexOf(byte e1); - - /** Adds an element to the end of this container (the last index is incremented by one). */ - public void add(byte e1); - - /** - * Inserts the specified element at the specified position in this list. - * - * @param index The index at which the element should be inserted, shifting any existing and - * subsequent elements to the right. - */ - public void insert(int index, byte e1); - - /** - * Replaces the element at the specified position in this list with the specified element. - * - * @return Returns the previous value in the list. - */ - public byte set(int index, byte e1); - - /** - * @return Returns the element at index index from the list. - */ - public byte get(int index); - - /** - * Removes the element at the specified position in this container and returns it. - * - * @see #removeFirst - * @see #removeLast - * @see #removeAll - */ - public byte removeAt(int index); - - /** Removes and returns the last element of this container. This container must not be empty. */ - public byte removeLast(); - - /** - * Removes from this container all of the elements with indexes between fromIndex, - * inclusive, and toIndex, exclusive. - */ - public void removeRange(int fromIndex, int toIndex); - - /** Returns this container elements as a stream. */ - - /** Sorts the elements in this container and returns this container. */ - public ByteIndexedContainer sort(); - - /** Reverses the elements in this container and returns this container. */ - public ByteIndexedContainer reverse(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ByteLookupContainer.java b/src/main/java/com/carrotsearch/hppc/ByteLookupContainer.java deleted file mode 100755 index ac41bc0b..00000000 --- a/src/main/java/com/carrotsearch/hppc/ByteLookupContainer.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.carrotsearch.hppc; - -/** - * Marker interface for containers that can check if they contain a given object in at least time - * O(log n) and ideally in amortized constant time O(1). - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeLookupContainer.java") -public interface ByteLookupContainer extends ByteContainer { - public boolean contains(byte e); -} diff --git a/src/main/java/com/carrotsearch/hppc/ByteStack.java b/src/main/java/com/carrotsearch/hppc/ByteStack.java deleted file mode 100755 index 0cf32432..00000000 --- a/src/main/java/com/carrotsearch/hppc/ByteStack.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ByteCursor; - -/** - * A subclass of {@link ByteArrayList} adding stack-related utility methods. The top of the stack is - * at the {@link #size()} - 1 element. - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeStack.java") -public class ByteStack extends ByteArrayList { - /** New instance with sane defaults. */ - public ByteStack() { - super(); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ByteStack(int expectedElements) { - super(expectedElements); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public ByteStack(int expectedElements, ArraySizingStrategy resizer) { - super(expectedElements, resizer); - } - - /** Create a stack by pushing all elements of another container to it. */ - public ByteStack(ByteContainer container) { - super(container); - } - - /** Adds one byte to the stack. */ - public void push(byte e1) { - ensureBufferSpace(1); - buffer[elementsCount++] = e1; - } - - /** Adds two bytes to the stack. */ - public void push(byte e1, byte e2) { - ensureBufferSpace(2); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - } - - /** Adds three bytes to the stack. */ - public void push(byte e1, byte e2, byte e3) { - ensureBufferSpace(3); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - buffer[elementsCount++] = e3; - } - - /** Adds four bytes to the stack. */ - public void push(byte e1, byte e2, byte e3, byte e4) { - ensureBufferSpace(4); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - buffer[elementsCount++] = e3; - buffer[elementsCount++] = e4; - } - - /** Add a range of array elements to the stack. */ - public void push(byte[] elements, int start, int len) { - assert start >= 0 && len >= 0; - - ensureBufferSpace(len); - System.arraycopy(elements, start, buffer, elementsCount, len); - elementsCount += len; - } - - /** - * Vararg-signature method for pushing elements at the top of the stack. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - */ - public final void push(byte... elements) { - push(elements, 0, elements.length); - } - - /** Pushes all elements from another container to the top of the stack. */ - public int pushAll(ByteContainer container) { - return addAll(container); - } - - /** Pushes all elements from another iterable to the top of the stack. */ - public int pushAll(Iterable iterable) { - return addAll(iterable); - } - - /** Discard an arbitrary number of elements from the top of the stack. */ - public void discard(int count) { - assert elementsCount >= count; - - elementsCount -= count; - } - - /** Discard the top element from the stack. */ - public void discard() { - assert elementsCount > 0; - - elementsCount--; - } - - /** Remove the top element from the stack and return it. */ - public byte pop() { - return removeLast(); - } - - /** Peek at the top element on the stack. */ - public byte peek() { - assert elementsCount > 0; - return buffer[elementsCount - 1]; - } - - /** Create a stack by pushing a variable number of arguments to it. */ - public static ByteStack from(byte... elements) { - final ByteStack stack = new ByteStack(elements.length); - stack.push(elements); - return stack; - } - - /** {@inheritDoc} */ - @Override - public ByteStack clone() { - return (ByteStack) super.clone(); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/CharArrayDeque.java b/src/main/java/com/carrotsearch/hppc/CharArrayDeque.java deleted file mode 100755 index e8aeb6a4..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharArrayDeque.java +++ /dev/null @@ -1,776 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; - -import com.carrotsearch.hppc.cursors.CharCursor; -import com.carrotsearch.hppc.predicates.CharPredicate; -import com.carrotsearch.hppc.procedures.CharProcedure; -import java.util.*; - -/** An array-backed {@link CharDeque}. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeArrayDeque.java") -public class CharArrayDeque extends AbstractCharCollection - implements CharDeque, Preallocable, Cloneable, Accountable { - - /** Reuse the same strategy instance. */ - private static final BoundedProportionalArraySizingStrategy DEFAULT_SIZING_STRATEGY = - BoundedProportionalArraySizingStrategy.DEFAULT_INSTANCE; - - /** Internal array for storing elements of the deque. */ - public char[] buffer = CharArrayList.EMPTY_ARRAY; - - /** - * The index of the element at the head of the deque or an arbitrary number equal to tail if the - * deque is empty. - */ - public int head; - - /** The index at which the next element would be added to the tail of the deque. */ - public int tail; - - /** Buffer resizing strategy. */ - protected final ArraySizingStrategy resizer; - - /** New instance with sane defaults. */ - public CharArrayDeque() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public CharArrayDeque(int expectedElements) { - this(expectedElements, DEFAULT_SIZING_STRATEGY); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public CharArrayDeque(int expectedElements, ArraySizingStrategy resizer) { - assert resizer != null; - this.resizer = resizer; - ensureCapacity(expectedElements); - } - - /** - * Creates a new deque from elements of another container, appending elements at the end of the - * deque in the iteration order. - */ - public CharArrayDeque(CharContainer container) { - this(container.size()); - addLast(container); - } - - /** {@inheritDoc} */ - @Override - public void addFirst(char e1) { - int h = oneLeft(head, buffer.length); - if (h == tail) { - ensureBufferSpace(1); - h = oneLeft(head, buffer.length); - } - buffer[head = h] = e1; - } - - /** - * Vararg-signature method for adding elements at the front of this deque. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - * - * @param elements The elements to add. - */ - public final void addFirst(char... elements) { - ensureBufferSpace(elements.length); - for (char k : elements) { - addFirst(k); - } - } - - /** - * Inserts all elements from the given container to the front of this deque. - * - * @param container The container to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addFirst(CharContainer container) { - int size = container.size(); - ensureBufferSpace(size); - - for (CharCursor cursor : container) { - addFirst(cursor.value); - } - - return size; - } - - /** - * Inserts all elements from the given iterable to the front of this deque. - * - * @param iterable The iterable to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addFirst(Iterable iterable) { - int size = 0; - for (CharCursor cursor : iterable) { - addFirst(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public void addLast(char e1) { - int t = oneRight(tail, buffer.length); - if (head == t) { - ensureBufferSpace(1); - t = oneRight(tail, buffer.length); - } - buffer[tail] = e1; - tail = t; - } - - /** - * Vararg-signature method for adding elements at the end of this deque. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - * - * @param elements The elements to iterate over. - */ - public final void addLast(char... elements) { - ensureBufferSpace(1); - for (char k : elements) { - addLast(k); - } - } - - /** - * Inserts all elements from the given container to the end of this deque. - * - * @param container The container to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addLast(CharContainer container) { - int size = container.size(); - ensureBufferSpace(size); - - for (CharCursor cursor : container) { - addLast(cursor.value); - } - - return size; - } - - /** - * Inserts all elements from the given iterable to the end of this deque. - * - * @param iterable The iterable to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addLast(Iterable iterable) { - int size = 0; - for (CharCursor cursor : iterable) { - addLast(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public char removeFirst() { - assert size() > 0 : "The deque is empty."; - - final char result = buffer[head]; - buffer[head] = ((char) 0); - head = oneRight(head, buffer.length); - return result; - } - - /** {@inheritDoc} */ - @Override - public char removeLast() { - assert size() > 0 : "The deque is empty."; - - tail = oneLeft(tail, buffer.length); - final char result = buffer[tail]; - buffer[tail] = ((char) 0); - return result; - } - - /** {@inheritDoc} */ - @Override - public char getFirst() { - assert size() > 0 : "The deque is empty."; - - return buffer[head]; - } - - /** {@inheritDoc} */ - @Override - public char getLast() { - assert size() > 0 : "The deque is empty."; - - return buffer[oneLeft(tail, buffer.length)]; - } - - /** {@inheritDoc} */ - @Override - public int removeFirst(char e1) { - final int index = bufferIndexOf(e1); - if (index >= 0) removeAtBufferIndex(index); - return index; - } - - /** - * Return the index of the first (counting from head) element equal to e1. The index - * points to the {@link #buffer} array. - * - * @param e1 The element to look for. - * @return Returns the index of the first element equal to e1 or -1 if - * not found. - */ - public int bufferIndexOf(char e1) { - final int last = tail; - final int bufLen = buffer.length; - for (int i = head; i != last; i = oneRight(i, bufLen)) { - if (((e1) == (buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int removeLast(char e1) { - final int index = lastBufferIndexOf(e1); - if (index >= 0) { - removeAtBufferIndex(index); - } - return index; - } - - /** - * Return the index of the last (counting from tail) element equal to e1. The index - * points to the {@link #buffer} array. - * - * @param e1 The element to look for. - * @return Returns the index of the first element equal to e1 or -1 if - * not found. - */ - public int lastBufferIndexOf(char e1) { - final int bufLen = buffer.length; - final int last = oneLeft(head, bufLen); - for (int i = oneLeft(tail, bufLen); i != last; i = oneLeft(i, bufLen)) { - if (((e1) == (buffer[i]))) return i; - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(char e1) { - int removed = 0; - final int last = tail; - final int bufLen = buffer.length; - int from, to; - for (from = to = head; from != last; from = oneRight(from, bufLen)) { - if (((e1) == (buffer[from]))) { - buffer[from] = ((char) 0); - removed++; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = ((char) 0); - } - - to = oneRight(to, bufLen); - } - - tail = to; - return removed; - } - - /** - * Removes the element at index in the internal {#link {@link #buffer} array, - * returning its value. - * - * @param index Index of the element to remove. The index must be located between {@link #head} - * and {@link #tail} in modulo {@link #buffer} arithmetic. - */ - public void removeAtBufferIndex(int index) { - assert (head <= tail ? index >= head && index < tail : index >= head || index < tail) - : "Index out of range (head=" + head + ", tail=" + tail + ", index=" + index + ")."; - - // Cache fields in locals (hopefully moved to registers). - final char[] buffer = this.buffer; - final int bufLen = buffer.length; - final int lastIndex = bufLen - 1; - final int head = this.head; - final int tail = this.tail; - - final int leftChunk = Math.abs(index - head) % bufLen; - final int rightChunk = Math.abs(tail - index) % bufLen; - - if (leftChunk < rightChunk) { - if (index >= head) { - System.arraycopy(buffer, head, buffer, head + 1, leftChunk); - } else { - System.arraycopy(buffer, 0, buffer, 1, index); - buffer[0] = buffer[lastIndex]; - System.arraycopy(buffer, head, buffer, head + 1, lastIndex - head); - } - buffer[head] = ((char) 0); - this.head = oneRight(head, bufLen); - } else { - if (index < tail) { - System.arraycopy(buffer, index + 1, buffer, index, rightChunk); - } else { - System.arraycopy(buffer, index + 1, buffer, index, lastIndex - index); - buffer[lastIndex] = buffer[0]; - System.arraycopy(buffer, 1, buffer, 0, tail); - } - buffer[tail] = ((char) 0); - this.tail = oneLeft(tail, bufLen); - } - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int size() { - if (head <= tail) return tail - head; - else return (tail - head + buffer.length); - } - - /** - * {@inheritDoc} - * - *

The internal array buffers are not released as a result of this call. - * - * @see #release() - */ - @Override - public void clear() { - if (head < tail) { - Arrays.fill(buffer, head, tail, ((char) 0)); - } else { - Arrays.fill(buffer, 0, tail, ((char) 0)); - Arrays.fill(buffer, head, buffer.length, ((char) 0)); - } - this.head = tail = 0; - } - - /** Release internal buffers of this deque and reallocate with the default buffer. */ - public void release() { - this.head = tail = 0; - buffer = CharArrayList.EMPTY_ARRAY; - ensureBufferSpace(0); - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - ensureBufferSpace(expectedElements - size()); - } - - /** - * Ensures the internal buffer has enough free slots to store expectedAdditions. - * Increases internal buffer size if needed. - */ - protected void ensureBufferSpace(int expectedAdditions) { - final int bufferLen = buffer.length; - final int elementsCount = size(); - - if (elementsCount + expectedAdditions >= bufferLen) { - final int emptySlot = 1; // deque invariant: always an empty slot. - final int newSize = resizer.grow(bufferLen, elementsCount + emptySlot, expectedAdditions); - assert newSize >= (elementsCount + expectedAdditions + emptySlot) - : "Resizer failed to" - + " return sensible new size: " - + newSize - + " <= " - + (elementsCount + expectedAdditions); - - try { - final char[] newBuffer = (new char[newSize]); - if (bufferLen > 0) { - toArray(newBuffer); - tail = elementsCount; - head = 0; - } - this.buffer = newBuffer; - } catch (OutOfMemoryError e) { - throw new BufferAllocationException( - "Not enough memory to allocate new buffers: %,d -> %,d", e, bufferLen, newSize); - } - } - } - - /** {@inheritDoc} */ - @Override - public char[] toArray() { - - final int size = size(); - return toArray((new char[size])); - } - - /** - * Copies elements of this deque to an array. The content of the target array is - * filled from index 0 (head of the queue) to index size() - 1 (tail of the queue). - * - * @param target The target array must be large enough to hold all elements. - * @return Returns the target argument for chaining. - */ - public char[] toArray(char[] target) { - assert target.length >= size() : "Target array must be >= " + size(); - - if (head < tail) { - // The contents is not wrapped around. Just copy. - System.arraycopy(buffer, head, target, 0, size()); - } else if (head > tail) { - // The contents is split. Merge elements from the following indexes: - // [head...buffer.length - 1][0, tail - 1] - final int rightCount = buffer.length - head; - System.arraycopy(buffer, head, target, 0, rightCount); - System.arraycopy(buffer, 0, target, rightCount, tail); - } - - return target; - } - - /** - * Clone this object. The returned clone will reuse the same hash function and array resizing - * strategy. - */ - @Override - public CharArrayDeque clone() { - try { - - CharArrayDeque cloned = (CharArrayDeque) super.clone(); - cloned.buffer = buffer.clone(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Move one index to the left, wrapping around buffer. */ - protected static int oneLeft(int index, int modulus) { - if (index >= 1) { - return index - 1; - } - return modulus - 1; - } - - /** Move one index to the right, wrapping around buffer. */ - protected static int oneRight(int index, int modulus) { - if (index + 1 == modulus) { - return 0; - } - return index + 1; - } - - @Override - public long ramBytesAllocated() { - // int: head, tail - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES * 2 - + resizer.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(buffer); - } - - @Override - public long ramBytesUsed() { - // int: head, tail - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES * 2 - + resizer.ramBytesUsed() - + RamUsageEstimator.shallowUsedSizeOfArray(buffer, size()); - } - - /** An iterator implementation for {@link ObjectArrayDeque#iterator}. */ - private final class ValueIterator extends AbstractIterator { - private final CharCursor cursor; - private int remaining; - - public ValueIterator() { - cursor = new CharCursor(); - cursor.index = oneLeft(head, buffer.length); - this.remaining = size(); - } - - @Override - protected CharCursor fetch() { - if (remaining == 0) { - return done(); - } - - remaining--; - cursor.value = buffer[cursor.index = oneRight(cursor.index, buffer.length)]; - return cursor; - } - } - - /** An iterator implementation for {@link ObjectArrayDeque#descendingIterator()}. */ - private final class DescendingValueIterator extends AbstractIterator { - private final CharCursor cursor; - private int remaining; - - public DescendingValueIterator() { - cursor = new CharCursor(); - cursor.index = tail; - this.remaining = size(); - } - - @Override - protected CharCursor fetch() { - if (remaining == 0) return done(); - - remaining--; - cursor.value = buffer[cursor.index = oneLeft(cursor.index, buffer.length)]; - return cursor; - } - } - - /** - * Returns a cursor over the values of this deque (in head to tail order). The iterator is - * implemented as a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()} (to avoid boxing of primitive types). To read the current value (or index in - * the deque's buffer) use the cursor's public fields. An example is shown below. - * - *

-   * for (IntValueCursor c : intDeque) {
-   *   System.out.println("buffer index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator iterator() { - return new ValueIterator(); - } - - /** - * Returns a cursor over the values of this deque (in tail to head order). The iterator is - * implemented as a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()} (to avoid boxing of primitive types). To read the current value (or index in - * the deque's buffer) use the cursor's public fields. An example is shown below. - * - *
-   * for (Iterator<IntCursor> i = intDeque.descendingIterator(); i.hasNext();) {
-   *   final IntCursor c = i.next();
-   *   System.out.println("buffer index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator descendingIterator() { - return new DescendingValueIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - forEach(procedure, head, tail); - return procedure; - } - - /** - * Applies procedure to a slice of the deque, fromIndex, inclusive, to - * toIndex, exclusive. - */ - private void forEach(CharProcedure procedure, int fromIndex, final int toIndex) { - final char[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - procedure.apply(buffer[i]); - } - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - int fromIndex = head; - int toIndex = tail; - - final char[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - if (!predicate.apply(buffer[i])) { - break; - } - } - - return predicate; - } - - /** Applies procedure to all elements of this deque, tail to head. */ - @Override - public T descendingForEach(T procedure) { - descendingForEach(procedure, head, tail); - return procedure; - } - - /** - * Applies procedure to a slice of the deque, toIndex, exclusive, down - * to fromIndex, inclusive. - */ - private void descendingForEach(CharProcedure procedure, int fromIndex, final int toIndex) { - if (fromIndex == toIndex) return; - - final char[] buffer = this.buffer; - int i = toIndex; - do { - i = oneLeft(i, buffer.length); - procedure.apply(buffer[i]); - } while (i != fromIndex); - } - - /** {@inheritDoc} */ - @Override - public T descendingForEach(T predicate) { - descendingForEach(predicate, head, tail); - return predicate; - } - - /** - * Applies predicate to a slice of the deque, toIndex, exclusive, down - * to fromIndex, inclusive or until the predicate returns false. - */ - private void descendingForEach(CharPredicate predicate, int fromIndex, final int toIndex) { - if (fromIndex == toIndex) return; - - final char[] buffer = this.buffer; - int i = toIndex; - do { - i = oneLeft(i, buffer.length); - if (!predicate.apply(buffer[i])) { - break; - } - } while (i != fromIndex); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharPredicate predicate) { - final char[] buffer = this.buffer; - final int last = tail; - final int bufLen = buffer.length; - int removed = 0; - int from, to; - from = to = head; - try { - for (from = to = head; from != last; from = oneRight(from, bufLen)) { - if (predicate.apply(buffer[from])) { - buffer[from] = ((char) 0); - removed++; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = ((char) 0); - } - - to = oneRight(to, bufLen); - } - } finally { - // Keep the deque in consistent state even if the predicate throws an exception. - for (; from != last; from = oneRight(from, bufLen)) { - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = ((char) 0); - } - - to = oneRight(to, bufLen); - } - tail = to; - } - - return removed; - } - - /** {@inheritDoc} */ - @Override - public boolean contains(char e) { - int fromIndex = head; - int toIndex = tail; - - final char[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - if (((e) == (buffer[i]))) { - return true; - } - } - - return false; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = 1; - int fromIndex = head; - int toIndex = tail; - - final char[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - h = 31 * h + BitMixer.mix(this.buffer[i]); - } - return h; - } - - /** - * Returns true only if the other object is an instance of the same class and with - * the same elements. - */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Compare order-aligned elements against another {@link CharDeque}. */ - protected boolean equalElements(CharArrayDeque other) { - int max = size(); - if (other.size() != max) { - return false; - } - - Iterator i1 = this.iterator(); - Iterator i2 = other.iterator(); - - while (i1.hasNext() && i2.hasNext()) { - if (!((i1.next().value) == (i2.next().value))) { - return false; - } - } - - return !i1.hasNext() && !i2.hasNext(); - } - - /** Create a new deque by pushing a variable number of arguments to the end of it. */ - public static CharArrayDeque from(char... elements) { - final CharArrayDeque coll = new CharArrayDeque(elements.length); - coll.addLast(elements); - return coll; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/CharArrayList.java b/src/main/java/com/carrotsearch/hppc/CharArrayList.java deleted file mode 100755 index 90837d31..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharArrayList.java +++ /dev/null @@ -1,579 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.CharPredicate; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** An array-backed list of chars. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeArrayList.java") -public class CharArrayList extends AbstractCharCollection - implements CharIndexedContainer, Preallocable, Cloneable, Accountable { - /** An immutable empty buffer (array). */ - public static final char[] EMPTY_ARRAY = new char[0]; - - ; - - /** Reuse the same strategy instance. */ - private static final BoundedProportionalArraySizingStrategy DEFAULT_SIZING_STRATEGY = - BoundedProportionalArraySizingStrategy.DEFAULT_INSTANCE; - - /** - * Internal array for storing the list. The array may be larger than the current size ({@link - * #size()}). - */ - public char[] buffer = EMPTY_ARRAY; - - /** Current number of elements stored in {@link #buffer}. */ - public int elementsCount; - - /** Buffer resizing strategy. */ - protected final ArraySizingStrategy resizer; - - /** New instance with sane defaults. */ - public CharArrayList() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public CharArrayList(int expectedElements) { - this(expectedElements, DEFAULT_SIZING_STRATEGY); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public CharArrayList(int expectedElements, ArraySizingStrategy resizer) { - assert resizer != null; - this.resizer = resizer; - buffer = Arrays.copyOf(buffer, expectedElements); - } - - /** Creates a new list from the elements of another container in its iteration order. */ - public CharArrayList(CharContainer container) { - this(container.size()); - addAll(container); - } - - /** {@inheritDoc} */ - @Override - public void add(char e1) { - ensureBufferSpace(1); - buffer[elementsCount++] = e1; - } - - /** - * Appends two elements at the end of the list. To add more than two elements, use add - * (vararg-version) or access the buffer directly (tight loop). - */ - public void add(char e1, char e2) { - ensureBufferSpace(2); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - } - - /** Add all elements from a range of given array to the list. */ - public void add(char[] elements, int start, int length) { - assert length >= 0 : "Length must be >= 0"; - - ensureBufferSpace(length); - System.arraycopy(elements, start, buffer, elementsCount, length); - elementsCount += length; - } - - /** - * Vararg-signature method for adding elements at the end of the list. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - */ - public final void add(char... elements) { - add(elements, 0, elements.length); - } - - /** Adds all elements from another container. */ - public int addAll(CharContainer container) { - final int size = container.size(); - ensureBufferSpace(size); - - for (CharCursor cursor : container) { - add(cursor.value); - } - - return size; - } - - /** Adds all elements from another iterable. */ - public int addAll(Iterable iterable) { - int size = 0; - for (CharCursor cursor : iterable) { - add(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public void insert(int index, char e1) { - assert (index >= 0 && index <= size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + "]."; - - ensureBufferSpace(1); - System.arraycopy(buffer, index, buffer, index + 1, elementsCount - index); - buffer[index] = e1; - elementsCount++; - } - - /** {@inheritDoc} */ - @Override - public char get(int index) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - return buffer[index]; - } - - /** {@inheritDoc} */ - @Override - public char set(int index, char e1) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - final char v = buffer[index]; - buffer[index] = e1; - return v; - } - - /** {@inheritDoc} */ - @Override - public char removeAt(int index) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - final char v = buffer[index]; - System.arraycopy(buffer, index + 1, buffer, index, --elementsCount - index); - - return v; - } - - /** {@inheritDoc} */ - @Override - public char removeLast() { - assert elementsCount > 0; - - final char v = buffer[--elementsCount]; - - return v; - } - - /** {@inheritDoc} */ - @Override - public void removeRange(int fromIndex, int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - System.arraycopy(buffer, toIndex, buffer, fromIndex, elementsCount - toIndex); - final int count = toIndex - fromIndex; - elementsCount -= count; - } - - /** {@inheritDoc} */ - @Override - public boolean removeElement(char e1) { - return removeFirst(e1) != -1; - } - - /** {@inheritDoc} */ - @Override - public int removeFirst(char e1) { - final int index = indexOf(e1); - if (index >= 0) removeAt(index); - return index; - } - - /** {@inheritDoc} */ - @Override - public int removeLast(char e1) { - final int index = lastIndexOf(e1); - if (index >= 0) removeAt(index); - return index; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(char e1) { - int to = 0; - for (int from = 0; from < elementsCount; from++) { - if (((e1) == (buffer[from]))) { - continue; - } - if (to != from) { - buffer[to] = buffer[from]; - } - to++; - } - final int deleted = elementsCount - to; - this.elementsCount = to; - - return deleted; - } - - /** {@inheritDoc} */ - @Override - public boolean contains(char e1) { - return indexOf(e1) >= 0; - } - - /** {@inheritDoc} */ - @Override - public int indexOf(char e1) { - for (int i = 0; i < elementsCount; i++) { - if (((e1) == (buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int lastIndexOf(char e1) { - for (int i = elementsCount - 1; i >= 0; i--) { - if (((e1) == (buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return elementsCount == 0; - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - final int bufferLen = (buffer == null ? 0 : buffer.length); - if (expectedElements > bufferLen) { - ensureBufferSpace(expectedElements - size()); - } - } - - /** - * Ensures the internal buffer has enough free slots to store expectedAdditions. - * Increases internal buffer size if needed. - */ - protected void ensureBufferSpace(int expectedAdditions) { - final int bufferLen = (buffer == null ? 0 : buffer.length); - if (elementsCount + expectedAdditions > bufferLen) { - final int newSize = resizer.grow(bufferLen, elementsCount, expectedAdditions); - assert newSize >= elementsCount + expectedAdditions - : "Resizer failed to" - + " return sensible new size: " - + newSize - + " <= " - + (elementsCount + expectedAdditions); - - this.buffer = Arrays.copyOf(buffer, newSize); - } - } - - /** - * Truncate or expand the list to the new size. If the list is truncated, the buffer will not be - * reallocated (use {@link #trimToSize()} if you need a truncated buffer), but the truncated - * values will be reset to the default value (zero). If the list is expanded, the elements beyond - * the current size are initialized with JVM-defaults (zero or null values). - */ - public void resize(int newSize) { - if (newSize <= buffer.length) { - if (newSize < elementsCount) { - Arrays.fill(buffer, newSize, elementsCount, ((char) 0)); - } else { - Arrays.fill(buffer, elementsCount, newSize, ((char) 0)); - } - } else { - ensureCapacity(newSize); - } - this.elementsCount = newSize; - } - - /** {@inheritDoc} */ - @Override - public int size() { - return elementsCount; - } - - /** Trim the internal buffer to the current size. */ - public void trimToSize() { - if (size() != this.buffer.length) { - this.buffer = toArray(); - } - } - - /** - * Sets the number of stored elements to zero. Releases and initializes the internal storage array - * to default values. To clear the list without cleaning the buffer, simply set the {@link - * #elementsCount} field to zero. - */ - @Override - public void clear() { - Arrays.fill(buffer, 0, elementsCount, ((char) 0)); - this.elementsCount = 0; - } - - /** Sets the number of stored elements to zero and releases the internal storage array. */ - @Override - public void release() { - this.buffer = EMPTY_ARRAY; - this.elementsCount = 0; - } - - /** - * {@inheritDoc} - * - *

The returned array is sized to match exactly the number of elements of the stack. - */ - @Override - public char[] toArray() { - - return Arrays.copyOf(buffer, elementsCount); - } - - /** {@inheritDoc} */ - @Override - public CharIndexedContainer sort() { - Arrays.sort(buffer, 0, elementsCount); - return this; - } - - /** {@inheritDoc} */ - @Override - public CharIndexedContainer reverse() { - for (int i = 0, mid = elementsCount >> 1, j = elementsCount - 1; i < mid; i++, j--) { - char tmp = buffer[i]; - buffer[i] = buffer[j]; - buffer[j] = tmp; - } - return this; - } - - /** - * Clone this object. The returned clone will reuse the same hash function and array resizing - * strategy. - */ - @Override - public CharArrayList clone() { - try { - - final CharArrayList cloned = (CharArrayList) super.clone(); - cloned.buffer = buffer.clone(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = 1, max = elementsCount; - for (int i = 0; i < max; i++) { - h = 31 * h + BitMixer.mix(this.buffer[i]); - } - return h; - } - - /** - * Returns true only if the other object is an instance of the same class and with - * the same elements. - */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Compare index-aligned elements against another {@link CharIndexedContainer}. */ - protected boolean equalElements(CharArrayList other) { - int max = size(); - if (other.size() != max) { - return false; - } - - for (int i = 0; i < max; i++) { - if (!((get(i)) == (other.get(i)))) { - return false; - } - } - - return true; - } - - @Override - public long ramBytesAllocated() { - // int: elementsCount - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES - + resizer.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(buffer); - } - - @Override - public long ramBytesUsed() { - // int: elementsCount - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES - + resizer.ramBytesUsed() - + RamUsageEstimator.shallowUsedSizeOfArray(buffer, elementsCount); - } - - /** An iterator implementation for {@link CharArrayList#iterator}. */ - static final class ValueIterator extends AbstractIterator { - private final CharCursor cursor; - - private final char[] buffer; - private final int size; - - public ValueIterator(char[] buffer, int size) { - this.cursor = new CharCursor(); - this.cursor.index = -1; - this.size = size; - this.buffer = buffer; - } - - @Override - protected CharCursor fetch() { - if (cursor.index + 1 == size) return done(); - - cursor.value = buffer[++cursor.index]; - return cursor; - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new ValueIterator(buffer, size()); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - return forEach(procedure, 0, size()); - } - - /** - * Applies procedure to a slice of the list, fromIndex, inclusive, to - * toIndex, exclusive. - */ - public T forEach(T procedure, int fromIndex, final int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - final char[] buffer = this.buffer; - for (int i = fromIndex; i < toIndex; i++) { - procedure.apply(buffer[i]); - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharPredicate predicate) { - final char[] buffer = this.buffer; - final int elementsCount = this.elementsCount; - int to = 0; - int from = 0; - try { - for (; from < elementsCount; from++) { - if (predicate.apply(buffer[from])) { - buffer[from] = ((char) 0); - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = ((char) 0); - } - to++; - } - } finally { - // Keep the list in a consistent state, even if the predicate throws an exception. - for (; from < elementsCount; from++) { - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = ((char) 0); - } - to++; - } - - this.elementsCount = to; - } - - return elementsCount - to; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - return forEach(predicate, 0, size()); - } - - /** - * Applies predicate to a slice of the list, fromIndex, inclusive, to - * toIndex, exclusive, or until predicate returns false. - */ - public T forEach(T predicate, int fromIndex, final int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - final char[] buffer = this.buffer; - for (int i = fromIndex; i < toIndex; i++) { - if (!predicate.apply(buffer[i])) break; - } - - return predicate; - } - - /** - * Create a list from a variable number of arguments or an array of char. The - * elements are copied from the argument to the internal buffer. - */ - public static CharArrayList from(char... elements) { - final CharArrayList list = new CharArrayList(elements.length); - list.add(elements); - return list; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/CharBufferVisualizer.java b/src/main/java/com/carrotsearch/hppc/CharBufferVisualizer.java deleted file mode 100755 index 7bd17436..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharBufferVisualizer.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.carrotsearch.hppc; - -/** - * Reused buffer visualization routines. - * - * @see CharSet#visualizeKeyDistribution(int) - * @see CharVTypeMap#visualizeKeyDistribution(int) - */ -class CharBufferVisualizer { - static String visualizeKeyDistribution(char[] buffer, int max, int characters) { - final StringBuilder b = new StringBuilder(); - final char[] chars = ".123456789X".toCharArray(); - for (int i = 1, start = -1; i <= characters; i++) { - int end = (int) ((long) i * max / characters); - - if (start + 1 <= end) { - int taken = 0; - int slots = 0; - for (int slot = start + 1; slot <= end; slot++, slots++) { - if (!((buffer[slot]) == 0)) { - taken++; - } - } - b.append(chars[Math.min(chars.length - 1, taken * chars.length / slots)]); - start = end; - } - } - while (b.length() < characters) { - b.append(' '); - } - return b.toString(); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/CharByteAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/CharByteAssociativeContainer.java deleted file mode 100755 index a69b4beb..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharByteAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see CharContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface CharByteAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(char key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharBytePredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link CharByteProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link CharBytePredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public CharCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public ByteContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharByteHashMap.java b/src/main/java/com/carrotsearch/hppc/CharByteHashMap.java deleted file mode 100755 index 93dd3c8f..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharByteHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of char to byte, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class CharByteHashMap implements CharByteMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public char[] keys; - - /** The array holding values. */ - public byte[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public CharByteHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public CharByteHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public CharByteHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public CharByteHashMap(CharByteAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public byte put(char key, byte value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - byte previousValue = hasEmptyKey ? values[mask + 1] : ((byte) 0); - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final byte previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return ((byte) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(CharByteAssociativeContainer container) { - final int count = size(); - for (CharByteCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (CharByteCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public byte putOrAdd(char key, byte putValue, byte incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((byte) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public byte addTo(char key, byte incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public byte remove(char key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return ((byte) 0); - } - hasEmptyKey = false; - byte previousValue = values[mask + 1]; - values[mask + 1] = ((byte) 0); - return previousValue; - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final byte previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return ((byte) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof CharLookupContainer) { - if (hasEmptyKey && other.contains(((char) 0))) { - hasEmptyKey = false; - values[mask + 1] = ((byte) 0); - } - - final char[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - char existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (CharCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharBytePredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(((char) 0), values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = ((byte) 0); - } - } - - final char[] keys = this.keys; - final byte[] values = this.values; - for (int slot = 0; slot <= mask; ) { - char existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(((char) 0))) { - hasEmptyKey = false; - values[mask + 1] = ((byte) 0); - } - } - - final char[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - char existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public byte get(char key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : ((byte) 0); - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return ((byte) 0); - } - } - - /** {@inheritDoc} */ - @Override - public byte getOrDefault(char key, byte defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(char key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(char key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public byte indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public byte indexReplace(int index, byte newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - byte previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, char key, byte value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public byte indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - byte previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = ((byte) 0); - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, ((char) 0)); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (CharByteCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(CharByteHashMap other) { - if (other.size() != size()) { - return false; - } - - for (CharByteCursor c : other) { - char key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final char[] prevKeys = this.keys; - final byte[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final CharByteCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new CharByteCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharByteCursor fetch() { - final int mask = CharByteHashMap.this.mask; - while (index <= mask) { - char existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = ((char) 0); - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final char[] keys = this.keys; - final byte[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(((char) 0), values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final char[] keys = this.keys; - final byte[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(((char) 0), values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractCharCollection implements CharLookupContainer { - private final CharByteHashMap owner = CharByteHashMap.this; - - @Override - public boolean contains(char e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((CharByteProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((CharBytePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(CharPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final char e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final CharCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new CharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharCursor fetch() { - final int mask = CharByteHashMap.this.mask; - while (index <= mask) { - char existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = ((char) 0); - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public ByteCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractByteCollection { - private final CharByteHashMap owner = CharByteHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(byte value) { - for (CharByteCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (CharByteCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (CharByteCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final byte e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final BytePredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ByteCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new ByteCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ByteCursor fetch() { - final int mask = CharByteHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public CharByteHashMap clone() { - try { - - CharByteHashMap cloned = (CharByteHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (CharByteCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return CharBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static CharByteHashMap from(char[] keys, byte[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - CharByteHashMap map = new CharByteHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(char key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(char[] fromKeys, byte[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final char[] keys = this.keys; - final byte[] values = this.values; - final int mask = this.mask; - char existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - char[] prevKeys = this.keys; - byte[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new char[arraySize + emptyElementSlot]); - this.values = (new byte[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, char pendingKey, byte pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final char[] prevKeys = this.keys; - final byte[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final char[] keys = this.keys; - final byte[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final char existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = ((char) 0); - values[gapSlot] = ((byte) 0); - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/CharByteMap.java b/src/main/java/com/carrotsearch/hppc/CharByteMap.java deleted file mode 100755 index dcb4ef54..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharByteMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.CharByteCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface CharByteMap extends CharByteAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public byte get(char key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public byte getOrDefault(char key, byte defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public byte put(char key, byte value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(char key, byte value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(CharByteAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public byte putOrAdd(char key, byte putValue, byte incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public byte addTo(char key, byte additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public byte remove(char key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link CharByteMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(char key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public byte indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public byte indexReplace(int index, byte newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, char key, byte value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public byte indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharCharAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/CharCharAssociativeContainer.java deleted file mode 100755 index b7334bbc..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharCharAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see CharContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface CharCharAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(char key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharCharPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link CharCharProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link CharCharPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public CharCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public CharContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharCharHashMap.java b/src/main/java/com/carrotsearch/hppc/CharCharHashMap.java deleted file mode 100755 index 509c1a2c..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharCharHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of char to char, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class CharCharHashMap implements CharCharMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public char[] keys; - - /** The array holding values. */ - public char[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public CharCharHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public CharCharHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public CharCharHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public CharCharHashMap(CharCharAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public char put(char key, char value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - char previousValue = hasEmptyKey ? values[mask + 1] : ((char) 0); - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final char previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return ((char) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(CharCharAssociativeContainer container) { - final int count = size(); - for (CharCharCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (CharCharCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public char putOrAdd(char key, char putValue, char incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((char) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public char addTo(char key, char incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public char remove(char key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return ((char) 0); - } - hasEmptyKey = false; - char previousValue = values[mask + 1]; - values[mask + 1] = ((char) 0); - return previousValue; - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final char previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return ((char) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof CharLookupContainer) { - if (hasEmptyKey && other.contains(((char) 0))) { - hasEmptyKey = false; - values[mask + 1] = ((char) 0); - } - - final char[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - char existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (CharCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharCharPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(((char) 0), values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = ((char) 0); - } - } - - final char[] keys = this.keys; - final char[] values = this.values; - for (int slot = 0; slot <= mask; ) { - char existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(((char) 0))) { - hasEmptyKey = false; - values[mask + 1] = ((char) 0); - } - } - - final char[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - char existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public char get(char key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : ((char) 0); - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return ((char) 0); - } - } - - /** {@inheritDoc} */ - @Override - public char getOrDefault(char key, char defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(char key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(char key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public char indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public char indexReplace(int index, char newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - char previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, char key, char value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public char indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - char previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = ((char) 0); - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, ((char) 0)); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (CharCharCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(CharCharHashMap other) { - if (other.size() != size()) { - return false; - } - - for (CharCharCursor c : other) { - char key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final char[] prevKeys = this.keys; - final char[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final CharCharCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new CharCharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharCharCursor fetch() { - final int mask = CharCharHashMap.this.mask; - while (index <= mask) { - char existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = ((char) 0); - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final char[] keys = this.keys; - final char[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(((char) 0), values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final char[] keys = this.keys; - final char[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(((char) 0), values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractCharCollection implements CharLookupContainer { - private final CharCharHashMap owner = CharCharHashMap.this; - - @Override - public boolean contains(char e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((CharCharProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((CharCharPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(CharPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final char e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final CharCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new CharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharCursor fetch() { - final int mask = CharCharHashMap.this.mask; - while (index <= mask) { - char existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = ((char) 0); - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public CharCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractCharCollection { - private final CharCharHashMap owner = CharCharHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(char value) { - for (CharCharCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (CharCharCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (CharCharCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final char e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final CharPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final CharCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new CharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharCursor fetch() { - final int mask = CharCharHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public CharCharHashMap clone() { - try { - - CharCharHashMap cloned = (CharCharHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (CharCharCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return CharBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static CharCharHashMap from(char[] keys, char[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - CharCharHashMap map = new CharCharHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(char key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(char[] fromKeys, char[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final char[] keys = this.keys; - final char[] values = this.values; - final int mask = this.mask; - char existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - char[] prevKeys = this.keys; - char[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new char[arraySize + emptyElementSlot]); - this.values = (new char[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, char pendingKey, char pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final char[] prevKeys = this.keys; - final char[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final char[] keys = this.keys; - final char[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final char existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = ((char) 0); - values[gapSlot] = ((char) 0); - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/CharCharMap.java b/src/main/java/com/carrotsearch/hppc/CharCharMap.java deleted file mode 100755 index c3690bba..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharCharMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.CharCharCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface CharCharMap extends CharCharAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public char get(char key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public char getOrDefault(char key, char defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public char put(char key, char value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(char key, char value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(CharCharAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public char putOrAdd(char key, char putValue, char incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public char addTo(char key, char additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public char remove(char key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link CharCharMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(char key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public char indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public char indexReplace(int index, char newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, char key, char value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public char indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharCollection.java b/src/main/java/com/carrotsearch/hppc/CharCollection.java deleted file mode 100755 index 4cc8517b..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharCollection.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.predicates.CharPredicate; - -/** - * A collection allows basic, efficient operations on sets of elements (difference and - * intersection). - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeCollection.java") -public interface CharCollection extends CharContainer { - /** - * Removes all occurrences of e from this collection. - * - * @param e Element to be removed from this collection, if present. - * @return The number of removed elements as a result of this call. - */ - public int removeAll(char e); - - /** - * Removes all elements in this collection that are present in c. - * - * @return Returns the number of removed elements. - */ - public int removeAll(CharLookupContainer c); - - /** - * Removes all elements in this collection for which the given predicate returns true - * . - * - * @return Returns the number of removed elements. - */ - public int removeAll(CharPredicate predicate); - - /** - * Keeps all elements in this collection that are present in c. Runs in time - * proportional to the number of elements in this collection. Equivalent of sets intersection. - * - * @return Returns the number of removed elements. - */ - public int retainAll(CharLookupContainer c); - - /** - * Keeps all elements in this collection for which the given predicate returns true. - * - * @return Returns the number of removed elements. - */ - public int retainAll(CharPredicate predicate); - - /** - * Removes all elements from this collection. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharContainer.java b/src/main/java/com/carrotsearch/hppc/CharContainer.java deleted file mode 100755 index 5977583b..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharContainer.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.CharCursor; -import com.carrotsearch.hppc.predicates.CharPredicate; -import com.carrotsearch.hppc.procedures.CharProcedure; -import java.util.Iterator; - -/** A generic container holding chars. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeContainer.java") -public interface CharContainer extends Iterable { - /** - * Returns an iterator to a cursor traversing the collection. The order of traversal is not - * defined. More than one cursor may be active at a time. The behavior of iterators is undefined - * if structural changes are made to the underlying collection. - * - *

The iterator is implemented as a cursor and it returns the same cursor instance on - * every call to {@link Iterator#next()} (to avoid boxing of primitive types). To read the current - * list's value (or index in the list) use the cursor's public fields. An example is shown below. - * - *

-   * for (CharCursor<char> c : container) {
-   *   System.out.println("index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator iterator(); - - /** - * Lookup a given element in the container. This operation has no speed guarantees (may be linear - * with respect to the size of this container). - * - * @return Returns true if this container has an element equal to e. - */ - public boolean contains(char e); - - /** - * Return the current number of elements in this container. The time for calculating the - * container's size may take O(n) time, although implementing classes should try to - * maintain the current size and return in constant time. - */ - public int size(); - - /** Shortcut for size() == 0. */ - public boolean isEmpty(); - - /** - * Copies all elements of this container to an array. - * - *

The returned array is always a copy, regardless of the storage used by the container. - */ - public char[] toArray(); - - /** - * Applies a procedure to all container elements. Returns the argument (any subclass - * of {@link CharProcedure}. This lets the caller to call methods of the argument by chaining the - * call (even if the argument is an anonymous type) to retrieve computed values, for example - * (IntContainer): - * - *

-   * int count = container.forEach(new IntProcedure() {
-   *   int count; // this is a field declaration in an anonymous class.
-   *
-   *   public void apply(int value) {
-   *     count++;
-   *   }
-   * }).count;
-   * 
- */ - public T forEach(T procedure); - - /** - * Applies a predicate to container elements as long, as the predicate returns - * true. The iteration is interrupted otherwise. - */ - public T forEach(T predicate); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharDeque.java b/src/main/java/com/carrotsearch/hppc/CharDeque.java deleted file mode 100755 index 34ff5c92..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharDeque.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.CharCursor; -import com.carrotsearch.hppc.predicates.CharPredicate; -import com.carrotsearch.hppc.procedures.CharProcedure; -import java.util.Deque; -import java.util.Iterator; - -/** - * A linear collection that supports element insertion and removal at both ends. - * - * @see Deque - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeDeque.java") -public interface CharDeque extends CharCollection { - /** - * Removes the first element that equals e. - * - * @return The deleted element's index or -1 if the element was not found. - */ - public int removeFirst(char e); - - /** - * Removes the last element that equals e. - * - * @return The deleted element's index or -1 if the element was not found. - */ - public int removeLast(char e); - - /** Inserts the specified element at the front of this deque. */ - public void addFirst(char e); - - /** Inserts the specified element at the end of this deque. */ - public void addLast(char e); - - /** - * Retrieves and removes the first element of this deque. - * - * @return the head (first) element of this deque. - */ - public char removeFirst(); - - /** - * Retrieves and removes the last element of this deque. - * - * @return the tail of this deque. - */ - public char removeLast(); - - /** - * Retrieves the first element of this deque but does not remove it. - * - * @return the head of this deque. - */ - public char getFirst(); - - /** - * Retrieves the last element of this deque but does not remove it. - * - * @return the head of this deque. - */ - public char getLast(); - - /** - * @return An iterator over elements in this deque in tail-to-head order. - */ - public Iterator descendingIterator(); - - /** Applies a procedure to all elements in tail-to-head order. */ - public T descendingForEach(T procedure); - - /** - * Applies a predicate to container elements as long, as the predicate returns - * true. The iteration is interrupted otherwise. - */ - public T descendingForEach(T predicate); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharDoubleAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/CharDoubleAssociativeContainer.java deleted file mode 100755 index 2d898f2c..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharDoubleAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see CharContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface CharDoubleAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *
-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(char key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharDoublePredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link CharDoubleProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link CharDoublePredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public CharCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public DoubleContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharDoubleHashMap.java b/src/main/java/com/carrotsearch/hppc/CharDoubleHashMap.java deleted file mode 100755 index 0a68bfaf..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharDoubleHashMap.java +++ /dev/null @@ -1,1082 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of char to double, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class CharDoubleHashMap implements CharDoubleMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public char[] keys; - - /** The array holding values. */ - public double[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public CharDoubleHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public CharDoubleHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public CharDoubleHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public CharDoubleHashMap(CharDoubleAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public double put(char key, double value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - double previousValue = hasEmptyKey ? values[mask + 1] : 0d; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final double previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0d; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(CharDoubleAssociativeContainer container) { - final int count = size(); - for (CharDoubleCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (CharDoubleCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public double putOrAdd(char key, double putValue, double incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((double) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public double addTo(char key, double incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public double remove(char key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return 0d; - } - hasEmptyKey = false; - double previousValue = values[mask + 1]; - values[mask + 1] = 0d; - return previousValue; - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final double previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0d; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof CharLookupContainer) { - if (hasEmptyKey && other.contains(((char) 0))) { - hasEmptyKey = false; - values[mask + 1] = 0d; - } - - final char[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - char existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (CharCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharDoublePredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(((char) 0), values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0d; - } - } - - final char[] keys = this.keys; - final double[] values = this.values; - for (int slot = 0; slot <= mask; ) { - char existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(((char) 0))) { - hasEmptyKey = false; - values[mask + 1] = 0d; - } - } - - final char[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - char existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public double get(char key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : 0d; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0d; - } - } - - /** {@inheritDoc} */ - @Override - public double getOrDefault(char key, double defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(char key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(char key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public double indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public double indexReplace(int index, double newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - double previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, char key, double value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public double indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - double previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0d; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, ((char) 0)); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (CharDoubleCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(CharDoubleHashMap other) { - if (other.size() != size()) { - return false; - } - - for (CharDoubleCursor c : other) { - char key = c.key; - if (!containsKey(key) - || !(Double.doubleToLongBits(c.value) == Double.doubleToLongBits(get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final char[] prevKeys = this.keys; - final double[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final CharDoubleCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new CharDoubleCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharDoubleCursor fetch() { - final int mask = CharDoubleHashMap.this.mask; - while (index <= mask) { - char existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = ((char) 0); - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final char[] keys = this.keys; - final double[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(((char) 0), values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final char[] keys = this.keys; - final double[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(((char) 0), values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractCharCollection implements CharLookupContainer { - private final CharDoubleHashMap owner = CharDoubleHashMap.this; - - @Override - public boolean contains(char e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((CharDoubleProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((CharDoublePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(CharPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final char e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final CharCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new CharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharCursor fetch() { - final int mask = CharDoubleHashMap.this.mask; - while (index <= mask) { - char existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = ((char) 0); - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public DoubleCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractDoubleCollection { - private final CharDoubleHashMap owner = CharDoubleHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(double value) { - for (CharDoubleCursor c : owner) { - if ((Double.doubleToLongBits(value) == Double.doubleToLongBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (CharDoubleCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (CharDoubleCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final double e) { - return owner.removeAll( - (key, value) -> (Double.doubleToLongBits(e) == Double.doubleToLongBits(value))); - } - - @Override - public int removeAll(final DoublePredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final DoubleCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new DoubleCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected DoubleCursor fetch() { - final int mask = CharDoubleHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public CharDoubleHashMap clone() { - try { - - CharDoubleHashMap cloned = (CharDoubleHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (CharDoubleCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return CharBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static CharDoubleHashMap from(char[] keys, double[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - CharDoubleHashMap map = new CharDoubleHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(char key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(char[] fromKeys, double[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final char[] keys = this.keys; - final double[] values = this.values; - final int mask = this.mask; - char existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - char[] prevKeys = this.keys; - double[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new char[arraySize + emptyElementSlot]); - this.values = (new double[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, char pendingKey, double pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final char[] prevKeys = this.keys; - final double[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final char[] keys = this.keys; - final double[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final char existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = ((char) 0); - values[gapSlot] = 0d; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/CharDoubleMap.java b/src/main/java/com/carrotsearch/hppc/CharDoubleMap.java deleted file mode 100755 index 2ba6e41b..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharDoubleMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.CharDoubleCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface CharDoubleMap extends CharDoubleAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public double get(char key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public double getOrDefault(char key, double defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public double put(char key, double value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(char key, double value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(CharDoubleAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public double putOrAdd(char key, double putValue, double incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public double addTo(char key, double additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public double remove(char key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link CharDoubleMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(char key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public double indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public double indexReplace(int index, double newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, char key, double value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public double indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharFloatAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/CharFloatAssociativeContainer.java deleted file mode 100755 index 10069a39..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharFloatAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see CharContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface CharFloatAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(char key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharFloatPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link CharFloatProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link CharFloatPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public CharCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public FloatContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharFloatHashMap.java b/src/main/java/com/carrotsearch/hppc/CharFloatHashMap.java deleted file mode 100755 index 0f4d6d8f..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharFloatHashMap.java +++ /dev/null @@ -1,1081 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of char to float, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class CharFloatHashMap implements CharFloatMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public char[] keys; - - /** The array holding values. */ - public float[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public CharFloatHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public CharFloatHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public CharFloatHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public CharFloatHashMap(CharFloatAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public float put(char key, float value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - float previousValue = hasEmptyKey ? values[mask + 1] : 0f; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final float previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0f; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(CharFloatAssociativeContainer container) { - final int count = size(); - for (CharFloatCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (CharFloatCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public float putOrAdd(char key, float putValue, float incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((float) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public float addTo(char key, float incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public float remove(char key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return 0f; - } - hasEmptyKey = false; - float previousValue = values[mask + 1]; - values[mask + 1] = 0f; - return previousValue; - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final float previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0f; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof CharLookupContainer) { - if (hasEmptyKey && other.contains(((char) 0))) { - hasEmptyKey = false; - values[mask + 1] = 0f; - } - - final char[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - char existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (CharCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharFloatPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(((char) 0), values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0f; - } - } - - final char[] keys = this.keys; - final float[] values = this.values; - for (int slot = 0; slot <= mask; ) { - char existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(((char) 0))) { - hasEmptyKey = false; - values[mask + 1] = 0f; - } - } - - final char[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - char existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public float get(char key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : 0f; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0f; - } - } - - /** {@inheritDoc} */ - @Override - public float getOrDefault(char key, float defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(char key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(char key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public float indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public float indexReplace(int index, float newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - float previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, char key, float value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public float indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - float previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0f; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, ((char) 0)); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (CharFloatCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(CharFloatHashMap other) { - if (other.size() != size()) { - return false; - } - - for (CharFloatCursor c : other) { - char key = c.key; - if (!containsKey(key) || !(Float.floatToIntBits(c.value) == Float.floatToIntBits(get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final char[] prevKeys = this.keys; - final float[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final CharFloatCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new CharFloatCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharFloatCursor fetch() { - final int mask = CharFloatHashMap.this.mask; - while (index <= mask) { - char existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = ((char) 0); - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final char[] keys = this.keys; - final float[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(((char) 0), values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final char[] keys = this.keys; - final float[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(((char) 0), values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractCharCollection implements CharLookupContainer { - private final CharFloatHashMap owner = CharFloatHashMap.this; - - @Override - public boolean contains(char e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((CharFloatProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((CharFloatPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(CharPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final char e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final CharCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new CharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharCursor fetch() { - final int mask = CharFloatHashMap.this.mask; - while (index <= mask) { - char existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = ((char) 0); - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public FloatCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractFloatCollection { - private final CharFloatHashMap owner = CharFloatHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(float value) { - for (CharFloatCursor c : owner) { - if ((Float.floatToIntBits(value) == Float.floatToIntBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (CharFloatCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (CharFloatCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final float e) { - return owner.removeAll( - (key, value) -> (Float.floatToIntBits(e) == Float.floatToIntBits(value))); - } - - @Override - public int removeAll(final FloatPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final FloatCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new FloatCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected FloatCursor fetch() { - final int mask = CharFloatHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public CharFloatHashMap clone() { - try { - - CharFloatHashMap cloned = (CharFloatHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (CharFloatCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return CharBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static CharFloatHashMap from(char[] keys, float[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - CharFloatHashMap map = new CharFloatHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(char key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(char[] fromKeys, float[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final char[] keys = this.keys; - final float[] values = this.values; - final int mask = this.mask; - char existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - char[] prevKeys = this.keys; - float[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new char[arraySize + emptyElementSlot]); - this.values = (new float[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, char pendingKey, float pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final char[] prevKeys = this.keys; - final float[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final char[] keys = this.keys; - final float[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final char existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = ((char) 0); - values[gapSlot] = 0f; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/CharFloatMap.java b/src/main/java/com/carrotsearch/hppc/CharFloatMap.java deleted file mode 100755 index 4bf7569f..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharFloatMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.CharFloatCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface CharFloatMap extends CharFloatAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public float get(char key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public float getOrDefault(char key, float defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public float put(char key, float value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(char key, float value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(CharFloatAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public float putOrAdd(char key, float putValue, float incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public float addTo(char key, float additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public float remove(char key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link CharFloatMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(char key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public float indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public float indexReplace(int index, float newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, char key, float value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public float indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharHashSet.java b/src/main/java/com/carrotsearch/hppc/CharHashSet.java deleted file mode 100755 index 58ad8f26..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharHashSet.java +++ /dev/null @@ -1,787 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash set of chars, implemented using open addressing with linear probing for - * collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypeHashSet.java") -public class CharHashSet extends AbstractCharCollection - implements CharLookupContainer, CharSet, Preallocable, Cloneable, Accountable { - /** The hash array holding keys. */ - public char[] keys; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any. - * - * @see #size() - * @see #hasEmptyKey - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** - * New instance with sane defaults. - * - * @see #CharHashSet(int, double) - */ - public CharHashSet() { - this(DEFAULT_EXPECTED_ELEMENTS, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with sane defaults. - * - * @see #CharHashSet(int, double) - */ - public CharHashSet(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public CharHashSet(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** New instance copying elements from another {@link CharContainer}. */ - public CharHashSet(CharContainer container) { - this(container.size()); - addAll(container); - } - - /** {@inheritDoc} */ - @Override - public boolean add(char key) { - if (((key) == 0)) { - assert ((keys[mask + 1]) == 0); - boolean added = !hasEmptyKey; - hasEmptyKey = true; - return added; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return false; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key); - } else { - keys[slot] = key; - } - - assigned++; - return true; - } - } - - /** - * Adds all elements from the given list (vararg) to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - */ - public final int addAll(char... elements) { - ensureCapacity(elements.length); - int count = 0; - for (char e : elements) { - if (add(e)) { - count++; - } - } - return count; - } - - /** - * Adds all elements from the given {@link CharContainer} to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - */ - public int addAll(CharContainer container) { - ensureCapacity(container.size()); - return addAll((Iterable) container); - } - - /** - * Adds all elements from the given iterable to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - */ - public int addAll(Iterable iterable) { - int count = 0; - for (CharCursor cursor : iterable) { - if (add(cursor.value)) { - count++; - } - } - return count; - } - - /** {@inheritDoc} */ - @Override - public char[] toArray() { - - final char[] cloned = (new char[size()]); - int j = 0; - if (hasEmptyKey) { - cloned[j++] = ((char) 0); - } - - final char[] keys = this.keys; - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - char existing; - if (!((existing = keys[slot]) == 0)) { - cloned[j++] = existing; - } - } - - return cloned; - } - - /** An alias for the (preferred) {@link #removeAll}. */ - public boolean remove(char key) { - if (((key) == 0)) { - boolean hadEmptyKey = hasEmptyKey; - hasEmptyKey = false; - return hadEmptyKey; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - shiftConflictingKeys(slot); - return true; - } - slot = (slot + 1) & mask; - } - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(char key) { - return remove(key) ? 1 : 0; - } - - /** - * Removes all keys present in a given container. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharContainer other) { - final int before = size(); - - // Try to iterate over the smaller set or over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof CharLookupContainer) { - if (hasEmptyKey && other.contains(((char) 0))) { - hasEmptyKey = false; - } - - final char[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - char existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (CharCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharPredicate predicate) { - int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(((char) 0))) { - hasEmptyKey = false; - } - } - - final char[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - char existing; - if (!((existing = keys[slot]) == 0)) { - if (predicate.apply(existing)) { - shiftConflictingKeys(slot); - continue; // Repeat the check for the same slot i (shifted). - } - } - slot++; - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public boolean contains(char key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - return false; - } - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - Arrays.fill(keys, ((char) 0)); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - keys = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return size() == 0; - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final char[] prevKeys = this.keys; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys); - } - } - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - final char[] keys = this.keys; - for (int slot = mask; slot >= 0; slot--) { - char existing; - if (!((existing = keys[slot]) == 0)) { - h += BitMixer.mix(existing); - } - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && sameKeys(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - private boolean sameKeys(CharSet other) { - if (other.size() != size()) { - return false; - } - - for (CharCursor c : other) { - if (!contains(c.value)) { - return false; - } - } - - return true; - } - - /** {@inheritDoc} */ - @Override - public CharHashSet clone() { - try { - - CharHashSet cloned = (CharHashSet) super.clone(); - cloned.keys = keys.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - @Override - public long ramBytesAllocated() { - // int: assigned, mask, keyMixer, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys); - } - - @Override - public long ramBytesUsed() { - // int: assigned, mask, keyMixer, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - protected final class EntryIterator extends AbstractIterator { - private final CharCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new CharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharCursor fetch() { - final int mask = CharHashSet.this.mask; - while (index <= mask) { - char existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = ((char) 0); - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - if (hasEmptyKey) { - procedure.apply(((char) 0)); - } - - final char[] keys = this.keys; - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - char existing; - if (!((existing = keys[slot]) == 0)) { - procedure.apply(existing); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - if (hasEmptyKey) { - if (!predicate.apply(((char) 0))) { - return predicate; - } - } - - final char[] keys = this.keys; - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - char existing; - if (!((existing = keys[slot]) == 0)) { - if (!predicate.apply(existing)) { - break; - } - } - } - - return predicate; - } - - /** - * Create a set from a variable number of arguments or an array of char. The elements - * are copied from the argument to the internal buffer. - */ - public static CharHashSet from(char... elements) { - final CharHashSet set = new CharHashSet(elements.length); - set.addAll(elements); - return set; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(char key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up logic in - * certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between modifications (it will not be affected by read-only - * operations). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the set. - * @return A non-negative value of the logical "index" of the key in the set or a negative value - * if the key did not exist. - */ - public int indexOf(char key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index) { - assert index < 0 || index <= mask || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** - * Returns the exact value of the existing key. This method makes sense for sets of objects which - * define custom key-equality relationship. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the equivalent key currently stored in the set. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public char indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return keys[index]; - } - - /** - * Replaces the existing equivalent key with the given one and returns any previous value stored - * for that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @param equivalentKey The key to put in the set as a replacement. Must be equivalent to the key - * currently stored at the provided index. - * @return Returns the previous key stored in the set. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public char indexReplace(int index, char equivalentKey) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - assert ((keys[index]) == (equivalentKey)); - - char previousValue = keys[index]; - keys[index] = equivalentKey; - return previousValue; - } - - /** - * Inserts a key for an index that is not present in the set. This method may help in avoiding - * double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public void indexInsert(int index, char key) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - assert ((keys[index]) == 0); - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key); - } else { - keys[index] = key; - } - - assigned++; - } - } - - /** - * Removes a key at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public void indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - if (index > mask) { - hasEmptyKey = false; - } else { - shiftConflictingKeys(index); - } - } - - @Override - public String visualizeKeyDistribution(int characters) { - return CharBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(char[] fromKeys) { - assert HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored keys into the new buffers. - final char[] keys = this.keys; - final int mask = this.mask; - char existing; - for (int i = fromKeys.length - 1; --i >= 0; ) { - if (!((existing = fromKeys[i]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - char[] prevKeys = this.keys; - try { - int emptyElementSlot = 1; - this.keys = (new char[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.keys == null ? 0 : size(), arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key to be inserted into the buffer but there is not - * enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, char pendingKey) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final char[] prevKeys = this.keys; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - - // Rehash old keys, including the pending key. - rehash(prevKeys); - } - - /** Shift all the slot-conflicting keys allocated to (and including) slot. */ - protected void shiftConflictingKeys(int gapSlot) { - final char[] keys = this.keys; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final char existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = ((char) 0); - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/CharIndexedContainer.java b/src/main/java/com/carrotsearch/hppc/CharIndexedContainer.java deleted file mode 100755 index 96824521..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharIndexedContainer.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.carrotsearch.hppc; - -import java.util.RandomAccess; - -/** - * An indexed container provides random access to elements based on an index. Indexes - * are zero-based. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeIndexedContainer.java") -public interface CharIndexedContainer extends CharCollection, RandomAccess { - /** - * Removes the first element that equals e1, returning whether an element has been - * removed. - */ - public boolean removeElement(char e1); - - /** - * Removes the first element that equals e1, returning its deleted position or - * -1 if the element was not found. - */ - public int removeFirst(char e1); - - /** - * Removes the last element that equals e1, returning its deleted position or - * -1 if the element was not found. - */ - public int removeLast(char e1); - - /** - * Returns the index of the first occurrence of the specified element in this list, or -1 if this - * list does not contain the element. - */ - public int indexOf(char e1); - - /** - * Returns the index of the last occurrence of the specified element in this list, or -1 if this - * list does not contain the element. - */ - public int lastIndexOf(char e1); - - /** Adds an element to the end of this container (the last index is incremented by one). */ - public void add(char e1); - - /** - * Inserts the specified element at the specified position in this list. - * - * @param index The index at which the element should be inserted, shifting any existing and - * subsequent elements to the right. - */ - public void insert(int index, char e1); - - /** - * Replaces the element at the specified position in this list with the specified element. - * - * @return Returns the previous value in the list. - */ - public char set(int index, char e1); - - /** - * @return Returns the element at index index from the list. - */ - public char get(int index); - - /** - * Removes the element at the specified position in this container and returns it. - * - * @see #removeFirst - * @see #removeLast - * @see #removeAll - */ - public char removeAt(int index); - - /** Removes and returns the last element of this container. This container must not be empty. */ - public char removeLast(); - - /** - * Removes from this container all of the elements with indexes between fromIndex, - * inclusive, and toIndex, exclusive. - */ - public void removeRange(int fromIndex, int toIndex); - - /** Returns this container elements as a stream. */ - - /** Sorts the elements in this container and returns this container. */ - public CharIndexedContainer sort(); - - /** Reverses the elements in this container and returns this container. */ - public CharIndexedContainer reverse(); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharIntAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/CharIntAssociativeContainer.java deleted file mode 100755 index 08c5d64a..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharIntAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see CharContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface CharIntAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(char key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharIntPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link CharIntProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link CharIntPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public CharCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public IntContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharIntHashMap.java b/src/main/java/com/carrotsearch/hppc/CharIntHashMap.java deleted file mode 100755 index aff7e50d..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharIntHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of char to int, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class CharIntHashMap implements CharIntMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public char[] keys; - - /** The array holding values. */ - public int[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public CharIntHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public CharIntHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public CharIntHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public CharIntHashMap(CharIntAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public int put(char key, int value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - int previousValue = hasEmptyKey ? values[mask + 1] : 0; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final int previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(CharIntAssociativeContainer container) { - final int count = size(); - for (CharIntCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (CharIntCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public int putOrAdd(char key, int putValue, int incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((int) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public int addTo(char key, int incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public int remove(char key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return 0; - } - hasEmptyKey = false; - int previousValue = values[mask + 1]; - values[mask + 1] = 0; - return previousValue; - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final int previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof CharLookupContainer) { - if (hasEmptyKey && other.contains(((char) 0))) { - hasEmptyKey = false; - values[mask + 1] = 0; - } - - final char[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - char existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (CharCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharIntPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(((char) 0), values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0; - } - } - - final char[] keys = this.keys; - final int[] values = this.values; - for (int slot = 0; slot <= mask; ) { - char existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(((char) 0))) { - hasEmptyKey = false; - values[mask + 1] = 0; - } - } - - final char[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - char existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int get(char key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : 0; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0; - } - } - - /** {@inheritDoc} */ - @Override - public int getOrDefault(char key, int defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(char key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(char key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public int indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public int indexReplace(int index, int newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - int previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, char key, int value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public int indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - int previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, ((char) 0)); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (CharIntCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(CharIntHashMap other) { - if (other.size() != size()) { - return false; - } - - for (CharIntCursor c : other) { - char key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final char[] prevKeys = this.keys; - final int[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final CharIntCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new CharIntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharIntCursor fetch() { - final int mask = CharIntHashMap.this.mask; - while (index <= mask) { - char existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = ((char) 0); - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final char[] keys = this.keys; - final int[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(((char) 0), values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final char[] keys = this.keys; - final int[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(((char) 0), values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractCharCollection implements CharLookupContainer { - private final CharIntHashMap owner = CharIntHashMap.this; - - @Override - public boolean contains(char e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((CharIntProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((CharIntPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(CharPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final char e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final CharCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new CharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharCursor fetch() { - final int mask = CharIntHashMap.this.mask; - while (index <= mask) { - char existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = ((char) 0); - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public IntCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractIntCollection { - private final CharIntHashMap owner = CharIntHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(int value) { - for (CharIntCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (CharIntCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (CharIntCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final int e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final IntPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final IntCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new IntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntCursor fetch() { - final int mask = CharIntHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public CharIntHashMap clone() { - try { - - CharIntHashMap cloned = (CharIntHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (CharIntCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return CharBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static CharIntHashMap from(char[] keys, int[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - CharIntHashMap map = new CharIntHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(char key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(char[] fromKeys, int[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final char[] keys = this.keys; - final int[] values = this.values; - final int mask = this.mask; - char existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - char[] prevKeys = this.keys; - int[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new char[arraySize + emptyElementSlot]); - this.values = (new int[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, char pendingKey, int pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final char[] prevKeys = this.keys; - final int[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final char[] keys = this.keys; - final int[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final char existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = ((char) 0); - values[gapSlot] = 0; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/CharIntMap.java b/src/main/java/com/carrotsearch/hppc/CharIntMap.java deleted file mode 100755 index 8c2f24e0..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharIntMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.CharIntCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface CharIntMap extends CharIntAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public int get(char key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public int getOrDefault(char key, int defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public int put(char key, int value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(char key, int value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(CharIntAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public int putOrAdd(char key, int putValue, int incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public int addTo(char key, int additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public int remove(char key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link CharIntMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(char key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public int indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public int indexReplace(int index, int newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, char key, int value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public int indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharLongAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/CharLongAssociativeContainer.java deleted file mode 100755 index 7999c340..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharLongAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see CharContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface CharLongAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(char key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharLongPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link CharLongProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link CharLongPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public CharCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public LongContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharLongHashMap.java b/src/main/java/com/carrotsearch/hppc/CharLongHashMap.java deleted file mode 100755 index e1bea344..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharLongHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of char to long, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class CharLongHashMap implements CharLongMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public char[] keys; - - /** The array holding values. */ - public long[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public CharLongHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public CharLongHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public CharLongHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public CharLongHashMap(CharLongAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public long put(char key, long value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - long previousValue = hasEmptyKey ? values[mask + 1] : 0L; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final long previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0L; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(CharLongAssociativeContainer container) { - final int count = size(); - for (CharLongCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (CharLongCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public long putOrAdd(char key, long putValue, long incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((long) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public long addTo(char key, long incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public long remove(char key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return 0L; - } - hasEmptyKey = false; - long previousValue = values[mask + 1]; - values[mask + 1] = 0L; - return previousValue; - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final long previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0L; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof CharLookupContainer) { - if (hasEmptyKey && other.contains(((char) 0))) { - hasEmptyKey = false; - values[mask + 1] = 0L; - } - - final char[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - char existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (CharCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharLongPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(((char) 0), values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0L; - } - } - - final char[] keys = this.keys; - final long[] values = this.values; - for (int slot = 0; slot <= mask; ) { - char existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(((char) 0))) { - hasEmptyKey = false; - values[mask + 1] = 0L; - } - } - - final char[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - char existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public long get(char key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : 0L; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0L; - } - } - - /** {@inheritDoc} */ - @Override - public long getOrDefault(char key, long defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(char key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(char key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public long indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public long indexReplace(int index, long newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - long previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, char key, long value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public long indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - long previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0L; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, ((char) 0)); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (CharLongCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(CharLongHashMap other) { - if (other.size() != size()) { - return false; - } - - for (CharLongCursor c : other) { - char key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final char[] prevKeys = this.keys; - final long[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final CharLongCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new CharLongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharLongCursor fetch() { - final int mask = CharLongHashMap.this.mask; - while (index <= mask) { - char existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = ((char) 0); - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final char[] keys = this.keys; - final long[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(((char) 0), values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final char[] keys = this.keys; - final long[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(((char) 0), values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractCharCollection implements CharLookupContainer { - private final CharLongHashMap owner = CharLongHashMap.this; - - @Override - public boolean contains(char e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((CharLongProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((CharLongPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(CharPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final char e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final CharCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new CharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharCursor fetch() { - final int mask = CharLongHashMap.this.mask; - while (index <= mask) { - char existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = ((char) 0); - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public LongCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractLongCollection { - private final CharLongHashMap owner = CharLongHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(long value) { - for (CharLongCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (CharLongCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (CharLongCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final long e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final LongPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final LongCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new LongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongCursor fetch() { - final int mask = CharLongHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public CharLongHashMap clone() { - try { - - CharLongHashMap cloned = (CharLongHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (CharLongCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return CharBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static CharLongHashMap from(char[] keys, long[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - CharLongHashMap map = new CharLongHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(char key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(char[] fromKeys, long[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final char[] keys = this.keys; - final long[] values = this.values; - final int mask = this.mask; - char existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - char[] prevKeys = this.keys; - long[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new char[arraySize + emptyElementSlot]); - this.values = (new long[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, char pendingKey, long pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final char[] prevKeys = this.keys; - final long[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final char[] keys = this.keys; - final long[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final char existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = ((char) 0); - values[gapSlot] = 0L; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/CharLongMap.java b/src/main/java/com/carrotsearch/hppc/CharLongMap.java deleted file mode 100755 index fcbda2b4..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharLongMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.CharLongCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface CharLongMap extends CharLongAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public long get(char key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public long getOrDefault(char key, long defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public long put(char key, long value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(char key, long value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(CharLongAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public long putOrAdd(char key, long putValue, long incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public long addTo(char key, long additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public long remove(char key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link CharLongMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(char key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public long indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public long indexReplace(int index, long newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, char key, long value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public long indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharLookupContainer.java b/src/main/java/com/carrotsearch/hppc/CharLookupContainer.java deleted file mode 100755 index 3e2fd0a4..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharLookupContainer.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.carrotsearch.hppc; - -/** - * Marker interface for containers that can check if they contain a given object in at least time - * O(log n) and ideally in amortized constant time O(1). - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeLookupContainer.java") -public interface CharLookupContainer extends CharContainer { - public boolean contains(char e); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharObjectAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/CharObjectAssociativeContainer.java deleted file mode 100755 index 806efa68..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharObjectAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see CharContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface CharObjectAssociativeContainer extends Iterable> { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator> iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(char key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharObjectPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link CharObjectProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public > T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link CharObjectPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public > T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public CharCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public ObjectContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharObjectHashMap.java b/src/main/java/com/carrotsearch/hppc/CharObjectHashMap.java deleted file mode 100755 index a9d9a0d5..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharObjectHashMap.java +++ /dev/null @@ -1,1050 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of char to Object, implemented using open addressing with - * linear probing for collision resolution. Supports null values. - * - * @see HPPC interfaces diagram - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class CharObjectHashMap - implements CharObjectMap, Preallocable, Cloneable, Accountable { - /** The array holding keys. */ - public char[] keys; - - /** The array holding values. */ - public Object[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public CharObjectHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public CharObjectHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public CharObjectHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public CharObjectHashMap(CharObjectAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public VType put(char key, VType value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - VType previousValue = hasEmptyKey ? (VType) values[mask + 1] : null; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final VType previousValue = (VType) values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return null; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(CharObjectAssociativeContainer container) { - final int count = size(); - for (CharObjectCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable> iterable) { - final int count = size(); - for (CharObjectCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** {@inheritDoc} */ - @Override - public VType remove(char key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return null; - } - hasEmptyKey = false; - VType previousValue = (VType) values[mask + 1]; - values[mask + 1] = null; - return previousValue; - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final VType previousValue = (VType) values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return null; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof CharLookupContainer) { - if (hasEmptyKey && other.contains(((char) 0))) { - hasEmptyKey = false; - values[mask + 1] = null; - } - - final char[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - char existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (CharCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharObjectPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(((char) 0), (VType) values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = null; - } - } - - final char[] keys = this.keys; - final VType[] values = (VType[]) this.values; - for (int slot = 0; slot <= mask; ) { - char existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(((char) 0))) { - hasEmptyKey = false; - values[mask + 1] = null; - } - } - - final char[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - char existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public VType get(char key) { - if (((key) == 0)) { - return hasEmptyKey ? (VType) values[mask + 1] : null; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return (VType) values[slot]; - } - slot = (slot + 1) & mask; - } - - return null; - } - } - - /** {@inheritDoc} */ - @Override - public VType getOrDefault(char key, VType defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? (VType) values[mask + 1] : defaultValue; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return (VType) values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(char key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(char key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public VType indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return (VType) values[index]; - } - - /** {@inheritDoc} */ - @Override - public VType indexReplace(int index, VType newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - VType previousValue = (VType) values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, char key, VType value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public VType indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - VType previousValue = (VType) values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = null; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, ((char) 0)); - - Arrays.fill(values, null); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (CharObjectCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** - * Return true if all keys of some other container exist in this container. Values are compared - * using {@link Objects#equals(Object)} method. - */ - protected boolean equalElements(CharObjectHashMap other) { - if (other.size() != size()) { - return false; - } - - for (CharObjectCursor c : other) { - char key = c.key; - if (!containsKey(key) || !java.util.Objects.equals(c.value, get(key))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final char[] prevKeys = this.keys; - final VType[] prevValues = (VType[]) this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final CharObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new CharObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharObjectCursor fetch() { - final int mask = CharObjectHashMap.this.mask; - while (index <= mask) { - char existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = (VType) values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = ((char) 0); - cursor.value = (VType) values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator> iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T procedure) { - final char[] keys = this.keys; - final VType[] values = (VType[]) this.values; - - if (hasEmptyKey) { - procedure.apply(((char) 0), (VType) values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T predicate) { - final char[] keys = this.keys; - final VType[] values = (VType[]) this.values; - - if (hasEmptyKey) { - if (!predicate.apply(((char) 0), (VType) values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractCharCollection implements CharLookupContainer { - private final CharObjectHashMap owner = CharObjectHashMap.this; - - @Override - public boolean contains(char e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((CharObjectProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((CharObjectPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(CharPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final char e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final CharCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new CharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharCursor fetch() { - final int mask = CharObjectHashMap.this.mask; - while (index <= mask) { - char existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = ((char) 0); - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public ObjectCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractObjectCollection { - private final CharObjectHashMap owner = CharObjectHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(VType value) { - for (CharObjectCursor c : owner) { - if (java.util.Objects.equals(value, c.value)) { - return true; - } - } - return false; - } - - @Override - public > T forEach(T procedure) { - for (CharObjectCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public > T forEach(T predicate) { - for (CharObjectCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator> iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final VType e) { - return owner.removeAll((key, value) -> java.util.Objects.equals(e, value)); - } - - @Override - public int removeAll(final ObjectPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator> { - private final ObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new ObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectCursor fetch() { - final int mask = CharObjectHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = (VType) values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = (VType) values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public CharObjectHashMap clone() { - try { - - CharObjectHashMap cloned = (CharObjectHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (CharObjectCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return CharBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static CharObjectHashMap from(char[] keys, VType[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - CharObjectHashMap map = new CharObjectHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(char key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(char[] fromKeys, VType[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final char[] keys = this.keys; - final VType[] values = (VType[]) this.values; - final int mask = this.mask; - char existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - char[] prevKeys = this.keys; - VType[] prevValues = (VType[]) this.values; - try { - int emptyElementSlot = 1; - this.keys = (new char[arraySize + emptyElementSlot]); - this.values = ((VType[]) new Object[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, char pendingKey, VType pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final char[] prevKeys = this.keys; - final VType[] prevValues = (VType[]) this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final char[] keys = this.keys; - final VType[] values = (VType[]) this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final char existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = ((char) 0); - values[gapSlot] = null; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/CharObjectMap.java b/src/main/java/com/carrotsearch/hppc/CharObjectMap.java deleted file mode 100755 index 6a1004df..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharObjectMap.java +++ /dev/null @@ -1,181 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.CharObjectCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface CharObjectMap extends CharObjectAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public VType get(char key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public VType getOrDefault(char key, VType defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public VType put(char key, VType value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(char key, VType value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(CharObjectAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable> iterable); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public VType remove(char key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link CharObjectMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(char key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public VType indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public VType indexReplace(int index, VType newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, char key, VType value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public VType indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharSet.java b/src/main/java/com/carrotsearch/hppc/CharSet.java deleted file mode 100755 index a9740eac..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharSet.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.carrotsearch.hppc; - -/** A set of chars. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeSet.java") -public interface CharSet extends CharCollection { - /** - * Adds k to the set. - * - * @return Returns true if this element was not part of the set before. Returns - * false if an equal element is already part of the set, does not replace the - * existing element with the argument. - */ - public boolean add(char k); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); - - /** - * Adds all elements from the given {@link CharContainer} to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - * @since 0.9.1 - */ - public int addAll(CharContainer container); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharShortAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/CharShortAssociativeContainer.java deleted file mode 100755 index 66f6c444..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharShortAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see CharContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface CharShortAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(char key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(CharShortPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link CharShortProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link CharShortPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public CharCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public ShortContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharShortHashMap.java b/src/main/java/com/carrotsearch/hppc/CharShortHashMap.java deleted file mode 100755 index 686281c6..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharShortHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of char to short, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class CharShortHashMap implements CharShortMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public char[] keys; - - /** The array holding values. */ - public short[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public CharShortHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public CharShortHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public CharShortHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public CharShortHashMap(CharShortAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public short put(char key, short value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - short previousValue = hasEmptyKey ? values[mask + 1] : ((short) 0); - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final short previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return ((short) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(CharShortAssociativeContainer container) { - final int count = size(); - for (CharShortCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (CharShortCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public short putOrAdd(char key, short putValue, short incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((short) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public short addTo(char key, short incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public short remove(char key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return ((short) 0); - } - hasEmptyKey = false; - short previousValue = values[mask + 1]; - values[mask + 1] = ((short) 0); - return previousValue; - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final short previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return ((short) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof CharLookupContainer) { - if (hasEmptyKey && other.contains(((char) 0))) { - hasEmptyKey = false; - values[mask + 1] = ((short) 0); - } - - final char[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - char existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (CharCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharShortPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(((char) 0), values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = ((short) 0); - } - } - - final char[] keys = this.keys; - final short[] values = this.values; - for (int slot = 0; slot <= mask; ) { - char existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(CharPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(((char) 0))) { - hasEmptyKey = false; - values[mask + 1] = ((short) 0); - } - } - - final char[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - char existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public short get(char key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : ((short) 0); - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return ((short) 0); - } - } - - /** {@inheritDoc} */ - @Override - public short getOrDefault(char key, short defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(char key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final char[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(char key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final char[] keys = this.keys; - int slot = hashKey(key) & mask; - - char existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public short indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public short indexReplace(int index, short newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - short previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, char key, short value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public short indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - short previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = ((short) 0); - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, ((char) 0)); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (CharShortCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(CharShortHashMap other) { - if (other.size() != size()) { - return false; - } - - for (CharShortCursor c : other) { - char key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final char[] prevKeys = this.keys; - final short[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final CharShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new CharShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharShortCursor fetch() { - final int mask = CharShortHashMap.this.mask; - while (index <= mask) { - char existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = ((char) 0); - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final char[] keys = this.keys; - final short[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(((char) 0), values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final char[] keys = this.keys; - final short[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(((char) 0), values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractCharCollection implements CharLookupContainer { - private final CharShortHashMap owner = CharShortHashMap.this; - - @Override - public boolean contains(char e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((CharShortProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((CharShortPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(CharPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final char e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final CharCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new CharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharCursor fetch() { - final int mask = CharShortHashMap.this.mask; - while (index <= mask) { - char existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = ((char) 0); - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public ShortCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractShortCollection { - private final CharShortHashMap owner = CharShortHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(short value) { - for (CharShortCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (CharShortCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (CharShortCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final short e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final ShortPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new ShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortCursor fetch() { - final int mask = CharShortHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public CharShortHashMap clone() { - try { - - CharShortHashMap cloned = (CharShortHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (CharShortCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return CharBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static CharShortHashMap from(char[] keys, short[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - CharShortHashMap map = new CharShortHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(char key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(char[] fromKeys, short[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final char[] keys = this.keys; - final short[] values = this.values; - final int mask = this.mask; - char existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - char[] prevKeys = this.keys; - short[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new char[arraySize + emptyElementSlot]); - this.values = (new short[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, char pendingKey, short pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final char[] prevKeys = this.keys; - final short[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final char[] keys = this.keys; - final short[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final char existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = ((char) 0); - values[gapSlot] = ((short) 0); - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/CharShortMap.java b/src/main/java/com/carrotsearch/hppc/CharShortMap.java deleted file mode 100755 index e5b0deeb..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharShortMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.CharShortCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface CharShortMap extends CharShortAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public short get(char key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public short getOrDefault(char key, short defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public short put(char key, short value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(char key, short value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(CharShortAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public short putOrAdd(char key, short putValue, short incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public short addTo(char key, short additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public short remove(char key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link CharShortMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(char key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public short indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public short indexReplace(int index, short newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, char key, short value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public short indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/CharStack.java b/src/main/java/com/carrotsearch/hppc/CharStack.java deleted file mode 100755 index fb7a5947..00000000 --- a/src/main/java/com/carrotsearch/hppc/CharStack.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.CharCursor; - -/** - * A subclass of {@link CharArrayList} adding stack-related utility methods. The top of the stack is - * at the {@link #size()} - 1 element. - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeStack.java") -public class CharStack extends CharArrayList { - /** New instance with sane defaults. */ - public CharStack() { - super(); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public CharStack(int expectedElements) { - super(expectedElements); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public CharStack(int expectedElements, ArraySizingStrategy resizer) { - super(expectedElements, resizer); - } - - /** Create a stack by pushing all elements of another container to it. */ - public CharStack(CharContainer container) { - super(container); - } - - /** Adds one char to the stack. */ - public void push(char e1) { - ensureBufferSpace(1); - buffer[elementsCount++] = e1; - } - - /** Adds two chars to the stack. */ - public void push(char e1, char e2) { - ensureBufferSpace(2); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - } - - /** Adds three chars to the stack. */ - public void push(char e1, char e2, char e3) { - ensureBufferSpace(3); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - buffer[elementsCount++] = e3; - } - - /** Adds four chars to the stack. */ - public void push(char e1, char e2, char e3, char e4) { - ensureBufferSpace(4); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - buffer[elementsCount++] = e3; - buffer[elementsCount++] = e4; - } - - /** Add a range of array elements to the stack. */ - public void push(char[] elements, int start, int len) { - assert start >= 0 && len >= 0; - - ensureBufferSpace(len); - System.arraycopy(elements, start, buffer, elementsCount, len); - elementsCount += len; - } - - /** - * Vararg-signature method for pushing elements at the top of the stack. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - */ - public final void push(char... elements) { - push(elements, 0, elements.length); - } - - /** Pushes all elements from another container to the top of the stack. */ - public int pushAll(CharContainer container) { - return addAll(container); - } - - /** Pushes all elements from another iterable to the top of the stack. */ - public int pushAll(Iterable iterable) { - return addAll(iterable); - } - - /** Discard an arbitrary number of elements from the top of the stack. */ - public void discard(int count) { - assert elementsCount >= count; - - elementsCount -= count; - } - - /** Discard the top element from the stack. */ - public void discard() { - assert elementsCount > 0; - - elementsCount--; - } - - /** Remove the top element from the stack and return it. */ - public char pop() { - return removeLast(); - } - - /** Peek at the top element on the stack. */ - public char peek() { - assert elementsCount > 0; - return buffer[elementsCount - 1]; - } - - /** Create a stack by pushing a variable number of arguments to it. */ - public static CharStack from(char... elements) { - final CharStack stack = new CharStack(elements.length); - stack.push(elements); - return stack; - } - - /** {@inheritDoc} */ - @Override - public CharStack clone() { - return (CharStack) super.clone(); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/Containers.java b/src/main/java/com/carrotsearch/hppc/Containers.java deleted file mode 100755 index edd41b7c..00000000 --- a/src/main/java/com/carrotsearch/hppc/Containers.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.internals.SuppressForbidden; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Constants used as defaults in containers. - * - * @see HashContainers - */ -public final class Containers { - /** The default number of expected elements for containers. */ - public static final int DEFAULT_EXPECTED_ELEMENTS = 4; - - /** - * External initial seed value. We do not care about multiple assignments so not volatile. - * - * @see #randomSeed64() - */ - private static String testsSeedProperty; - - /** Unique marker for {@link #testsSeedProperty}. */ - private static final String NOT_AVAILABLE = new String(); - - private Containers() {} - - /** - * Provides a (possibly) random initial seed for randomized stuff. - * - *

If tests.seed property is available and accessible, the returned value will be - * derived from the value of that property and will be constant to ensure reproducibility in - * presence of the randomized testing package. - * - * @see "https://github.com/carrotsearch/randomizedtesting" - */ - @SuppressForbidden - public static long randomSeed64() { - if (testsSeedProperty == null) { - testsSeedProperty = System.getProperty("tests.seed", NOT_AVAILABLE); - } - - long initialSeed; - if (testsSeedProperty != NOT_AVAILABLE) { - initialSeed = testsSeedProperty.hashCode(); - } else { - // Mix something that is changing over time (nanoTime) - // ... with something that is thread-local and relatively unique - // even for very short time-spans (new Object's address from a TLAB). - initialSeed = System.nanoTime() ^ System.identityHashCode(new Object()); - } - return BitMixer.mix64(initialSeed); - } - - /** Reset state for tests. */ - static void test$reset() { - testsSeedProperty = null; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/DoubleArrayDeque.java b/src/main/java/com/carrotsearch/hppc/DoubleArrayDeque.java deleted file mode 100755 index 7561da28..00000000 --- a/src/main/java/com/carrotsearch/hppc/DoubleArrayDeque.java +++ /dev/null @@ -1,776 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; - -import com.carrotsearch.hppc.cursors.DoubleCursor; -import com.carrotsearch.hppc.predicates.DoublePredicate; -import com.carrotsearch.hppc.procedures.DoubleProcedure; -import java.util.*; - -/** An array-backed {@link DoubleDeque}. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeArrayDeque.java") -public class DoubleArrayDeque extends AbstractDoubleCollection - implements DoubleDeque, Preallocable, Cloneable, Accountable { - - /** Reuse the same strategy instance. */ - private static final BoundedProportionalArraySizingStrategy DEFAULT_SIZING_STRATEGY = - BoundedProportionalArraySizingStrategy.DEFAULT_INSTANCE; - - /** Internal array for storing elements of the deque. */ - public double[] buffer = DoubleArrayList.EMPTY_ARRAY; - - /** - * The index of the element at the head of the deque or an arbitrary number equal to tail if the - * deque is empty. - */ - public int head; - - /** The index at which the next element would be added to the tail of the deque. */ - public int tail; - - /** Buffer resizing strategy. */ - protected final ArraySizingStrategy resizer; - - /** New instance with sane defaults. */ - public DoubleArrayDeque() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public DoubleArrayDeque(int expectedElements) { - this(expectedElements, DEFAULT_SIZING_STRATEGY); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public DoubleArrayDeque(int expectedElements, ArraySizingStrategy resizer) { - assert resizer != null; - this.resizer = resizer; - ensureCapacity(expectedElements); - } - - /** - * Creates a new deque from elements of another container, appending elements at the end of the - * deque in the iteration order. - */ - public DoubleArrayDeque(DoubleContainer container) { - this(container.size()); - addLast(container); - } - - /** {@inheritDoc} */ - @Override - public void addFirst(double e1) { - int h = oneLeft(head, buffer.length); - if (h == tail) { - ensureBufferSpace(1); - h = oneLeft(head, buffer.length); - } - buffer[head = h] = e1; - } - - /** - * Vararg-signature method for adding elements at the front of this deque. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - * - * @param elements The elements to add. - */ - public final void addFirst(double... elements) { - ensureBufferSpace(elements.length); - for (double k : elements) { - addFirst(k); - } - } - - /** - * Inserts all elements from the given container to the front of this deque. - * - * @param container The container to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addFirst(DoubleContainer container) { - int size = container.size(); - ensureBufferSpace(size); - - for (DoubleCursor cursor : container) { - addFirst(cursor.value); - } - - return size; - } - - /** - * Inserts all elements from the given iterable to the front of this deque. - * - * @param iterable The iterable to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addFirst(Iterable iterable) { - int size = 0; - for (DoubleCursor cursor : iterable) { - addFirst(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public void addLast(double e1) { - int t = oneRight(tail, buffer.length); - if (head == t) { - ensureBufferSpace(1); - t = oneRight(tail, buffer.length); - } - buffer[tail] = e1; - tail = t; - } - - /** - * Vararg-signature method for adding elements at the end of this deque. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - * - * @param elements The elements to iterate over. - */ - public final void addLast(double... elements) { - ensureBufferSpace(1); - for (double k : elements) { - addLast(k); - } - } - - /** - * Inserts all elements from the given container to the end of this deque. - * - * @param container The container to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addLast(DoubleContainer container) { - int size = container.size(); - ensureBufferSpace(size); - - for (DoubleCursor cursor : container) { - addLast(cursor.value); - } - - return size; - } - - /** - * Inserts all elements from the given iterable to the end of this deque. - * - * @param iterable The iterable to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addLast(Iterable iterable) { - int size = 0; - for (DoubleCursor cursor : iterable) { - addLast(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public double removeFirst() { - assert size() > 0 : "The deque is empty."; - - final double result = buffer[head]; - buffer[head] = 0d; - head = oneRight(head, buffer.length); - return result; - } - - /** {@inheritDoc} */ - @Override - public double removeLast() { - assert size() > 0 : "The deque is empty."; - - tail = oneLeft(tail, buffer.length); - final double result = buffer[tail]; - buffer[tail] = 0d; - return result; - } - - /** {@inheritDoc} */ - @Override - public double getFirst() { - assert size() > 0 : "The deque is empty."; - - return buffer[head]; - } - - /** {@inheritDoc} */ - @Override - public double getLast() { - assert size() > 0 : "The deque is empty."; - - return buffer[oneLeft(tail, buffer.length)]; - } - - /** {@inheritDoc} */ - @Override - public int removeFirst(double e1) { - final int index = bufferIndexOf(e1); - if (index >= 0) removeAtBufferIndex(index); - return index; - } - - /** - * Return the index of the first (counting from head) element equal to e1. The index - * points to the {@link #buffer} array. - * - * @param e1 The element to look for. - * @return Returns the index of the first element equal to e1 or -1 if - * not found. - */ - public int bufferIndexOf(double e1) { - final int last = tail; - final int bufLen = buffer.length; - for (int i = head; i != last; i = oneRight(i, bufLen)) { - if ((Double.doubleToLongBits(e1) == Double.doubleToLongBits(buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int removeLast(double e1) { - final int index = lastBufferIndexOf(e1); - if (index >= 0) { - removeAtBufferIndex(index); - } - return index; - } - - /** - * Return the index of the last (counting from tail) element equal to e1. The index - * points to the {@link #buffer} array. - * - * @param e1 The element to look for. - * @return Returns the index of the first element equal to e1 or -1 if - * not found. - */ - public int lastBufferIndexOf(double e1) { - final int bufLen = buffer.length; - final int last = oneLeft(head, bufLen); - for (int i = oneLeft(tail, bufLen); i != last; i = oneLeft(i, bufLen)) { - if ((Double.doubleToLongBits(e1) == Double.doubleToLongBits(buffer[i]))) return i; - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(double e1) { - int removed = 0; - final int last = tail; - final int bufLen = buffer.length; - int from, to; - for (from = to = head; from != last; from = oneRight(from, bufLen)) { - if ((Double.doubleToLongBits(e1) == Double.doubleToLongBits(buffer[from]))) { - buffer[from] = 0d; - removed++; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0d; - } - - to = oneRight(to, bufLen); - } - - tail = to; - return removed; - } - - /** - * Removes the element at index in the internal {#link {@link #buffer} array, - * returning its value. - * - * @param index Index of the element to remove. The index must be located between {@link #head} - * and {@link #tail} in modulo {@link #buffer} arithmetic. - */ - public void removeAtBufferIndex(int index) { - assert (head <= tail ? index >= head && index < tail : index >= head || index < tail) - : "Index out of range (head=" + head + ", tail=" + tail + ", index=" + index + ")."; - - // Cache fields in locals (hopefully moved to registers). - final double[] buffer = this.buffer; - final int bufLen = buffer.length; - final int lastIndex = bufLen - 1; - final int head = this.head; - final int tail = this.tail; - - final int leftChunk = Math.abs(index - head) % bufLen; - final int rightChunk = Math.abs(tail - index) % bufLen; - - if (leftChunk < rightChunk) { - if (index >= head) { - System.arraycopy(buffer, head, buffer, head + 1, leftChunk); - } else { - System.arraycopy(buffer, 0, buffer, 1, index); - buffer[0] = buffer[lastIndex]; - System.arraycopy(buffer, head, buffer, head + 1, lastIndex - head); - } - buffer[head] = 0d; - this.head = oneRight(head, bufLen); - } else { - if (index < tail) { - System.arraycopy(buffer, index + 1, buffer, index, rightChunk); - } else { - System.arraycopy(buffer, index + 1, buffer, index, lastIndex - index); - buffer[lastIndex] = buffer[0]; - System.arraycopy(buffer, 1, buffer, 0, tail); - } - buffer[tail] = 0d; - this.tail = oneLeft(tail, bufLen); - } - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int size() { - if (head <= tail) return tail - head; - else return (tail - head + buffer.length); - } - - /** - * {@inheritDoc} - * - *

The internal array buffers are not released as a result of this call. - * - * @see #release() - */ - @Override - public void clear() { - if (head < tail) { - Arrays.fill(buffer, head, tail, 0d); - } else { - Arrays.fill(buffer, 0, tail, 0d); - Arrays.fill(buffer, head, buffer.length, 0d); - } - this.head = tail = 0; - } - - /** Release internal buffers of this deque and reallocate with the default buffer. */ - public void release() { - this.head = tail = 0; - buffer = DoubleArrayList.EMPTY_ARRAY; - ensureBufferSpace(0); - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - ensureBufferSpace(expectedElements - size()); - } - - /** - * Ensures the internal buffer has enough free slots to store expectedAdditions. - * Increases internal buffer size if needed. - */ - protected void ensureBufferSpace(int expectedAdditions) { - final int bufferLen = buffer.length; - final int elementsCount = size(); - - if (elementsCount + expectedAdditions >= bufferLen) { - final int emptySlot = 1; // deque invariant: always an empty slot. - final int newSize = resizer.grow(bufferLen, elementsCount + emptySlot, expectedAdditions); - assert newSize >= (elementsCount + expectedAdditions + emptySlot) - : "Resizer failed to" - + " return sensible new size: " - + newSize - + " <= " - + (elementsCount + expectedAdditions); - - try { - final double[] newBuffer = (new double[newSize]); - if (bufferLen > 0) { - toArray(newBuffer); - tail = elementsCount; - head = 0; - } - this.buffer = newBuffer; - } catch (OutOfMemoryError e) { - throw new BufferAllocationException( - "Not enough memory to allocate new buffers: %,d -> %,d", e, bufferLen, newSize); - } - } - } - - /** {@inheritDoc} */ - @Override - public double[] toArray() { - - final int size = size(); - return toArray((new double[size])); - } - - /** - * Copies elements of this deque to an array. The content of the target array is - * filled from index 0 (head of the queue) to index size() - 1 (tail of the queue). - * - * @param target The target array must be large enough to hold all elements. - * @return Returns the target argument for chaining. - */ - public double[] toArray(double[] target) { - assert target.length >= size() : "Target array must be >= " + size(); - - if (head < tail) { - // The contents is not wrapped around. Just copy. - System.arraycopy(buffer, head, target, 0, size()); - } else if (head > tail) { - // The contents is split. Merge elements from the following indexes: - // [head...buffer.length - 1][0, tail - 1] - final int rightCount = buffer.length - head; - System.arraycopy(buffer, head, target, 0, rightCount); - System.arraycopy(buffer, 0, target, rightCount, tail); - } - - return target; - } - - /** - * Clone this object. The returned clone will reuse the same hash function and array resizing - * strategy. - */ - @Override - public DoubleArrayDeque clone() { - try { - - DoubleArrayDeque cloned = (DoubleArrayDeque) super.clone(); - cloned.buffer = buffer.clone(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Move one index to the left, wrapping around buffer. */ - protected static int oneLeft(int index, int modulus) { - if (index >= 1) { - return index - 1; - } - return modulus - 1; - } - - /** Move one index to the right, wrapping around buffer. */ - protected static int oneRight(int index, int modulus) { - if (index + 1 == modulus) { - return 0; - } - return index + 1; - } - - @Override - public long ramBytesAllocated() { - // int: head, tail - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES * 2 - + resizer.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(buffer); - } - - @Override - public long ramBytesUsed() { - // int: head, tail - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES * 2 - + resizer.ramBytesUsed() - + RamUsageEstimator.shallowUsedSizeOfArray(buffer, size()); - } - - /** An iterator implementation for {@link ObjectArrayDeque#iterator}. */ - private final class ValueIterator extends AbstractIterator { - private final DoubleCursor cursor; - private int remaining; - - public ValueIterator() { - cursor = new DoubleCursor(); - cursor.index = oneLeft(head, buffer.length); - this.remaining = size(); - } - - @Override - protected DoubleCursor fetch() { - if (remaining == 0) { - return done(); - } - - remaining--; - cursor.value = buffer[cursor.index = oneRight(cursor.index, buffer.length)]; - return cursor; - } - } - - /** An iterator implementation for {@link ObjectArrayDeque#descendingIterator()}. */ - private final class DescendingValueIterator extends AbstractIterator { - private final DoubleCursor cursor; - private int remaining; - - public DescendingValueIterator() { - cursor = new DoubleCursor(); - cursor.index = tail; - this.remaining = size(); - } - - @Override - protected DoubleCursor fetch() { - if (remaining == 0) return done(); - - remaining--; - cursor.value = buffer[cursor.index = oneLeft(cursor.index, buffer.length)]; - return cursor; - } - } - - /** - * Returns a cursor over the values of this deque (in head to tail order). The iterator is - * implemented as a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()} (to avoid boxing of primitive types). To read the current value (or index in - * the deque's buffer) use the cursor's public fields. An example is shown below. - * - *

-   * for (IntValueCursor c : intDeque) {
-   *   System.out.println("buffer index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator iterator() { - return new ValueIterator(); - } - - /** - * Returns a cursor over the values of this deque (in tail to head order). The iterator is - * implemented as a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()} (to avoid boxing of primitive types). To read the current value (or index in - * the deque's buffer) use the cursor's public fields. An example is shown below. - * - *
-   * for (Iterator<IntCursor> i = intDeque.descendingIterator(); i.hasNext();) {
-   *   final IntCursor c = i.next();
-   *   System.out.println("buffer index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator descendingIterator() { - return new DescendingValueIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - forEach(procedure, head, tail); - return procedure; - } - - /** - * Applies procedure to a slice of the deque, fromIndex, inclusive, to - * toIndex, exclusive. - */ - private void forEach(DoubleProcedure procedure, int fromIndex, final int toIndex) { - final double[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - procedure.apply(buffer[i]); - } - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - int fromIndex = head; - int toIndex = tail; - - final double[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - if (!predicate.apply(buffer[i])) { - break; - } - } - - return predicate; - } - - /** Applies procedure to all elements of this deque, tail to head. */ - @Override - public T descendingForEach(T procedure) { - descendingForEach(procedure, head, tail); - return procedure; - } - - /** - * Applies procedure to a slice of the deque, toIndex, exclusive, down - * to fromIndex, inclusive. - */ - private void descendingForEach(DoubleProcedure procedure, int fromIndex, final int toIndex) { - if (fromIndex == toIndex) return; - - final double[] buffer = this.buffer; - int i = toIndex; - do { - i = oneLeft(i, buffer.length); - procedure.apply(buffer[i]); - } while (i != fromIndex); - } - - /** {@inheritDoc} */ - @Override - public T descendingForEach(T predicate) { - descendingForEach(predicate, head, tail); - return predicate; - } - - /** - * Applies predicate to a slice of the deque, toIndex, exclusive, down - * to fromIndex, inclusive or until the predicate returns false. - */ - private void descendingForEach(DoublePredicate predicate, int fromIndex, final int toIndex) { - if (fromIndex == toIndex) return; - - final double[] buffer = this.buffer; - int i = toIndex; - do { - i = oneLeft(i, buffer.length); - if (!predicate.apply(buffer[i])) { - break; - } - } while (i != fromIndex); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(DoublePredicate predicate) { - final double[] buffer = this.buffer; - final int last = tail; - final int bufLen = buffer.length; - int removed = 0; - int from, to; - from = to = head; - try { - for (from = to = head; from != last; from = oneRight(from, bufLen)) { - if (predicate.apply(buffer[from])) { - buffer[from] = 0d; - removed++; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0d; - } - - to = oneRight(to, bufLen); - } - } finally { - // Keep the deque in consistent state even if the predicate throws an exception. - for (; from != last; from = oneRight(from, bufLen)) { - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0d; - } - - to = oneRight(to, bufLen); - } - tail = to; - } - - return removed; - } - - /** {@inheritDoc} */ - @Override - public boolean contains(double e) { - int fromIndex = head; - int toIndex = tail; - - final double[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - if ((Double.doubleToLongBits(e) == Double.doubleToLongBits(buffer[i]))) { - return true; - } - } - - return false; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = 1; - int fromIndex = head; - int toIndex = tail; - - final double[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - h = 31 * h + BitMixer.mix(this.buffer[i]); - } - return h; - } - - /** - * Returns true only if the other object is an instance of the same class and with - * the same elements. - */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Compare order-aligned elements against another {@link DoubleDeque}. */ - protected boolean equalElements(DoubleArrayDeque other) { - int max = size(); - if (other.size() != max) { - return false; - } - - Iterator i1 = this.iterator(); - Iterator i2 = other.iterator(); - - while (i1.hasNext() && i2.hasNext()) { - if (!(Double.doubleToLongBits(i1.next().value) == Double.doubleToLongBits(i2.next().value))) { - return false; - } - } - - return !i1.hasNext() && !i2.hasNext(); - } - - /** Create a new deque by pushing a variable number of arguments to the end of it. */ - public static DoubleArrayDeque from(double... elements) { - final DoubleArrayDeque coll = new DoubleArrayDeque(elements.length); - coll.addLast(elements); - return coll; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/DoubleArrayList.java b/src/main/java/com/carrotsearch/hppc/DoubleArrayList.java deleted file mode 100755 index a0a8bbdd..00000000 --- a/src/main/java/com/carrotsearch/hppc/DoubleArrayList.java +++ /dev/null @@ -1,586 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.DoublePredicate; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; -import java.util.stream.DoubleStream; - -/** An array-backed list of doubles. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeArrayList.java") -public class DoubleArrayList extends AbstractDoubleCollection - implements DoubleIndexedContainer, Preallocable, Cloneable, Accountable { - /** An immutable empty buffer (array). */ - public static final double[] EMPTY_ARRAY = new double[0]; - - ; - - /** Reuse the same strategy instance. */ - private static final BoundedProportionalArraySizingStrategy DEFAULT_SIZING_STRATEGY = - BoundedProportionalArraySizingStrategy.DEFAULT_INSTANCE; - - /** - * Internal array for storing the list. The array may be larger than the current size ({@link - * #size()}). - */ - public double[] buffer = EMPTY_ARRAY; - - /** Current number of elements stored in {@link #buffer}. */ - public int elementsCount; - - /** Buffer resizing strategy. */ - protected final ArraySizingStrategy resizer; - - /** New instance with sane defaults. */ - public DoubleArrayList() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public DoubleArrayList(int expectedElements) { - this(expectedElements, DEFAULT_SIZING_STRATEGY); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public DoubleArrayList(int expectedElements, ArraySizingStrategy resizer) { - assert resizer != null; - this.resizer = resizer; - buffer = Arrays.copyOf(buffer, expectedElements); - } - - /** Creates a new list from the elements of another container in its iteration order. */ - public DoubleArrayList(DoubleContainer container) { - this(container.size()); - addAll(container); - } - - /** {@inheritDoc} */ - @Override - public void add(double e1) { - ensureBufferSpace(1); - buffer[elementsCount++] = e1; - } - - /** - * Appends two elements at the end of the list. To add more than two elements, use add - * (vararg-version) or access the buffer directly (tight loop). - */ - public void add(double e1, double e2) { - ensureBufferSpace(2); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - } - - /** Add all elements from a range of given array to the list. */ - public void add(double[] elements, int start, int length) { - assert length >= 0 : "Length must be >= 0"; - - ensureBufferSpace(length); - System.arraycopy(elements, start, buffer, elementsCount, length); - elementsCount += length; - } - - /** - * Vararg-signature method for adding elements at the end of the list. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - */ - public final void add(double... elements) { - add(elements, 0, elements.length); - } - - /** Adds all elements from another container. */ - public int addAll(DoubleContainer container) { - final int size = container.size(); - ensureBufferSpace(size); - - for (DoubleCursor cursor : container) { - add(cursor.value); - } - - return size; - } - - /** Adds all elements from another iterable. */ - public int addAll(Iterable iterable) { - int size = 0; - for (DoubleCursor cursor : iterable) { - add(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public void insert(int index, double e1) { - assert (index >= 0 && index <= size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + "]."; - - ensureBufferSpace(1); - System.arraycopy(buffer, index, buffer, index + 1, elementsCount - index); - buffer[index] = e1; - elementsCount++; - } - - /** {@inheritDoc} */ - @Override - public double get(int index) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - return buffer[index]; - } - - /** {@inheritDoc} */ - @Override - public double set(int index, double e1) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - final double v = buffer[index]; - buffer[index] = e1; - return v; - } - - /** {@inheritDoc} */ - @Override - public double removeAt(int index) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - final double v = buffer[index]; - System.arraycopy(buffer, index + 1, buffer, index, --elementsCount - index); - - return v; - } - - /** {@inheritDoc} */ - @Override - public double removeLast() { - assert elementsCount > 0; - - final double v = buffer[--elementsCount]; - - return v; - } - - /** {@inheritDoc} */ - @Override - public void removeRange(int fromIndex, int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - System.arraycopy(buffer, toIndex, buffer, fromIndex, elementsCount - toIndex); - final int count = toIndex - fromIndex; - elementsCount -= count; - } - - /** {@inheritDoc} */ - @Override - public boolean removeElement(double e1) { - return removeFirst(e1) != -1; - } - - /** {@inheritDoc} */ - @Override - public int removeFirst(double e1) { - final int index = indexOf(e1); - if (index >= 0) removeAt(index); - return index; - } - - /** {@inheritDoc} */ - @Override - public int removeLast(double e1) { - final int index = lastIndexOf(e1); - if (index >= 0) removeAt(index); - return index; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(double e1) { - int to = 0; - for (int from = 0; from < elementsCount; from++) { - if ((Double.doubleToLongBits(e1) == Double.doubleToLongBits(buffer[from]))) { - continue; - } - if (to != from) { - buffer[to] = buffer[from]; - } - to++; - } - final int deleted = elementsCount - to; - this.elementsCount = to; - - return deleted; - } - - /** {@inheritDoc} */ - @Override - public boolean contains(double e1) { - return indexOf(e1) >= 0; - } - - /** {@inheritDoc} */ - @Override - public int indexOf(double e1) { - for (int i = 0; i < elementsCount; i++) { - if ((Double.doubleToLongBits(e1) == Double.doubleToLongBits(buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int lastIndexOf(double e1) { - for (int i = elementsCount - 1; i >= 0; i--) { - if ((Double.doubleToLongBits(e1) == Double.doubleToLongBits(buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return elementsCount == 0; - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - final int bufferLen = (buffer == null ? 0 : buffer.length); - if (expectedElements > bufferLen) { - ensureBufferSpace(expectedElements - size()); - } - } - - /** - * Ensures the internal buffer has enough free slots to store expectedAdditions. - * Increases internal buffer size if needed. - */ - protected void ensureBufferSpace(int expectedAdditions) { - final int bufferLen = (buffer == null ? 0 : buffer.length); - if (elementsCount + expectedAdditions > bufferLen) { - final int newSize = resizer.grow(bufferLen, elementsCount, expectedAdditions); - assert newSize >= elementsCount + expectedAdditions - : "Resizer failed to" - + " return sensible new size: " - + newSize - + " <= " - + (elementsCount + expectedAdditions); - - this.buffer = Arrays.copyOf(buffer, newSize); - } - } - - /** - * Truncate or expand the list to the new size. If the list is truncated, the buffer will not be - * reallocated (use {@link #trimToSize()} if you need a truncated buffer), but the truncated - * values will be reset to the default value (zero). If the list is expanded, the elements beyond - * the current size are initialized with JVM-defaults (zero or null values). - */ - public void resize(int newSize) { - if (newSize <= buffer.length) { - if (newSize < elementsCount) { - Arrays.fill(buffer, newSize, elementsCount, 0d); - } else { - Arrays.fill(buffer, elementsCount, newSize, 0d); - } - } else { - ensureCapacity(newSize); - } - this.elementsCount = newSize; - } - - /** {@inheritDoc} */ - @Override - public int size() { - return elementsCount; - } - - /** Trim the internal buffer to the current size. */ - public void trimToSize() { - if (size() != this.buffer.length) { - this.buffer = toArray(); - } - } - - /** - * Sets the number of stored elements to zero. Releases and initializes the internal storage array - * to default values. To clear the list without cleaning the buffer, simply set the {@link - * #elementsCount} field to zero. - */ - @Override - public void clear() { - Arrays.fill(buffer, 0, elementsCount, 0d); - this.elementsCount = 0; - } - - /** Sets the number of stored elements to zero and releases the internal storage array. */ - @Override - public void release() { - this.buffer = EMPTY_ARRAY; - this.elementsCount = 0; - } - - /** - * {@inheritDoc} - * - *

The returned array is sized to match exactly the number of elements of the stack. - */ - @Override - public double[] toArray() { - - return Arrays.copyOf(buffer, elementsCount); - } - - @Override - public DoubleStream stream() { - - return Arrays.stream(buffer, 0, size()); - } - - /** {@inheritDoc} */ - @Override - public DoubleIndexedContainer sort() { - Arrays.sort(buffer, 0, elementsCount); - return this; - } - - /** {@inheritDoc} */ - @Override - public DoubleIndexedContainer reverse() { - for (int i = 0, mid = elementsCount >> 1, j = elementsCount - 1; i < mid; i++, j--) { - double tmp = buffer[i]; - buffer[i] = buffer[j]; - buffer[j] = tmp; - } - return this; - } - - /** - * Clone this object. The returned clone will reuse the same hash function and array resizing - * strategy. - */ - @Override - public DoubleArrayList clone() { - try { - - final DoubleArrayList cloned = (DoubleArrayList) super.clone(); - cloned.buffer = buffer.clone(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = 1, max = elementsCount; - for (int i = 0; i < max; i++) { - h = 31 * h + BitMixer.mix(this.buffer[i]); - } - return h; - } - - /** - * Returns true only if the other object is an instance of the same class and with - * the same elements. - */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Compare index-aligned elements against another {@link DoubleIndexedContainer}. */ - protected boolean equalElements(DoubleArrayList other) { - int max = size(); - if (other.size() != max) { - return false; - } - - for (int i = 0; i < max; i++) { - if (!(Double.doubleToLongBits(get(i)) == Double.doubleToLongBits(other.get(i)))) { - return false; - } - } - - return true; - } - - @Override - public long ramBytesAllocated() { - // int: elementsCount - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES - + resizer.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(buffer); - } - - @Override - public long ramBytesUsed() { - // int: elementsCount - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES - + resizer.ramBytesUsed() - + RamUsageEstimator.shallowUsedSizeOfArray(buffer, elementsCount); - } - - /** An iterator implementation for {@link DoubleArrayList#iterator}. */ - static final class ValueIterator extends AbstractIterator { - private final DoubleCursor cursor; - - private final double[] buffer; - private final int size; - - public ValueIterator(double[] buffer, int size) { - this.cursor = new DoubleCursor(); - this.cursor.index = -1; - this.size = size; - this.buffer = buffer; - } - - @Override - protected DoubleCursor fetch() { - if (cursor.index + 1 == size) return done(); - - cursor.value = buffer[++cursor.index]; - return cursor; - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new ValueIterator(buffer, size()); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - return forEach(procedure, 0, size()); - } - - /** - * Applies procedure to a slice of the list, fromIndex, inclusive, to - * toIndex, exclusive. - */ - public T forEach(T procedure, int fromIndex, final int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - final double[] buffer = this.buffer; - for (int i = fromIndex; i < toIndex; i++) { - procedure.apply(buffer[i]); - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(DoublePredicate predicate) { - final double[] buffer = this.buffer; - final int elementsCount = this.elementsCount; - int to = 0; - int from = 0; - try { - for (; from < elementsCount; from++) { - if (predicate.apply(buffer[from])) { - buffer[from] = 0d; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0d; - } - to++; - } - } finally { - // Keep the list in a consistent state, even if the predicate throws an exception. - for (; from < elementsCount; from++) { - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0d; - } - to++; - } - - this.elementsCount = to; - } - - return elementsCount - to; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - return forEach(predicate, 0, size()); - } - - /** - * Applies predicate to a slice of the list, fromIndex, inclusive, to - * toIndex, exclusive, or until predicate returns false. - */ - public T forEach(T predicate, int fromIndex, final int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - final double[] buffer = this.buffer; - for (int i = fromIndex; i < toIndex; i++) { - if (!predicate.apply(buffer[i])) break; - } - - return predicate; - } - - /** - * Create a list from a variable number of arguments or an array of double. The - * elements are copied from the argument to the internal buffer. - */ - public static DoubleArrayList from(double... elements) { - final DoubleArrayList list = new DoubleArrayList(elements.length); - list.add(elements); - return list; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/DoubleBufferVisualizer.java b/src/main/java/com/carrotsearch/hppc/DoubleBufferVisualizer.java deleted file mode 100755 index f708c7a9..00000000 --- a/src/main/java/com/carrotsearch/hppc/DoubleBufferVisualizer.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.carrotsearch.hppc; - -/** - * Reused buffer visualization routines. - * - * @see DoubleSet#visualizeKeyDistribution(int) - * @see DoubleVTypeMap#visualizeKeyDistribution(int) - */ -class DoubleBufferVisualizer { - static String visualizeKeyDistribution(double[] buffer, int max, int characters) { - final StringBuilder b = new StringBuilder(); - final char[] chars = ".123456789X".toCharArray(); - for (int i = 1, start = -1; i <= characters; i++) { - int end = (int) ((long) i * max / characters); - - if (start + 1 <= end) { - int taken = 0; - int slots = 0; - for (int slot = start + 1; slot <= end; slot++, slots++) { - if (!(Double.doubleToLongBits(buffer[slot]) == 0)) { - taken++; - } - } - b.append(chars[Math.min(chars.length - 1, taken * chars.length / slots)]); - start = end; - } - } - while (b.length() < characters) { - b.append(' '); - } - return b.toString(); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/DoubleCollection.java b/src/main/java/com/carrotsearch/hppc/DoubleCollection.java deleted file mode 100755 index e1c4f96a..00000000 --- a/src/main/java/com/carrotsearch/hppc/DoubleCollection.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.predicates.DoublePredicate; - -/** - * A collection allows basic, efficient operations on sets of elements (difference and - * intersection). - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeCollection.java") -public interface DoubleCollection extends DoubleContainer { - /** - * Removes all occurrences of e from this collection. - * - * @param e Element to be removed from this collection, if present. - * @return The number of removed elements as a result of this call. - */ - public int removeAll(double e); - - /** - * Removes all elements in this collection that are present in c. - * - * @return Returns the number of removed elements. - */ - public int removeAll(DoubleLookupContainer c); - - /** - * Removes all elements in this collection for which the given predicate returns true - * . - * - * @return Returns the number of removed elements. - */ - public int removeAll(DoublePredicate predicate); - - /** - * Keeps all elements in this collection that are present in c. Runs in time - * proportional to the number of elements in this collection. Equivalent of sets intersection. - * - * @return Returns the number of removed elements. - */ - public int retainAll(DoubleLookupContainer c); - - /** - * Keeps all elements in this collection for which the given predicate returns true. - * - * @return Returns the number of removed elements. - */ - public int retainAll(DoublePredicate predicate); - - /** - * Removes all elements from this collection. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); -} diff --git a/src/main/java/com/carrotsearch/hppc/DoubleContainer.java b/src/main/java/com/carrotsearch/hppc/DoubleContainer.java deleted file mode 100755 index b5719fd5..00000000 --- a/src/main/java/com/carrotsearch/hppc/DoubleContainer.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.DoubleCursor; -import com.carrotsearch.hppc.predicates.DoublePredicate; -import com.carrotsearch.hppc.procedures.DoubleProcedure; -import java.util.Iterator; - -/** A generic container holding doubles. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeContainer.java") -public interface DoubleContainer extends Iterable { - /** - * Returns an iterator to a cursor traversing the collection. The order of traversal is not - * defined. More than one cursor may be active at a time. The behavior of iterators is undefined - * if structural changes are made to the underlying collection. - * - *

The iterator is implemented as a cursor and it returns the same cursor instance on - * every call to {@link Iterator#next()} (to avoid boxing of primitive types). To read the current - * list's value (or index in the list) use the cursor's public fields. An example is shown below. - * - *

-   * for (DoubleCursor<double> c : container) {
-   *   System.out.println("index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator iterator(); - - /** - * Lookup a given element in the container. This operation has no speed guarantees (may be linear - * with respect to the size of this container). - * - * @return Returns true if this container has an element equal to e. - */ - public boolean contains(double e); - - /** - * Return the current number of elements in this container. The time for calculating the - * container's size may take O(n) time, although implementing classes should try to - * maintain the current size and return in constant time. - */ - public int size(); - - /** Shortcut for size() == 0. */ - public boolean isEmpty(); - - /** - * Copies all elements of this container to an array. - * - *

The returned array is always a copy, regardless of the storage used by the container. - */ - public double[] toArray(); - - /** - * Applies a procedure to all container elements. Returns the argument (any subclass - * of {@link DoubleProcedure}. This lets the caller to call methods of the argument by chaining - * the call (even if the argument is an anonymous type) to retrieve computed values, for example - * (IntContainer): - * - *

-   * int count = container.forEach(new IntProcedure() {
-   *   int count; // this is a field declaration in an anonymous class.
-   *
-   *   public void apply(int value) {
-   *     count++;
-   *   }
-   * }).count;
-   * 
- */ - public T forEach(T procedure); - - /** - * Applies a predicate to container elements as long, as the predicate returns - * true. The iteration is interrupted otherwise. - */ - public T forEach(T predicate); -} diff --git a/src/main/java/com/carrotsearch/hppc/DoubleDeque.java b/src/main/java/com/carrotsearch/hppc/DoubleDeque.java deleted file mode 100755 index 423026ec..00000000 --- a/src/main/java/com/carrotsearch/hppc/DoubleDeque.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.DoubleCursor; -import com.carrotsearch.hppc.predicates.DoublePredicate; -import com.carrotsearch.hppc.procedures.DoubleProcedure; -import java.util.Deque; -import java.util.Iterator; - -/** - * A linear collection that supports element insertion and removal at both ends. - * - * @see Deque - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeDeque.java") -public interface DoubleDeque extends DoubleCollection { - /** - * Removes the first element that equals e. - * - * @return The deleted element's index or -1 if the element was not found. - */ - public int removeFirst(double e); - - /** - * Removes the last element that equals e. - * - * @return The deleted element's index or -1 if the element was not found. - */ - public int removeLast(double e); - - /** Inserts the specified element at the front of this deque. */ - public void addFirst(double e); - - /** Inserts the specified element at the end of this deque. */ - public void addLast(double e); - - /** - * Retrieves and removes the first element of this deque. - * - * @return the head (first) element of this deque. - */ - public double removeFirst(); - - /** - * Retrieves and removes the last element of this deque. - * - * @return the tail of this deque. - */ - public double removeLast(); - - /** - * Retrieves the first element of this deque but does not remove it. - * - * @return the head of this deque. - */ - public double getFirst(); - - /** - * Retrieves the last element of this deque but does not remove it. - * - * @return the head of this deque. - */ - public double getLast(); - - /** - * @return An iterator over elements in this deque in tail-to-head order. - */ - public Iterator descendingIterator(); - - /** Applies a procedure to all elements in tail-to-head order. */ - public T descendingForEach(T procedure); - - /** - * Applies a predicate to container elements as long, as the predicate returns - * true. The iteration is interrupted otherwise. - */ - public T descendingForEach(T predicate); -} diff --git a/src/main/java/com/carrotsearch/hppc/DoubleIndexedContainer.java b/src/main/java/com/carrotsearch/hppc/DoubleIndexedContainer.java deleted file mode 100755 index faf6b27c..00000000 --- a/src/main/java/com/carrotsearch/hppc/DoubleIndexedContainer.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.carrotsearch.hppc; - -import java.util.RandomAccess; -import java.util.stream.DoubleStream; - -/** - * An indexed container provides random access to elements based on an index. Indexes - * are zero-based. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeIndexedContainer.java") -public interface DoubleIndexedContainer extends DoubleCollection, RandomAccess { - /** - * Removes the first element that equals e1, returning whether an element has been - * removed. - */ - public boolean removeElement(double e1); - - /** - * Removes the first element that equals e1, returning its deleted position or - * -1 if the element was not found. - */ - public int removeFirst(double e1); - - /** - * Removes the last element that equals e1, returning its deleted position or - * -1 if the element was not found. - */ - public int removeLast(double e1); - - /** - * Returns the index of the first occurrence of the specified element in this list, or -1 if this - * list does not contain the element. - */ - public int indexOf(double e1); - - /** - * Returns the index of the last occurrence of the specified element in this list, or -1 if this - * list does not contain the element. - */ - public int lastIndexOf(double e1); - - /** Adds an element to the end of this container (the last index is incremented by one). */ - public void add(double e1); - - /** - * Inserts the specified element at the specified position in this list. - * - * @param index The index at which the element should be inserted, shifting any existing and - * subsequent elements to the right. - */ - public void insert(int index, double e1); - - /** - * Replaces the element at the specified position in this list with the specified element. - * - * @return Returns the previous value in the list. - */ - public double set(int index, double e1); - - /** - * @return Returns the element at index index from the list. - */ - public double get(int index); - - /** - * Removes the element at the specified position in this container and returns it. - * - * @see #removeFirst - * @see #removeLast - * @see #removeAll - */ - public double removeAt(int index); - - /** Removes and returns the last element of this container. This container must not be empty. */ - public double removeLast(); - - /** - * Removes from this container all of the elements with indexes between fromIndex, - * inclusive, and toIndex, exclusive. - */ - public void removeRange(int fromIndex, int toIndex); - - /** Returns this container elements as a stream. */ - public DoubleStream stream(); - - /** Sorts the elements in this container and returns this container. */ - public DoubleIndexedContainer sort(); - - /** Reverses the elements in this container and returns this container. */ - public DoubleIndexedContainer reverse(); -} diff --git a/src/main/java/com/carrotsearch/hppc/DoubleLookupContainer.java b/src/main/java/com/carrotsearch/hppc/DoubleLookupContainer.java deleted file mode 100755 index c27e6b4b..00000000 --- a/src/main/java/com/carrotsearch/hppc/DoubleLookupContainer.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.carrotsearch.hppc; - -/** - * Marker interface for containers that can check if they contain a given object in at least time - * O(log n) and ideally in amortized constant time O(1). - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeLookupContainer.java") -public interface DoubleLookupContainer extends DoubleContainer { - public boolean contains(double e); -} diff --git a/src/main/java/com/carrotsearch/hppc/DoublePgmIndex.java b/src/main/java/com/carrotsearch/hppc/DoublePgmIndex.java deleted file mode 100755 index ee5ac213..00000000 --- a/src/main/java/com/carrotsearch/hppc/DoublePgmIndex.java +++ /dev/null @@ -1,600 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.DoubleCursor; -import com.carrotsearch.hppc.procedures.DoubleProcedure; -import java.util.Arrays; -import java.util.Iterator; - -/** - * Space-efficient index that enables fast rank/range search operations on a sorted sequence of - * double. - * - *

Implementation of the PGM-Index described at https://pgm.di.unipi.it/, based on the paper - * - *

- *   Paolo Ferragina and Giorgio Vinciguerra.
- *   The PGM-index: a fully-dynamic compressed learned index with provable worst-case bounds.
- *   PVLDB, 13(8): 1162-1175, 2020.
- * 
- * - * It provides {@code rank} and {@code range} search operations. {@code indexOf()} is faster than - * B+Tree, and the index is much more compact. {@code contains()} is between 4x to 7x slower than - * {@code IntHashSet#contains()}, but between 2.5x to 3x faster than {@link Arrays#binarySearch}. - * - *

Its compactness (40KB for 200MB of keys) makes it efficient for very large collections, the - * index fitting easily in the L2 cache. The {@code epsilon} parameter should be set according to - * the desired space-time trade-off. A smaller value makes the estimation more precise and the range - * smaller but at the cost of increased space usage. In practice, {@code epsilon} 64 is a good sweet - * spot. - * - *

Internally the index uses an optimal piecewise linear mapping from keys to their position in - * the sorted order. This mapping is represented as a sequence of linear models (segments) which are - * themselves recursively indexed by other piecewise linear mappings. - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypePgmIndex.java") -public class DoublePgmIndex implements Accountable { - - /** Empty immutable DoublePgmIndex. */ - public static final DoublePgmIndex EMPTY = new DoubleEmptyPgmIndex(); - - /** - * Epsilon approximation range when searching the list of keys. Controls the size of the returned - * search range, strictly greater than 0. It should be set according to the desired space-time - * trade-off. A smaller value makes the estimation more precise and the range smaller but at the - * cost of increased space usage. - * - *

With EPSILON=64 the benchmark with 200MB of keys shows that this PGM index requires only 2% - * additional memory on average (40KB). It depends on the distribution of the keys. This epsilon - * value is good even for 2MB of keys. With EPSILON=32: +5% speed, but 4x space (160KB). - */ - public static final int EPSILON = 64; - - /** - * Epsilon approximation range for the segments layers. Controls the size of the search range in - * the hierarchical segment lists, strictly greater than 0. - */ - public static final int EPSILON_RECURSIVE = 32; - - /** Size of a key, measured in {@link Integer#BYTES} because the key is stored in an int[]. */ - public static final int KEY_SIZE = - RamUsageEstimator.primitiveSizes.get(double.class) / Integer.BYTES; - - /** 2x {@link #KEY_SIZE}. */ - public static final int DOUBLE_KEY_SIZE = KEY_SIZE * 2; - - /** - * Data size of a segment, measured in {@link Integer#BYTES}, because segments are stored in an - * int[]. - */ - public static final int SEGMENT_DATA_SIZE = KEY_SIZE * 3; - - /** Initial value of the exponential jump when scanning out of the epsilon range. */ - public static final int BEYOND_EPSILON_JUMP = 16; - - /** - * The list of keys for which this index is built. It is sorted and may contain duplicate - * elements. - */ - public final DoubleArrayList keys; - - /** The size of the key set. That is, the number of distinct elements in {@link #keys}. */ - public final int size; - - /** The lowest key in {@link #keys}. */ - public final double firstKey; - - /** The highest key in {@link #keys}. */ - public final double lastKey; - - /** The epsilon range used to build this index. */ - public final int epsilon; - - /** The recursive epsilon range used to build this index. */ - public final int epsilonRecursive; - - /** The offsets in {@link #segmentData} of the first segment of each segment level. */ - public final int[] levelOffsets; - - /** The index data. It contains all the segments for all the levels. */ - public final int[] segmentData; - - private DoublePgmIndex( - DoubleArrayList keys, - int size, - int epsilon, - int epsilonRecursive, - int[] levelOffsets, - int[] segmentData) { - assert keys.size() > 0; - assert size > 0 && size <= keys.size(); - assert epsilon > 0; - assert epsilonRecursive > 0; - this.keys = keys; - this.size = size; - firstKey = keys.get(0); - lastKey = keys.get(keys.size() - 1); - this.epsilon = epsilon; - this.epsilonRecursive = epsilonRecursive; - this.levelOffsets = levelOffsets; - this.segmentData = segmentData; - } - - /** Empty set constructor. */ - private DoublePgmIndex() { - keys = new DoubleArrayList(0); - size = 0; - firstKey = 0d; - lastKey = 0d; - epsilon = 0; - epsilonRecursive = 0; - levelOffsets = new int[0]; - segmentData = levelOffsets; - } - - /** Returns the size of the key set. That is, the number of distinct elements in {@link #keys}. */ - public int size() { - return size; - } - - /** Returns whether this key set is empty. */ - public boolean isEmpty() { - return size() == 0; - } - - /** Returns whether this key set contains the given key. */ - public boolean contains(double key) { - return indexOf(key) >= 0; - } - - /** - * Searches the specified key, and returns its index in the element list. If multiple elements are - * equal to the specified key, there is no guarantee which one will be found. - * - * @return The index of the searched key if it is present; otherwise, {@code (-(insertion - * point) - 1)}. The insertion point is defined as the point at which the key would - * be inserted into the list: the index of the first element greater than the key, or {@link - * #keys}#{@code size()} if all the elements are less than the specified key. Note that this - * guarantees that the return value will be >= 0 if and only if the key is found. - */ - public int indexOf(double key) { - if (key < firstKey) { - return -1; - } - if (key > lastKey) { - return -keys.size() - 1; - } - final int[] segmentData = this.segmentData; - int segmentDataIndex = findSegment(key); - int nextIntercept = (int) getIntercept(segmentDataIndex + SEGMENT_DATA_SIZE, segmentData); - int index = - Math.min( - approximateIndex(key, segmentDataIndex, segmentData), - Math.min(nextIntercept, keys.size() - 1)); - assert index >= 0 && index < keys.size(); - double k = keys.get(index); - if (key < k) { - // Scan sequentially before the approximated index, within epsilon range. - final int fromIndex = Math.max(index - epsilon - 1, 0); - while (--index >= fromIndex) { - k = keys.get(index); - if (key > k) { - return -index - 2; - } - if ((Double.doubleToLongBits(key) == Double.doubleToLongBits(k))) { - return index; - } - } - // Continue scanning out of the epsilon range. - // This might happen in rare cases of precision error during the approximation - // computation for longs (we don't have long double 128 bits in Java). - // This might also happen in rare corner cases of large duplicate elements - // sequence at the epsilon range boundary. - index++; - int jump = BEYOND_EPSILON_JUMP; - do { - int loIndex = Math.max(index - jump, 0); - if (key >= keys.get(loIndex)) { - return Arrays.binarySearch(keys.buffer, loIndex, index, key); - } - index = loIndex; - jump <<= 1; - } while (index > 0); - return -1; - } else if ((Double.doubleToLongBits(key) == Double.doubleToLongBits(k))) { - return index; - } else { - // Scan sequentially after the approximated index, within epsilon range. - final int toIndex = Math.min(index + epsilon + 3, keys.size()); - while (++index < toIndex) { - k = keys.get(index); - if (key < k) { - return -index - 1; - } - if ((Double.doubleToLongBits(key) == Double.doubleToLongBits(k))) { - return index; - } - } - // Continue scanning out of the epsilon range. - int jump = BEYOND_EPSILON_JUMP; - do { - int hiIndex = Math.min(index + jump, keys.size()); - if (key <= keys.get(hiIndex)) { - return Arrays.binarySearch(keys.buffer, index, hiIndex, key); - } - index = hiIndex; - jump <<= 1; - } while (index < keys.size()); - return -keys.size() - 1; - } - } - - /** - * Returns, for any value {@code x}, the number of keys in the sorted list which are smaller than - * {@code x}. It is equal to {@link #indexOf} if {@code x} belongs to the list, or -{@link - * #indexOf}-1 otherwise. - * - *

If multiple elements are equal to the specified key, there is no guarantee which one will be - * found. - * - * @return The index of the searched key if it is present; otherwise, the {@code insertion point}. - * The insertion point is defined as the point at which the key would be inserted into - * the list: the index of the first element greater than the key, or {@link #keys}#{@code - * size()} if all the elements are less than the specified key. Note that this method always - * returns a value >= 0. - */ - public int rank(double x) { - int index = indexOf(x); - return index >= 0 ? index : -index - 1; - } - - /** - * Returns the number of keys in the list that are greater than or equal to {@code minKey} - * (inclusive), and less than or equal to {@code maxKey} (inclusive). - */ - public int rangeCardinality(double minKey, double maxKey) { - int fromIndex = rank(minKey); - int maxIndex = indexOf(maxKey); - int toIndex = maxIndex >= 0 ? maxIndex + 1 : -maxIndex - 1; - return Math.max(toIndex - fromIndex, 0); - } - - /** - * Returns an iterator over the keys in the list that are greater than or equal to {@code minKey} - * (inclusive), and less than or equal to {@code maxKey} (inclusive). - */ - public Iterator rangeIterator(double minKey, double maxKey) { - int fromIndex = rank(minKey); - return new RangeIterator(keys, fromIndex, maxKey); - } - - /** - * Applies {@code procedure} to the keys in the list that are greater than or equal to {@code - * minKey} (inclusive), and less than or equal to {@code maxKey} (inclusive). - */ - public T forEachInRange(T procedure, double minKey, double maxKey) { - final double[] buffer = keys.buffer; - double k; - for (int i = rank(minKey), size = keys.size(); i < size && (k = buffer[i]) <= maxKey; i++) { - procedure.apply(k); - } - return procedure; - } - - /** - * Estimates the allocated memory. It does not count the memory for the list of keys, only for the - * index itself. - */ - @Override - public long ramBytesAllocated() { - // int: size, epsilon, epsilonRecursive - // double: firstKey, lastKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 3 * Integer.BYTES - + 2L * KEY_SIZE * Integer.BYTES - // + keys.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(levelOffsets) - + RamUsageEstimator.shallowSizeOfArray(segmentData); - } - - /** - * Estimates the bytes that are actually used. It does not count the memory for the list of keys, - * only for the index itself. - */ - @Override - public long ramBytesUsed() { - // int: size, epsilon, epsilonRecursive - // double: firstKey, lastKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 3 * Integer.BYTES - + 2L * KEY_SIZE * Integer.BYTES - // + keys.ramBytesUsed() - + RamUsageEstimator.shallowSizeOfArray(levelOffsets) - + RamUsageEstimator.shallowSizeOfArray(segmentData); - } - - /** - * Finds the segment responsible for a given key, that is, the rightmost segment having its first - * key <= the searched key. - * - * @return the segment data index; or -1 if none. - */ - private int findSegment(double key) { - assert key >= firstKey && key <= lastKey; - final int epsilonRecursive = this.epsilonRecursive; - final int[] levelOffsets = this.levelOffsets; - final int[] segmentData = this.segmentData; - int level = levelOffsets.length - 1; - int segmentDataIndex = levelOffsets[level] * SEGMENT_DATA_SIZE; - while (--level >= 0) { - int nextIntercept = (int) getIntercept(segmentDataIndex + SEGMENT_DATA_SIZE, segmentData); - int index = Math.min(approximateIndex(key, segmentDataIndex, segmentData), nextIntercept); - assert index >= 0 && index <= levelOffsets[level + 1] - levelOffsets[level] - 1; - int sdIndex = (levelOffsets[level] + index) * SEGMENT_DATA_SIZE; - if (getKey(sdIndex, segmentData) <= key) { - // Scan sequentially segments after the approximated index, within the epsilon range. - final int levelNumSegments = levelOffsets[level + 1] - levelOffsets[level] - 1; - final int toIndex = Math.min(index + epsilonRecursive + 3, levelNumSegments); - while (index++ < toIndex && getKey(sdIndex + SEGMENT_DATA_SIZE, segmentData) <= key) { - sdIndex += SEGMENT_DATA_SIZE; - } - } else { - // Scan sequentially segments before the approximated index, within the epsilon range. - final int fromIndex = Math.max(index - epsilonRecursive - 1, 0); - while (index-- > fromIndex) { - sdIndex -= SEGMENT_DATA_SIZE; - if (getKey(sdIndex, segmentData) <= key) { - break; - } - } - } - segmentDataIndex = sdIndex; - } - assert segmentDataIndex >= 0; - return segmentDataIndex; - } - - private int approximateIndex(double key, int segmentDataIndex, int[] segmentData) { - long intercept = getIntercept(segmentDataIndex, segmentData); - double sKey = getKey(segmentDataIndex, segmentData); - double slope = getSlope(segmentDataIndex, segmentData); - int index = (int) (slope * ((double) key - sKey) + intercept); - return Math.max(index, 0); - } - - private static long getIntercept(int segmentDataIndex, int[] segmentData) { - return PgmIndexUtil.getIntercept(segmentDataIndex, segmentData, KEY_SIZE); - } - - private double getKey(int segmentDataIndex, int[] segmentData) { - return PgmIndexUtil.getKey(segmentDataIndex + KEY_SIZE, segmentData, 0d); - } - - private static double getSlope(int segmentDataIndex, int[] segmentData) { - return PgmIndexUtil.getSlope(segmentDataIndex + DOUBLE_KEY_SIZE, segmentData, KEY_SIZE); - } - - /** Empty immutable PGM Index. */ - private static class DoubleEmptyPgmIndex extends DoublePgmIndex { - - private final Iterator emptyIterator = new DoubleEmptyIterator(); - - @Override - public int indexOf(double key) { - return -1; - } - - @Override - public Iterator rangeIterator(double minKey, double maxKey) { - return emptyIterator; - } - - @Override - public T forEachInRange(T procedure, double minKey, double maxKey) { - return procedure; - } - - private static class DoubleEmptyIterator extends AbstractIterator { - @Override - protected DoubleCursor fetch() { - return done(); - } - } - } - - /** Iterator over a range of elements in a sorted array. */ - protected static class RangeIterator extends AbstractIterator { - private final double[] buffer; - private final int size; - private final DoubleCursor cursor; - private final double maxKey; - - /** Range iterator from {@code fromIndex} (inclusive) to {@code maxKey} (inclusive). */ - protected RangeIterator(DoubleArrayList keys, int fromIndex, double maxKey) { - this.buffer = keys.buffer; - this.size = keys.size(); - this.cursor = new DoubleCursor(); - this.cursor.index = fromIndex; - this.maxKey = maxKey; - } - - @Override - protected DoubleCursor fetch() { - if (cursor.index >= size) { - return done(); - } - cursor.value = buffer[cursor.index++]; - if (cursor.value > maxKey) { - cursor.index = size; - return done(); - } - return cursor; - } - } - - /** Builds a {@link DoublePgmIndex} on a provided sorted list of keys. */ - public static class DoubleBuilder implements PlaModel.SegmentConsumer, Accountable { - - protected DoubleArrayList keys; - protected int epsilon = EPSILON; - protected int epsilonRecursive = EPSILON_RECURSIVE; - protected PlaModel plam; - protected int size; - protected IntArrayList segmentData; - protected int numSegments; - - /** Sets the sorted list of keys to build the index for; duplicate elements are allowed. */ - public DoubleBuilder setSortedKeys(DoubleArrayList keys) { - this.keys = keys; - return this; - } - - /** Sets the sorted array of keys to build the index for; duplicate elements are allowed. */ - public DoubleBuilder setSortedKeys(double[] keys, int length) { - DoubleArrayList keyList = new DoubleArrayList(0); - keyList.buffer = keys; - keyList.elementsCount = length; - return setSortedKeys(keyList); - } - - /** Sets the epsilon range to use when learning the segments for the list of keys. */ - public DoubleBuilder setEpsilon(int epsilon) { - if (epsilon <= 0) { - throw new IllegalArgumentException("epsilon must be > 0"); - } - this.epsilon = epsilon; - return this; - } - - /** - * Sets the recursive epsilon range to use when learning the segments for the segment levels. - */ - public DoubleBuilder setEpsilonRecursive(int epsilonRecursive) { - if (epsilonRecursive <= 0) { - throw new IllegalArgumentException("epsilonRecursive must be > 0"); - } - this.epsilonRecursive = epsilonRecursive; - return this; - } - - /** Builds the {@link DoublePgmIndex}; or {@link #EMPTY} if there are no keys in the list. */ - public DoublePgmIndex build() { - if (keys == null || keys.size() == 0) { - return (DoublePgmIndex) EMPTY; - } - plam = new PlaModel(epsilon); - - int segmentsInitialCapacity = - Math.min( - Math.max(keys.size() / (2 * epsilon * epsilon) * SEGMENT_DATA_SIZE, 16), 1 << 19); - segmentData = new IntArrayList(segmentsInitialCapacity); - IntArrayList levelOffsets = new IntArrayList(16); - - int levelOffset = 0; - levelOffsets.add(levelOffset); - int levelNumSegments = buildFirstLevel(); - while (levelNumSegments > 1) { - int nextLevelOffset = numSegments; - levelOffsets.add(nextLevelOffset); - levelNumSegments = buildUpperLevel(levelOffset, levelNumSegments); - levelOffset = nextLevelOffset; - } - - int[] segmentDataFinal = segmentData.toArray(); - int[] levelOffsetsFinal = levelOffsets.toArray(); - return new DoublePgmIndex( - keys, size, epsilon, epsilonRecursive, levelOffsetsFinal, segmentDataFinal); - } - - private int buildFirstLevel() { - assert numSegments == 0; - int numKeys = keys.size(); - int size = 0; - double key = keys.get(0); - size++; - plam.addKey(key, 0, this); - for (int i = 1; i < numKeys; i++) { - double nextKey = keys.get(i); - if (!(Double.doubleToLongBits(nextKey) == Double.doubleToLongBits(key))) { - key = nextKey; - plam.addKey(key, i, this); - size++; - } - } - plam.finish(this); - addSentinelSegment(numKeys); - this.size = size; - return numSegments - 1; - } - - private int buildUpperLevel(int levelOffset, int levelNumSegments) { - plam.setEpsilon(epsilonRecursive); - assert numSegments > 0; - int initialNumSegments = numSegments; - int segmentDataIndex = levelOffset * SEGMENT_DATA_SIZE; - double key = getKey(segmentDataIndex, segmentData.buffer); - plam.addKey(key, 0, this); - for (int i = 1; i < levelNumSegments; i++) { - segmentDataIndex += SEGMENT_DATA_SIZE; - double nextKey = getKey(segmentDataIndex, segmentData.buffer); - if (!(Double.doubleToLongBits(nextKey) == Double.doubleToLongBits(key))) { - key = nextKey; - plam.addKey(key, i, this); - } - } - plam.finish(this); - addSentinelSegment(levelNumSegments); - return numSegments - initialNumSegments - 1; - } - - private double getKey(int segmentDataIndex, int[] segmentData) { - return PgmIndexUtil.getKey(segmentDataIndex + KEY_SIZE, segmentData, 0d); - } - - /** - * Adds a sentinel segment that is used to give a limit for the position approximation, but does - * not count in the number of segments per level. - */ - private void addSentinelSegment(int endIndex) { - // This sentinel segment is used in findSegment(). - accept(Double.MAX_VALUE, 0d, endIndex); - } - - @Override - public void accept(double firstKey, double slope, long intercept) { - PgmIndexUtil.addIntercept(intercept, segmentData, KEY_SIZE); - PgmIndexUtil.addKey((double) firstKey, segmentData); - PgmIndexUtil.addSlope(slope, segmentData, KEY_SIZE); - numSegments++; - assert segmentData.size() == numSegments * SEGMENT_DATA_SIZE; - } - - /** - * Estimates the allocated memory. It does not count the memory for the list of keys, only for - * the builder itself. - */ - @Override - public long ramBytesAllocated() { - // int: epsilon, epsilonRecursive, size, numSegments - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - // + keys.ramBytesAllocated() - + plam.ramBytesAllocated() - + segmentData.ramBytesAllocated(); - } - - /** - * Estimates the bytes that are actually used. It does not count the memory for the list of - * keys, only for the builder itself. - */ - @Override - public long ramBytesUsed() { - // int: epsilon, epsilonRecursive, size, numSegments - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - // + keys.ramBytesUsed() - + plam.ramBytesUsed() - + segmentData.ramBytesUsed(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/DoubleStack.java b/src/main/java/com/carrotsearch/hppc/DoubleStack.java deleted file mode 100755 index bf0a2219..00000000 --- a/src/main/java/com/carrotsearch/hppc/DoubleStack.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.DoubleCursor; - -/** - * A subclass of {@link DoubleArrayList} adding stack-related utility methods. The top of the stack - * is at the {@link #size()} - 1 element. - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeStack.java") -public class DoubleStack extends DoubleArrayList { - /** New instance with sane defaults. */ - public DoubleStack() { - super(); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public DoubleStack(int expectedElements) { - super(expectedElements); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public DoubleStack(int expectedElements, ArraySizingStrategy resizer) { - super(expectedElements, resizer); - } - - /** Create a stack by pushing all elements of another container to it. */ - public DoubleStack(DoubleContainer container) { - super(container); - } - - /** Adds one double to the stack. */ - public void push(double e1) { - ensureBufferSpace(1); - buffer[elementsCount++] = e1; - } - - /** Adds two doubles to the stack. */ - public void push(double e1, double e2) { - ensureBufferSpace(2); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - } - - /** Adds three doubles to the stack. */ - public void push(double e1, double e2, double e3) { - ensureBufferSpace(3); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - buffer[elementsCount++] = e3; - } - - /** Adds four doubles to the stack. */ - public void push(double e1, double e2, double e3, double e4) { - ensureBufferSpace(4); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - buffer[elementsCount++] = e3; - buffer[elementsCount++] = e4; - } - - /** Add a range of array elements to the stack. */ - public void push(double[] elements, int start, int len) { - assert start >= 0 && len >= 0; - - ensureBufferSpace(len); - System.arraycopy(elements, start, buffer, elementsCount, len); - elementsCount += len; - } - - /** - * Vararg-signature method for pushing elements at the top of the stack. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - */ - public final void push(double... elements) { - push(elements, 0, elements.length); - } - - /** Pushes all elements from another container to the top of the stack. */ - public int pushAll(DoubleContainer container) { - return addAll(container); - } - - /** Pushes all elements from another iterable to the top of the stack. */ - public int pushAll(Iterable iterable) { - return addAll(iterable); - } - - /** Discard an arbitrary number of elements from the top of the stack. */ - public void discard(int count) { - assert elementsCount >= count; - - elementsCount -= count; - } - - /** Discard the top element from the stack. */ - public void discard() { - assert elementsCount > 0; - - elementsCount--; - } - - /** Remove the top element from the stack and return it. */ - public double pop() { - return removeLast(); - } - - /** Peek at the top element on the stack. */ - public double peek() { - assert elementsCount > 0; - return buffer[elementsCount - 1]; - } - - /** Create a stack by pushing a variable number of arguments to it. */ - public static DoubleStack from(double... elements) { - final DoubleStack stack = new DoubleStack(elements.length); - stack.push(elements); - return stack; - } - - /** {@inheritDoc} */ - @Override - public DoubleStack clone() { - return (DoubleStack) super.clone(); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/FloatArrayDeque.java b/src/main/java/com/carrotsearch/hppc/FloatArrayDeque.java deleted file mode 100755 index e3d29082..00000000 --- a/src/main/java/com/carrotsearch/hppc/FloatArrayDeque.java +++ /dev/null @@ -1,776 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; - -import com.carrotsearch.hppc.cursors.FloatCursor; -import com.carrotsearch.hppc.predicates.FloatPredicate; -import com.carrotsearch.hppc.procedures.FloatProcedure; -import java.util.*; - -/** An array-backed {@link FloatDeque}. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeArrayDeque.java") -public class FloatArrayDeque extends AbstractFloatCollection - implements FloatDeque, Preallocable, Cloneable, Accountable { - - /** Reuse the same strategy instance. */ - private static final BoundedProportionalArraySizingStrategy DEFAULT_SIZING_STRATEGY = - BoundedProportionalArraySizingStrategy.DEFAULT_INSTANCE; - - /** Internal array for storing elements of the deque. */ - public float[] buffer = FloatArrayList.EMPTY_ARRAY; - - /** - * The index of the element at the head of the deque or an arbitrary number equal to tail if the - * deque is empty. - */ - public int head; - - /** The index at which the next element would be added to the tail of the deque. */ - public int tail; - - /** Buffer resizing strategy. */ - protected final ArraySizingStrategy resizer; - - /** New instance with sane defaults. */ - public FloatArrayDeque() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public FloatArrayDeque(int expectedElements) { - this(expectedElements, DEFAULT_SIZING_STRATEGY); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public FloatArrayDeque(int expectedElements, ArraySizingStrategy resizer) { - assert resizer != null; - this.resizer = resizer; - ensureCapacity(expectedElements); - } - - /** - * Creates a new deque from elements of another container, appending elements at the end of the - * deque in the iteration order. - */ - public FloatArrayDeque(FloatContainer container) { - this(container.size()); - addLast(container); - } - - /** {@inheritDoc} */ - @Override - public void addFirst(float e1) { - int h = oneLeft(head, buffer.length); - if (h == tail) { - ensureBufferSpace(1); - h = oneLeft(head, buffer.length); - } - buffer[head = h] = e1; - } - - /** - * Vararg-signature method for adding elements at the front of this deque. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - * - * @param elements The elements to add. - */ - public final void addFirst(float... elements) { - ensureBufferSpace(elements.length); - for (float k : elements) { - addFirst(k); - } - } - - /** - * Inserts all elements from the given container to the front of this deque. - * - * @param container The container to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addFirst(FloatContainer container) { - int size = container.size(); - ensureBufferSpace(size); - - for (FloatCursor cursor : container) { - addFirst(cursor.value); - } - - return size; - } - - /** - * Inserts all elements from the given iterable to the front of this deque. - * - * @param iterable The iterable to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addFirst(Iterable iterable) { - int size = 0; - for (FloatCursor cursor : iterable) { - addFirst(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public void addLast(float e1) { - int t = oneRight(tail, buffer.length); - if (head == t) { - ensureBufferSpace(1); - t = oneRight(tail, buffer.length); - } - buffer[tail] = e1; - tail = t; - } - - /** - * Vararg-signature method for adding elements at the end of this deque. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - * - * @param elements The elements to iterate over. - */ - public final void addLast(float... elements) { - ensureBufferSpace(1); - for (float k : elements) { - addLast(k); - } - } - - /** - * Inserts all elements from the given container to the end of this deque. - * - * @param container The container to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addLast(FloatContainer container) { - int size = container.size(); - ensureBufferSpace(size); - - for (FloatCursor cursor : container) { - addLast(cursor.value); - } - - return size; - } - - /** - * Inserts all elements from the given iterable to the end of this deque. - * - * @param iterable The iterable to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addLast(Iterable iterable) { - int size = 0; - for (FloatCursor cursor : iterable) { - addLast(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public float removeFirst() { - assert size() > 0 : "The deque is empty."; - - final float result = buffer[head]; - buffer[head] = 0f; - head = oneRight(head, buffer.length); - return result; - } - - /** {@inheritDoc} */ - @Override - public float removeLast() { - assert size() > 0 : "The deque is empty."; - - tail = oneLeft(tail, buffer.length); - final float result = buffer[tail]; - buffer[tail] = 0f; - return result; - } - - /** {@inheritDoc} */ - @Override - public float getFirst() { - assert size() > 0 : "The deque is empty."; - - return buffer[head]; - } - - /** {@inheritDoc} */ - @Override - public float getLast() { - assert size() > 0 : "The deque is empty."; - - return buffer[oneLeft(tail, buffer.length)]; - } - - /** {@inheritDoc} */ - @Override - public int removeFirst(float e1) { - final int index = bufferIndexOf(e1); - if (index >= 0) removeAtBufferIndex(index); - return index; - } - - /** - * Return the index of the first (counting from head) element equal to e1. The index - * points to the {@link #buffer} array. - * - * @param e1 The element to look for. - * @return Returns the index of the first element equal to e1 or -1 if - * not found. - */ - public int bufferIndexOf(float e1) { - final int last = tail; - final int bufLen = buffer.length; - for (int i = head; i != last; i = oneRight(i, bufLen)) { - if ((Float.floatToIntBits(e1) == Float.floatToIntBits(buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int removeLast(float e1) { - final int index = lastBufferIndexOf(e1); - if (index >= 0) { - removeAtBufferIndex(index); - } - return index; - } - - /** - * Return the index of the last (counting from tail) element equal to e1. The index - * points to the {@link #buffer} array. - * - * @param e1 The element to look for. - * @return Returns the index of the first element equal to e1 or -1 if - * not found. - */ - public int lastBufferIndexOf(float e1) { - final int bufLen = buffer.length; - final int last = oneLeft(head, bufLen); - for (int i = oneLeft(tail, bufLen); i != last; i = oneLeft(i, bufLen)) { - if ((Float.floatToIntBits(e1) == Float.floatToIntBits(buffer[i]))) return i; - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(float e1) { - int removed = 0; - final int last = tail; - final int bufLen = buffer.length; - int from, to; - for (from = to = head; from != last; from = oneRight(from, bufLen)) { - if ((Float.floatToIntBits(e1) == Float.floatToIntBits(buffer[from]))) { - buffer[from] = 0f; - removed++; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0f; - } - - to = oneRight(to, bufLen); - } - - tail = to; - return removed; - } - - /** - * Removes the element at index in the internal {#link {@link #buffer} array, - * returning its value. - * - * @param index Index of the element to remove. The index must be located between {@link #head} - * and {@link #tail} in modulo {@link #buffer} arithmetic. - */ - public void removeAtBufferIndex(int index) { - assert (head <= tail ? index >= head && index < tail : index >= head || index < tail) - : "Index out of range (head=" + head + ", tail=" + tail + ", index=" + index + ")."; - - // Cache fields in locals (hopefully moved to registers). - final float[] buffer = this.buffer; - final int bufLen = buffer.length; - final int lastIndex = bufLen - 1; - final int head = this.head; - final int tail = this.tail; - - final int leftChunk = Math.abs(index - head) % bufLen; - final int rightChunk = Math.abs(tail - index) % bufLen; - - if (leftChunk < rightChunk) { - if (index >= head) { - System.arraycopy(buffer, head, buffer, head + 1, leftChunk); - } else { - System.arraycopy(buffer, 0, buffer, 1, index); - buffer[0] = buffer[lastIndex]; - System.arraycopy(buffer, head, buffer, head + 1, lastIndex - head); - } - buffer[head] = 0f; - this.head = oneRight(head, bufLen); - } else { - if (index < tail) { - System.arraycopy(buffer, index + 1, buffer, index, rightChunk); - } else { - System.arraycopy(buffer, index + 1, buffer, index, lastIndex - index); - buffer[lastIndex] = buffer[0]; - System.arraycopy(buffer, 1, buffer, 0, tail); - } - buffer[tail] = 0f; - this.tail = oneLeft(tail, bufLen); - } - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int size() { - if (head <= tail) return tail - head; - else return (tail - head + buffer.length); - } - - /** - * {@inheritDoc} - * - *

The internal array buffers are not released as a result of this call. - * - * @see #release() - */ - @Override - public void clear() { - if (head < tail) { - Arrays.fill(buffer, head, tail, 0f); - } else { - Arrays.fill(buffer, 0, tail, 0f); - Arrays.fill(buffer, head, buffer.length, 0f); - } - this.head = tail = 0; - } - - /** Release internal buffers of this deque and reallocate with the default buffer. */ - public void release() { - this.head = tail = 0; - buffer = FloatArrayList.EMPTY_ARRAY; - ensureBufferSpace(0); - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - ensureBufferSpace(expectedElements - size()); - } - - /** - * Ensures the internal buffer has enough free slots to store expectedAdditions. - * Increases internal buffer size if needed. - */ - protected void ensureBufferSpace(int expectedAdditions) { - final int bufferLen = buffer.length; - final int elementsCount = size(); - - if (elementsCount + expectedAdditions >= bufferLen) { - final int emptySlot = 1; // deque invariant: always an empty slot. - final int newSize = resizer.grow(bufferLen, elementsCount + emptySlot, expectedAdditions); - assert newSize >= (elementsCount + expectedAdditions + emptySlot) - : "Resizer failed to" - + " return sensible new size: " - + newSize - + " <= " - + (elementsCount + expectedAdditions); - - try { - final float[] newBuffer = (new float[newSize]); - if (bufferLen > 0) { - toArray(newBuffer); - tail = elementsCount; - head = 0; - } - this.buffer = newBuffer; - } catch (OutOfMemoryError e) { - throw new BufferAllocationException( - "Not enough memory to allocate new buffers: %,d -> %,d", e, bufferLen, newSize); - } - } - } - - /** {@inheritDoc} */ - @Override - public float[] toArray() { - - final int size = size(); - return toArray((new float[size])); - } - - /** - * Copies elements of this deque to an array. The content of the target array is - * filled from index 0 (head of the queue) to index size() - 1 (tail of the queue). - * - * @param target The target array must be large enough to hold all elements. - * @return Returns the target argument for chaining. - */ - public float[] toArray(float[] target) { - assert target.length >= size() : "Target array must be >= " + size(); - - if (head < tail) { - // The contents is not wrapped around. Just copy. - System.arraycopy(buffer, head, target, 0, size()); - } else if (head > tail) { - // The contents is split. Merge elements from the following indexes: - // [head...buffer.length - 1][0, tail - 1] - final int rightCount = buffer.length - head; - System.arraycopy(buffer, head, target, 0, rightCount); - System.arraycopy(buffer, 0, target, rightCount, tail); - } - - return target; - } - - /** - * Clone this object. The returned clone will reuse the same hash function and array resizing - * strategy. - */ - @Override - public FloatArrayDeque clone() { - try { - - FloatArrayDeque cloned = (FloatArrayDeque) super.clone(); - cloned.buffer = buffer.clone(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Move one index to the left, wrapping around buffer. */ - protected static int oneLeft(int index, int modulus) { - if (index >= 1) { - return index - 1; - } - return modulus - 1; - } - - /** Move one index to the right, wrapping around buffer. */ - protected static int oneRight(int index, int modulus) { - if (index + 1 == modulus) { - return 0; - } - return index + 1; - } - - @Override - public long ramBytesAllocated() { - // int: head, tail - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES * 2 - + resizer.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(buffer); - } - - @Override - public long ramBytesUsed() { - // int: head, tail - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES * 2 - + resizer.ramBytesUsed() - + RamUsageEstimator.shallowUsedSizeOfArray(buffer, size()); - } - - /** An iterator implementation for {@link ObjectArrayDeque#iterator}. */ - private final class ValueIterator extends AbstractIterator { - private final FloatCursor cursor; - private int remaining; - - public ValueIterator() { - cursor = new FloatCursor(); - cursor.index = oneLeft(head, buffer.length); - this.remaining = size(); - } - - @Override - protected FloatCursor fetch() { - if (remaining == 0) { - return done(); - } - - remaining--; - cursor.value = buffer[cursor.index = oneRight(cursor.index, buffer.length)]; - return cursor; - } - } - - /** An iterator implementation for {@link ObjectArrayDeque#descendingIterator()}. */ - private final class DescendingValueIterator extends AbstractIterator { - private final FloatCursor cursor; - private int remaining; - - public DescendingValueIterator() { - cursor = new FloatCursor(); - cursor.index = tail; - this.remaining = size(); - } - - @Override - protected FloatCursor fetch() { - if (remaining == 0) return done(); - - remaining--; - cursor.value = buffer[cursor.index = oneLeft(cursor.index, buffer.length)]; - return cursor; - } - } - - /** - * Returns a cursor over the values of this deque (in head to tail order). The iterator is - * implemented as a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()} (to avoid boxing of primitive types). To read the current value (or index in - * the deque's buffer) use the cursor's public fields. An example is shown below. - * - *

-   * for (IntValueCursor c : intDeque) {
-   *   System.out.println("buffer index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator iterator() { - return new ValueIterator(); - } - - /** - * Returns a cursor over the values of this deque (in tail to head order). The iterator is - * implemented as a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()} (to avoid boxing of primitive types). To read the current value (or index in - * the deque's buffer) use the cursor's public fields. An example is shown below. - * - *
-   * for (Iterator<IntCursor> i = intDeque.descendingIterator(); i.hasNext();) {
-   *   final IntCursor c = i.next();
-   *   System.out.println("buffer index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator descendingIterator() { - return new DescendingValueIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - forEach(procedure, head, tail); - return procedure; - } - - /** - * Applies procedure to a slice of the deque, fromIndex, inclusive, to - * toIndex, exclusive. - */ - private void forEach(FloatProcedure procedure, int fromIndex, final int toIndex) { - final float[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - procedure.apply(buffer[i]); - } - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - int fromIndex = head; - int toIndex = tail; - - final float[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - if (!predicate.apply(buffer[i])) { - break; - } - } - - return predicate; - } - - /** Applies procedure to all elements of this deque, tail to head. */ - @Override - public T descendingForEach(T procedure) { - descendingForEach(procedure, head, tail); - return procedure; - } - - /** - * Applies procedure to a slice of the deque, toIndex, exclusive, down - * to fromIndex, inclusive. - */ - private void descendingForEach(FloatProcedure procedure, int fromIndex, final int toIndex) { - if (fromIndex == toIndex) return; - - final float[] buffer = this.buffer; - int i = toIndex; - do { - i = oneLeft(i, buffer.length); - procedure.apply(buffer[i]); - } while (i != fromIndex); - } - - /** {@inheritDoc} */ - @Override - public T descendingForEach(T predicate) { - descendingForEach(predicate, head, tail); - return predicate; - } - - /** - * Applies predicate to a slice of the deque, toIndex, exclusive, down - * to fromIndex, inclusive or until the predicate returns false. - */ - private void descendingForEach(FloatPredicate predicate, int fromIndex, final int toIndex) { - if (fromIndex == toIndex) return; - - final float[] buffer = this.buffer; - int i = toIndex; - do { - i = oneLeft(i, buffer.length); - if (!predicate.apply(buffer[i])) { - break; - } - } while (i != fromIndex); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(FloatPredicate predicate) { - final float[] buffer = this.buffer; - final int last = tail; - final int bufLen = buffer.length; - int removed = 0; - int from, to; - from = to = head; - try { - for (from = to = head; from != last; from = oneRight(from, bufLen)) { - if (predicate.apply(buffer[from])) { - buffer[from] = 0f; - removed++; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0f; - } - - to = oneRight(to, bufLen); - } - } finally { - // Keep the deque in consistent state even if the predicate throws an exception. - for (; from != last; from = oneRight(from, bufLen)) { - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0f; - } - - to = oneRight(to, bufLen); - } - tail = to; - } - - return removed; - } - - /** {@inheritDoc} */ - @Override - public boolean contains(float e) { - int fromIndex = head; - int toIndex = tail; - - final float[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - if ((Float.floatToIntBits(e) == Float.floatToIntBits(buffer[i]))) { - return true; - } - } - - return false; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = 1; - int fromIndex = head; - int toIndex = tail; - - final float[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - h = 31 * h + BitMixer.mix(this.buffer[i]); - } - return h; - } - - /** - * Returns true only if the other object is an instance of the same class and with - * the same elements. - */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Compare order-aligned elements against another {@link FloatDeque}. */ - protected boolean equalElements(FloatArrayDeque other) { - int max = size(); - if (other.size() != max) { - return false; - } - - Iterator i1 = this.iterator(); - Iterator i2 = other.iterator(); - - while (i1.hasNext() && i2.hasNext()) { - if (!(Float.floatToIntBits(i1.next().value) == Float.floatToIntBits(i2.next().value))) { - return false; - } - } - - return !i1.hasNext() && !i2.hasNext(); - } - - /** Create a new deque by pushing a variable number of arguments to the end of it. */ - public static FloatArrayDeque from(float... elements) { - final FloatArrayDeque coll = new FloatArrayDeque(elements.length); - coll.addLast(elements); - return coll; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/FloatArrayList.java b/src/main/java/com/carrotsearch/hppc/FloatArrayList.java deleted file mode 100755 index 0b82dc2a..00000000 --- a/src/main/java/com/carrotsearch/hppc/FloatArrayList.java +++ /dev/null @@ -1,579 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.FloatPredicate; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** An array-backed list of floats. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeArrayList.java") -public class FloatArrayList extends AbstractFloatCollection - implements FloatIndexedContainer, Preallocable, Cloneable, Accountable { - /** An immutable empty buffer (array). */ - public static final float[] EMPTY_ARRAY = new float[0]; - - ; - - /** Reuse the same strategy instance. */ - private static final BoundedProportionalArraySizingStrategy DEFAULT_SIZING_STRATEGY = - BoundedProportionalArraySizingStrategy.DEFAULT_INSTANCE; - - /** - * Internal array for storing the list. The array may be larger than the current size ({@link - * #size()}). - */ - public float[] buffer = EMPTY_ARRAY; - - /** Current number of elements stored in {@link #buffer}. */ - public int elementsCount; - - /** Buffer resizing strategy. */ - protected final ArraySizingStrategy resizer; - - /** New instance with sane defaults. */ - public FloatArrayList() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public FloatArrayList(int expectedElements) { - this(expectedElements, DEFAULT_SIZING_STRATEGY); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public FloatArrayList(int expectedElements, ArraySizingStrategy resizer) { - assert resizer != null; - this.resizer = resizer; - buffer = Arrays.copyOf(buffer, expectedElements); - } - - /** Creates a new list from the elements of another container in its iteration order. */ - public FloatArrayList(FloatContainer container) { - this(container.size()); - addAll(container); - } - - /** {@inheritDoc} */ - @Override - public void add(float e1) { - ensureBufferSpace(1); - buffer[elementsCount++] = e1; - } - - /** - * Appends two elements at the end of the list. To add more than two elements, use add - * (vararg-version) or access the buffer directly (tight loop). - */ - public void add(float e1, float e2) { - ensureBufferSpace(2); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - } - - /** Add all elements from a range of given array to the list. */ - public void add(float[] elements, int start, int length) { - assert length >= 0 : "Length must be >= 0"; - - ensureBufferSpace(length); - System.arraycopy(elements, start, buffer, elementsCount, length); - elementsCount += length; - } - - /** - * Vararg-signature method for adding elements at the end of the list. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - */ - public final void add(float... elements) { - add(elements, 0, elements.length); - } - - /** Adds all elements from another container. */ - public int addAll(FloatContainer container) { - final int size = container.size(); - ensureBufferSpace(size); - - for (FloatCursor cursor : container) { - add(cursor.value); - } - - return size; - } - - /** Adds all elements from another iterable. */ - public int addAll(Iterable iterable) { - int size = 0; - for (FloatCursor cursor : iterable) { - add(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public void insert(int index, float e1) { - assert (index >= 0 && index <= size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + "]."; - - ensureBufferSpace(1); - System.arraycopy(buffer, index, buffer, index + 1, elementsCount - index); - buffer[index] = e1; - elementsCount++; - } - - /** {@inheritDoc} */ - @Override - public float get(int index) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - return buffer[index]; - } - - /** {@inheritDoc} */ - @Override - public float set(int index, float e1) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - final float v = buffer[index]; - buffer[index] = e1; - return v; - } - - /** {@inheritDoc} */ - @Override - public float removeAt(int index) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - final float v = buffer[index]; - System.arraycopy(buffer, index + 1, buffer, index, --elementsCount - index); - - return v; - } - - /** {@inheritDoc} */ - @Override - public float removeLast() { - assert elementsCount > 0; - - final float v = buffer[--elementsCount]; - - return v; - } - - /** {@inheritDoc} */ - @Override - public void removeRange(int fromIndex, int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - System.arraycopy(buffer, toIndex, buffer, fromIndex, elementsCount - toIndex); - final int count = toIndex - fromIndex; - elementsCount -= count; - } - - /** {@inheritDoc} */ - @Override - public boolean removeElement(float e1) { - return removeFirst(e1) != -1; - } - - /** {@inheritDoc} */ - @Override - public int removeFirst(float e1) { - final int index = indexOf(e1); - if (index >= 0) removeAt(index); - return index; - } - - /** {@inheritDoc} */ - @Override - public int removeLast(float e1) { - final int index = lastIndexOf(e1); - if (index >= 0) removeAt(index); - return index; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(float e1) { - int to = 0; - for (int from = 0; from < elementsCount; from++) { - if ((Float.floatToIntBits(e1) == Float.floatToIntBits(buffer[from]))) { - continue; - } - if (to != from) { - buffer[to] = buffer[from]; - } - to++; - } - final int deleted = elementsCount - to; - this.elementsCount = to; - - return deleted; - } - - /** {@inheritDoc} */ - @Override - public boolean contains(float e1) { - return indexOf(e1) >= 0; - } - - /** {@inheritDoc} */ - @Override - public int indexOf(float e1) { - for (int i = 0; i < elementsCount; i++) { - if ((Float.floatToIntBits(e1) == Float.floatToIntBits(buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int lastIndexOf(float e1) { - for (int i = elementsCount - 1; i >= 0; i--) { - if ((Float.floatToIntBits(e1) == Float.floatToIntBits(buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return elementsCount == 0; - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - final int bufferLen = (buffer == null ? 0 : buffer.length); - if (expectedElements > bufferLen) { - ensureBufferSpace(expectedElements - size()); - } - } - - /** - * Ensures the internal buffer has enough free slots to store expectedAdditions. - * Increases internal buffer size if needed. - */ - protected void ensureBufferSpace(int expectedAdditions) { - final int bufferLen = (buffer == null ? 0 : buffer.length); - if (elementsCount + expectedAdditions > bufferLen) { - final int newSize = resizer.grow(bufferLen, elementsCount, expectedAdditions); - assert newSize >= elementsCount + expectedAdditions - : "Resizer failed to" - + " return sensible new size: " - + newSize - + " <= " - + (elementsCount + expectedAdditions); - - this.buffer = Arrays.copyOf(buffer, newSize); - } - } - - /** - * Truncate or expand the list to the new size. If the list is truncated, the buffer will not be - * reallocated (use {@link #trimToSize()} if you need a truncated buffer), but the truncated - * values will be reset to the default value (zero). If the list is expanded, the elements beyond - * the current size are initialized with JVM-defaults (zero or null values). - */ - public void resize(int newSize) { - if (newSize <= buffer.length) { - if (newSize < elementsCount) { - Arrays.fill(buffer, newSize, elementsCount, 0f); - } else { - Arrays.fill(buffer, elementsCount, newSize, 0f); - } - } else { - ensureCapacity(newSize); - } - this.elementsCount = newSize; - } - - /** {@inheritDoc} */ - @Override - public int size() { - return elementsCount; - } - - /** Trim the internal buffer to the current size. */ - public void trimToSize() { - if (size() != this.buffer.length) { - this.buffer = toArray(); - } - } - - /** - * Sets the number of stored elements to zero. Releases and initializes the internal storage array - * to default values. To clear the list without cleaning the buffer, simply set the {@link - * #elementsCount} field to zero. - */ - @Override - public void clear() { - Arrays.fill(buffer, 0, elementsCount, 0f); - this.elementsCount = 0; - } - - /** Sets the number of stored elements to zero and releases the internal storage array. */ - @Override - public void release() { - this.buffer = EMPTY_ARRAY; - this.elementsCount = 0; - } - - /** - * {@inheritDoc} - * - *

The returned array is sized to match exactly the number of elements of the stack. - */ - @Override - public float[] toArray() { - - return Arrays.copyOf(buffer, elementsCount); - } - - /** {@inheritDoc} */ - @Override - public FloatIndexedContainer sort() { - Arrays.sort(buffer, 0, elementsCount); - return this; - } - - /** {@inheritDoc} */ - @Override - public FloatIndexedContainer reverse() { - for (int i = 0, mid = elementsCount >> 1, j = elementsCount - 1; i < mid; i++, j--) { - float tmp = buffer[i]; - buffer[i] = buffer[j]; - buffer[j] = tmp; - } - return this; - } - - /** - * Clone this object. The returned clone will reuse the same hash function and array resizing - * strategy. - */ - @Override - public FloatArrayList clone() { - try { - - final FloatArrayList cloned = (FloatArrayList) super.clone(); - cloned.buffer = buffer.clone(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = 1, max = elementsCount; - for (int i = 0; i < max; i++) { - h = 31 * h + BitMixer.mix(this.buffer[i]); - } - return h; - } - - /** - * Returns true only if the other object is an instance of the same class and with - * the same elements. - */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Compare index-aligned elements against another {@link FloatIndexedContainer}. */ - protected boolean equalElements(FloatArrayList other) { - int max = size(); - if (other.size() != max) { - return false; - } - - for (int i = 0; i < max; i++) { - if (!(Float.floatToIntBits(get(i)) == Float.floatToIntBits(other.get(i)))) { - return false; - } - } - - return true; - } - - @Override - public long ramBytesAllocated() { - // int: elementsCount - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES - + resizer.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(buffer); - } - - @Override - public long ramBytesUsed() { - // int: elementsCount - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES - + resizer.ramBytesUsed() - + RamUsageEstimator.shallowUsedSizeOfArray(buffer, elementsCount); - } - - /** An iterator implementation for {@link FloatArrayList#iterator}. */ - static final class ValueIterator extends AbstractIterator { - private final FloatCursor cursor; - - private final float[] buffer; - private final int size; - - public ValueIterator(float[] buffer, int size) { - this.cursor = new FloatCursor(); - this.cursor.index = -1; - this.size = size; - this.buffer = buffer; - } - - @Override - protected FloatCursor fetch() { - if (cursor.index + 1 == size) return done(); - - cursor.value = buffer[++cursor.index]; - return cursor; - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new ValueIterator(buffer, size()); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - return forEach(procedure, 0, size()); - } - - /** - * Applies procedure to a slice of the list, fromIndex, inclusive, to - * toIndex, exclusive. - */ - public T forEach(T procedure, int fromIndex, final int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - final float[] buffer = this.buffer; - for (int i = fromIndex; i < toIndex; i++) { - procedure.apply(buffer[i]); - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(FloatPredicate predicate) { - final float[] buffer = this.buffer; - final int elementsCount = this.elementsCount; - int to = 0; - int from = 0; - try { - for (; from < elementsCount; from++) { - if (predicate.apply(buffer[from])) { - buffer[from] = 0f; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0f; - } - to++; - } - } finally { - // Keep the list in a consistent state, even if the predicate throws an exception. - for (; from < elementsCount; from++) { - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0f; - } - to++; - } - - this.elementsCount = to; - } - - return elementsCount - to; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - return forEach(predicate, 0, size()); - } - - /** - * Applies predicate to a slice of the list, fromIndex, inclusive, to - * toIndex, exclusive, or until predicate returns false. - */ - public T forEach(T predicate, int fromIndex, final int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - final float[] buffer = this.buffer; - for (int i = fromIndex; i < toIndex; i++) { - if (!predicate.apply(buffer[i])) break; - } - - return predicate; - } - - /** - * Create a list from a variable number of arguments or an array of float. The - * elements are copied from the argument to the internal buffer. - */ - public static FloatArrayList from(float... elements) { - final FloatArrayList list = new FloatArrayList(elements.length); - list.add(elements); - return list; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/FloatBufferVisualizer.java b/src/main/java/com/carrotsearch/hppc/FloatBufferVisualizer.java deleted file mode 100755 index d0c471c3..00000000 --- a/src/main/java/com/carrotsearch/hppc/FloatBufferVisualizer.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.carrotsearch.hppc; - -/** - * Reused buffer visualization routines. - * - * @see FloatSet#visualizeKeyDistribution(int) - * @see FloatVTypeMap#visualizeKeyDistribution(int) - */ -class FloatBufferVisualizer { - static String visualizeKeyDistribution(float[] buffer, int max, int characters) { - final StringBuilder b = new StringBuilder(); - final char[] chars = ".123456789X".toCharArray(); - for (int i = 1, start = -1; i <= characters; i++) { - int end = (int) ((long) i * max / characters); - - if (start + 1 <= end) { - int taken = 0; - int slots = 0; - for (int slot = start + 1; slot <= end; slot++, slots++) { - if (!(Float.floatToIntBits(buffer[slot]) == 0)) { - taken++; - } - } - b.append(chars[Math.min(chars.length - 1, taken * chars.length / slots)]); - start = end; - } - } - while (b.length() < characters) { - b.append(' '); - } - return b.toString(); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/FloatCollection.java b/src/main/java/com/carrotsearch/hppc/FloatCollection.java deleted file mode 100755 index 491fab8f..00000000 --- a/src/main/java/com/carrotsearch/hppc/FloatCollection.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.predicates.FloatPredicate; - -/** - * A collection allows basic, efficient operations on sets of elements (difference and - * intersection). - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeCollection.java") -public interface FloatCollection extends FloatContainer { - /** - * Removes all occurrences of e from this collection. - * - * @param e Element to be removed from this collection, if present. - * @return The number of removed elements as a result of this call. - */ - public int removeAll(float e); - - /** - * Removes all elements in this collection that are present in c. - * - * @return Returns the number of removed elements. - */ - public int removeAll(FloatLookupContainer c); - - /** - * Removes all elements in this collection for which the given predicate returns true - * . - * - * @return Returns the number of removed elements. - */ - public int removeAll(FloatPredicate predicate); - - /** - * Keeps all elements in this collection that are present in c. Runs in time - * proportional to the number of elements in this collection. Equivalent of sets intersection. - * - * @return Returns the number of removed elements. - */ - public int retainAll(FloatLookupContainer c); - - /** - * Keeps all elements in this collection for which the given predicate returns true. - * - * @return Returns the number of removed elements. - */ - public int retainAll(FloatPredicate predicate); - - /** - * Removes all elements from this collection. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); -} diff --git a/src/main/java/com/carrotsearch/hppc/FloatContainer.java b/src/main/java/com/carrotsearch/hppc/FloatContainer.java deleted file mode 100755 index 83b952bf..00000000 --- a/src/main/java/com/carrotsearch/hppc/FloatContainer.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.FloatCursor; -import com.carrotsearch.hppc.predicates.FloatPredicate; -import com.carrotsearch.hppc.procedures.FloatProcedure; -import java.util.Iterator; - -/** A generic container holding floats. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeContainer.java") -public interface FloatContainer extends Iterable { - /** - * Returns an iterator to a cursor traversing the collection. The order of traversal is not - * defined. More than one cursor may be active at a time. The behavior of iterators is undefined - * if structural changes are made to the underlying collection. - * - *

The iterator is implemented as a cursor and it returns the same cursor instance on - * every call to {@link Iterator#next()} (to avoid boxing of primitive types). To read the current - * list's value (or index in the list) use the cursor's public fields. An example is shown below. - * - *

-   * for (FloatCursor<float> c : container) {
-   *   System.out.println("index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator iterator(); - - /** - * Lookup a given element in the container. This operation has no speed guarantees (may be linear - * with respect to the size of this container). - * - * @return Returns true if this container has an element equal to e. - */ - public boolean contains(float e); - - /** - * Return the current number of elements in this container. The time for calculating the - * container's size may take O(n) time, although implementing classes should try to - * maintain the current size and return in constant time. - */ - public int size(); - - /** Shortcut for size() == 0. */ - public boolean isEmpty(); - - /** - * Copies all elements of this container to an array. - * - *

The returned array is always a copy, regardless of the storage used by the container. - */ - public float[] toArray(); - - /** - * Applies a procedure to all container elements. Returns the argument (any subclass - * of {@link FloatProcedure}. This lets the caller to call methods of the argument by chaining the - * call (even if the argument is an anonymous type) to retrieve computed values, for example - * (IntContainer): - * - *

-   * int count = container.forEach(new IntProcedure() {
-   *   int count; // this is a field declaration in an anonymous class.
-   *
-   *   public void apply(int value) {
-   *     count++;
-   *   }
-   * }).count;
-   * 
- */ - public T forEach(T procedure); - - /** - * Applies a predicate to container elements as long, as the predicate returns - * true. The iteration is interrupted otherwise. - */ - public T forEach(T predicate); -} diff --git a/src/main/java/com/carrotsearch/hppc/FloatDeque.java b/src/main/java/com/carrotsearch/hppc/FloatDeque.java deleted file mode 100755 index 240f3298..00000000 --- a/src/main/java/com/carrotsearch/hppc/FloatDeque.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.FloatCursor; -import com.carrotsearch.hppc.predicates.FloatPredicate; -import com.carrotsearch.hppc.procedures.FloatProcedure; -import java.util.Deque; -import java.util.Iterator; - -/** - * A linear collection that supports element insertion and removal at both ends. - * - * @see Deque - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeDeque.java") -public interface FloatDeque extends FloatCollection { - /** - * Removes the first element that equals e. - * - * @return The deleted element's index or -1 if the element was not found. - */ - public int removeFirst(float e); - - /** - * Removes the last element that equals e. - * - * @return The deleted element's index or -1 if the element was not found. - */ - public int removeLast(float e); - - /** Inserts the specified element at the front of this deque. */ - public void addFirst(float e); - - /** Inserts the specified element at the end of this deque. */ - public void addLast(float e); - - /** - * Retrieves and removes the first element of this deque. - * - * @return the head (first) element of this deque. - */ - public float removeFirst(); - - /** - * Retrieves and removes the last element of this deque. - * - * @return the tail of this deque. - */ - public float removeLast(); - - /** - * Retrieves the first element of this deque but does not remove it. - * - * @return the head of this deque. - */ - public float getFirst(); - - /** - * Retrieves the last element of this deque but does not remove it. - * - * @return the head of this deque. - */ - public float getLast(); - - /** - * @return An iterator over elements in this deque in tail-to-head order. - */ - public Iterator descendingIterator(); - - /** Applies a procedure to all elements in tail-to-head order. */ - public T descendingForEach(T procedure); - - /** - * Applies a predicate to container elements as long, as the predicate returns - * true. The iteration is interrupted otherwise. - */ - public T descendingForEach(T predicate); -} diff --git a/src/main/java/com/carrotsearch/hppc/FloatIndexedContainer.java b/src/main/java/com/carrotsearch/hppc/FloatIndexedContainer.java deleted file mode 100755 index f7beda6a..00000000 --- a/src/main/java/com/carrotsearch/hppc/FloatIndexedContainer.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.carrotsearch.hppc; - -import java.util.RandomAccess; - -/** - * An indexed container provides random access to elements based on an index. Indexes - * are zero-based. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeIndexedContainer.java") -public interface FloatIndexedContainer extends FloatCollection, RandomAccess { - /** - * Removes the first element that equals e1, returning whether an element has been - * removed. - */ - public boolean removeElement(float e1); - - /** - * Removes the first element that equals e1, returning its deleted position or - * -1 if the element was not found. - */ - public int removeFirst(float e1); - - /** - * Removes the last element that equals e1, returning its deleted position or - * -1 if the element was not found. - */ - public int removeLast(float e1); - - /** - * Returns the index of the first occurrence of the specified element in this list, or -1 if this - * list does not contain the element. - */ - public int indexOf(float e1); - - /** - * Returns the index of the last occurrence of the specified element in this list, or -1 if this - * list does not contain the element. - */ - public int lastIndexOf(float e1); - - /** Adds an element to the end of this container (the last index is incremented by one). */ - public void add(float e1); - - /** - * Inserts the specified element at the specified position in this list. - * - * @param index The index at which the element should be inserted, shifting any existing and - * subsequent elements to the right. - */ - public void insert(int index, float e1); - - /** - * Replaces the element at the specified position in this list with the specified element. - * - * @return Returns the previous value in the list. - */ - public float set(int index, float e1); - - /** - * @return Returns the element at index index from the list. - */ - public float get(int index); - - /** - * Removes the element at the specified position in this container and returns it. - * - * @see #removeFirst - * @see #removeLast - * @see #removeAll - */ - public float removeAt(int index); - - /** Removes and returns the last element of this container. This container must not be empty. */ - public float removeLast(); - - /** - * Removes from this container all of the elements with indexes between fromIndex, - * inclusive, and toIndex, exclusive. - */ - public void removeRange(int fromIndex, int toIndex); - - /** Returns this container elements as a stream. */ - - /** Sorts the elements in this container and returns this container. */ - public FloatIndexedContainer sort(); - - /** Reverses the elements in this container and returns this container. */ - public FloatIndexedContainer reverse(); -} diff --git a/src/main/java/com/carrotsearch/hppc/FloatLookupContainer.java b/src/main/java/com/carrotsearch/hppc/FloatLookupContainer.java deleted file mode 100755 index 558f763b..00000000 --- a/src/main/java/com/carrotsearch/hppc/FloatLookupContainer.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.carrotsearch.hppc; - -/** - * Marker interface for containers that can check if they contain a given object in at least time - * O(log n) and ideally in amortized constant time O(1). - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeLookupContainer.java") -public interface FloatLookupContainer extends FloatContainer { - public boolean contains(float e); -} diff --git a/src/main/java/com/carrotsearch/hppc/FloatPgmIndex.java b/src/main/java/com/carrotsearch/hppc/FloatPgmIndex.java deleted file mode 100755 index e14cb073..00000000 --- a/src/main/java/com/carrotsearch/hppc/FloatPgmIndex.java +++ /dev/null @@ -1,600 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.FloatCursor; -import com.carrotsearch.hppc.procedures.FloatProcedure; -import java.util.Arrays; -import java.util.Iterator; - -/** - * Space-efficient index that enables fast rank/range search operations on a sorted sequence of - * float. - * - *

Implementation of the PGM-Index described at https://pgm.di.unipi.it/, based on the paper - * - *

- *   Paolo Ferragina and Giorgio Vinciguerra.
- *   The PGM-index: a fully-dynamic compressed learned index with provable worst-case bounds.
- *   PVLDB, 13(8): 1162-1175, 2020.
- * 
- * - * It provides {@code rank} and {@code range} search operations. {@code indexOf()} is faster than - * B+Tree, and the index is much more compact. {@code contains()} is between 4x to 7x slower than - * {@code IntHashSet#contains()}, but between 2.5x to 3x faster than {@link Arrays#binarySearch}. - * - *

Its compactness (40KB for 200MB of keys) makes it efficient for very large collections, the - * index fitting easily in the L2 cache. The {@code epsilon} parameter should be set according to - * the desired space-time trade-off. A smaller value makes the estimation more precise and the range - * smaller but at the cost of increased space usage. In practice, {@code epsilon} 64 is a good sweet - * spot. - * - *

Internally the index uses an optimal piecewise linear mapping from keys to their position in - * the sorted order. This mapping is represented as a sequence of linear models (segments) which are - * themselves recursively indexed by other piecewise linear mappings. - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypePgmIndex.java") -public class FloatPgmIndex implements Accountable { - - /** Empty immutable FloatPgmIndex. */ - public static final FloatPgmIndex EMPTY = new FloatEmptyPgmIndex(); - - /** - * Epsilon approximation range when searching the list of keys. Controls the size of the returned - * search range, strictly greater than 0. It should be set according to the desired space-time - * trade-off. A smaller value makes the estimation more precise and the range smaller but at the - * cost of increased space usage. - * - *

With EPSILON=64 the benchmark with 200MB of keys shows that this PGM index requires only 2% - * additional memory on average (40KB). It depends on the distribution of the keys. This epsilon - * value is good even for 2MB of keys. With EPSILON=32: +5% speed, but 4x space (160KB). - */ - public static final int EPSILON = 64; - - /** - * Epsilon approximation range for the segments layers. Controls the size of the search range in - * the hierarchical segment lists, strictly greater than 0. - */ - public static final int EPSILON_RECURSIVE = 32; - - /** Size of a key, measured in {@link Integer#BYTES} because the key is stored in an int[]. */ - public static final int KEY_SIZE = - RamUsageEstimator.primitiveSizes.get(float.class) / Integer.BYTES; - - /** 2x {@link #KEY_SIZE}. */ - public static final int DOUBLE_KEY_SIZE = KEY_SIZE * 2; - - /** - * Data size of a segment, measured in {@link Integer#BYTES}, because segments are stored in an - * int[]. - */ - public static final int SEGMENT_DATA_SIZE = KEY_SIZE * 3; - - /** Initial value of the exponential jump when scanning out of the epsilon range. */ - public static final int BEYOND_EPSILON_JUMP = 16; - - /** - * The list of keys for which this index is built. It is sorted and may contain duplicate - * elements. - */ - public final FloatArrayList keys; - - /** The size of the key set. That is, the number of distinct elements in {@link #keys}. */ - public final int size; - - /** The lowest key in {@link #keys}. */ - public final float firstKey; - - /** The highest key in {@link #keys}. */ - public final float lastKey; - - /** The epsilon range used to build this index. */ - public final int epsilon; - - /** The recursive epsilon range used to build this index. */ - public final int epsilonRecursive; - - /** The offsets in {@link #segmentData} of the first segment of each segment level. */ - public final int[] levelOffsets; - - /** The index data. It contains all the segments for all the levels. */ - public final int[] segmentData; - - private FloatPgmIndex( - FloatArrayList keys, - int size, - int epsilon, - int epsilonRecursive, - int[] levelOffsets, - int[] segmentData) { - assert keys.size() > 0; - assert size > 0 && size <= keys.size(); - assert epsilon > 0; - assert epsilonRecursive > 0; - this.keys = keys; - this.size = size; - firstKey = keys.get(0); - lastKey = keys.get(keys.size() - 1); - this.epsilon = epsilon; - this.epsilonRecursive = epsilonRecursive; - this.levelOffsets = levelOffsets; - this.segmentData = segmentData; - } - - /** Empty set constructor. */ - private FloatPgmIndex() { - keys = new FloatArrayList(0); - size = 0; - firstKey = 0f; - lastKey = 0f; - epsilon = 0; - epsilonRecursive = 0; - levelOffsets = new int[0]; - segmentData = levelOffsets; - } - - /** Returns the size of the key set. That is, the number of distinct elements in {@link #keys}. */ - public int size() { - return size; - } - - /** Returns whether this key set is empty. */ - public boolean isEmpty() { - return size() == 0; - } - - /** Returns whether this key set contains the given key. */ - public boolean contains(float key) { - return indexOf(key) >= 0; - } - - /** - * Searches the specified key, and returns its index in the element list. If multiple elements are - * equal to the specified key, there is no guarantee which one will be found. - * - * @return The index of the searched key if it is present; otherwise, {@code (-(insertion - * point) - 1)}. The insertion point is defined as the point at which the key would - * be inserted into the list: the index of the first element greater than the key, or {@link - * #keys}#{@code size()} if all the elements are less than the specified key. Note that this - * guarantees that the return value will be >= 0 if and only if the key is found. - */ - public int indexOf(float key) { - if (key < firstKey) { - return -1; - } - if (key > lastKey) { - return -keys.size() - 1; - } - final int[] segmentData = this.segmentData; - int segmentDataIndex = findSegment(key); - int nextIntercept = (int) getIntercept(segmentDataIndex + SEGMENT_DATA_SIZE, segmentData); - int index = - Math.min( - approximateIndex(key, segmentDataIndex, segmentData), - Math.min(nextIntercept, keys.size() - 1)); - assert index >= 0 && index < keys.size(); - float k = keys.get(index); - if (key < k) { - // Scan sequentially before the approximated index, within epsilon range. - final int fromIndex = Math.max(index - epsilon - 1, 0); - while (--index >= fromIndex) { - k = keys.get(index); - if (key > k) { - return -index - 2; - } - if ((Float.floatToIntBits(key) == Float.floatToIntBits(k))) { - return index; - } - } - // Continue scanning out of the epsilon range. - // This might happen in rare cases of precision error during the approximation - // computation for longs (we don't have long double 128 bits in Java). - // This might also happen in rare corner cases of large duplicate elements - // sequence at the epsilon range boundary. - index++; - int jump = BEYOND_EPSILON_JUMP; - do { - int loIndex = Math.max(index - jump, 0); - if (key >= keys.get(loIndex)) { - return Arrays.binarySearch(keys.buffer, loIndex, index, key); - } - index = loIndex; - jump <<= 1; - } while (index > 0); - return -1; - } else if ((Float.floatToIntBits(key) == Float.floatToIntBits(k))) { - return index; - } else { - // Scan sequentially after the approximated index, within epsilon range. - final int toIndex = Math.min(index + epsilon + 3, keys.size()); - while (++index < toIndex) { - k = keys.get(index); - if (key < k) { - return -index - 1; - } - if ((Float.floatToIntBits(key) == Float.floatToIntBits(k))) { - return index; - } - } - // Continue scanning out of the epsilon range. - int jump = BEYOND_EPSILON_JUMP; - do { - int hiIndex = Math.min(index + jump, keys.size()); - if (key <= keys.get(hiIndex)) { - return Arrays.binarySearch(keys.buffer, index, hiIndex, key); - } - index = hiIndex; - jump <<= 1; - } while (index < keys.size()); - return -keys.size() - 1; - } - } - - /** - * Returns, for any value {@code x}, the number of keys in the sorted list which are smaller than - * {@code x}. It is equal to {@link #indexOf} if {@code x} belongs to the list, or -{@link - * #indexOf}-1 otherwise. - * - *

If multiple elements are equal to the specified key, there is no guarantee which one will be - * found. - * - * @return The index of the searched key if it is present; otherwise, the {@code insertion point}. - * The insertion point is defined as the point at which the key would be inserted into - * the list: the index of the first element greater than the key, or {@link #keys}#{@code - * size()} if all the elements are less than the specified key. Note that this method always - * returns a value >= 0. - */ - public int rank(float x) { - int index = indexOf(x); - return index >= 0 ? index : -index - 1; - } - - /** - * Returns the number of keys in the list that are greater than or equal to {@code minKey} - * (inclusive), and less than or equal to {@code maxKey} (inclusive). - */ - public int rangeCardinality(float minKey, float maxKey) { - int fromIndex = rank(minKey); - int maxIndex = indexOf(maxKey); - int toIndex = maxIndex >= 0 ? maxIndex + 1 : -maxIndex - 1; - return Math.max(toIndex - fromIndex, 0); - } - - /** - * Returns an iterator over the keys in the list that are greater than or equal to {@code minKey} - * (inclusive), and less than or equal to {@code maxKey} (inclusive). - */ - public Iterator rangeIterator(float minKey, float maxKey) { - int fromIndex = rank(minKey); - return new RangeIterator(keys, fromIndex, maxKey); - } - - /** - * Applies {@code procedure} to the keys in the list that are greater than or equal to {@code - * minKey} (inclusive), and less than or equal to {@code maxKey} (inclusive). - */ - public T forEachInRange(T procedure, float minKey, float maxKey) { - final float[] buffer = keys.buffer; - float k; - for (int i = rank(minKey), size = keys.size(); i < size && (k = buffer[i]) <= maxKey; i++) { - procedure.apply(k); - } - return procedure; - } - - /** - * Estimates the allocated memory. It does not count the memory for the list of keys, only for the - * index itself. - */ - @Override - public long ramBytesAllocated() { - // int: size, epsilon, epsilonRecursive - // float: firstKey, lastKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 3 * Integer.BYTES - + 2L * KEY_SIZE * Integer.BYTES - // + keys.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(levelOffsets) - + RamUsageEstimator.shallowSizeOfArray(segmentData); - } - - /** - * Estimates the bytes that are actually used. It does not count the memory for the list of keys, - * only for the index itself. - */ - @Override - public long ramBytesUsed() { - // int: size, epsilon, epsilonRecursive - // float: firstKey, lastKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 3 * Integer.BYTES - + 2L * KEY_SIZE * Integer.BYTES - // + keys.ramBytesUsed() - + RamUsageEstimator.shallowSizeOfArray(levelOffsets) - + RamUsageEstimator.shallowSizeOfArray(segmentData); - } - - /** - * Finds the segment responsible for a given key, that is, the rightmost segment having its first - * key <= the searched key. - * - * @return the segment data index; or -1 if none. - */ - private int findSegment(float key) { - assert key >= firstKey && key <= lastKey; - final int epsilonRecursive = this.epsilonRecursive; - final int[] levelOffsets = this.levelOffsets; - final int[] segmentData = this.segmentData; - int level = levelOffsets.length - 1; - int segmentDataIndex = levelOffsets[level] * SEGMENT_DATA_SIZE; - while (--level >= 0) { - int nextIntercept = (int) getIntercept(segmentDataIndex + SEGMENT_DATA_SIZE, segmentData); - int index = Math.min(approximateIndex(key, segmentDataIndex, segmentData), nextIntercept); - assert index >= 0 && index <= levelOffsets[level + 1] - levelOffsets[level] - 1; - int sdIndex = (levelOffsets[level] + index) * SEGMENT_DATA_SIZE; - if (getKey(sdIndex, segmentData) <= key) { - // Scan sequentially segments after the approximated index, within the epsilon range. - final int levelNumSegments = levelOffsets[level + 1] - levelOffsets[level] - 1; - final int toIndex = Math.min(index + epsilonRecursive + 3, levelNumSegments); - while (index++ < toIndex && getKey(sdIndex + SEGMENT_DATA_SIZE, segmentData) <= key) { - sdIndex += SEGMENT_DATA_SIZE; - } - } else { - // Scan sequentially segments before the approximated index, within the epsilon range. - final int fromIndex = Math.max(index - epsilonRecursive - 1, 0); - while (index-- > fromIndex) { - sdIndex -= SEGMENT_DATA_SIZE; - if (getKey(sdIndex, segmentData) <= key) { - break; - } - } - } - segmentDataIndex = sdIndex; - } - assert segmentDataIndex >= 0; - return segmentDataIndex; - } - - private int approximateIndex(float key, int segmentDataIndex, int[] segmentData) { - long intercept = getIntercept(segmentDataIndex, segmentData); - float sKey = getKey(segmentDataIndex, segmentData); - double slope = getSlope(segmentDataIndex, segmentData); - int index = (int) (slope * ((double) key - sKey) + intercept); - return Math.max(index, 0); - } - - private static long getIntercept(int segmentDataIndex, int[] segmentData) { - return PgmIndexUtil.getIntercept(segmentDataIndex, segmentData, KEY_SIZE); - } - - private float getKey(int segmentDataIndex, int[] segmentData) { - return PgmIndexUtil.getKey(segmentDataIndex + KEY_SIZE, segmentData, 0f); - } - - private static double getSlope(int segmentDataIndex, int[] segmentData) { - return PgmIndexUtil.getSlope(segmentDataIndex + DOUBLE_KEY_SIZE, segmentData, KEY_SIZE); - } - - /** Empty immutable PGM Index. */ - private static class FloatEmptyPgmIndex extends FloatPgmIndex { - - private final Iterator emptyIterator = new FloatEmptyIterator(); - - @Override - public int indexOf(float key) { - return -1; - } - - @Override - public Iterator rangeIterator(float minKey, float maxKey) { - return emptyIterator; - } - - @Override - public T forEachInRange(T procedure, float minKey, float maxKey) { - return procedure; - } - - private static class FloatEmptyIterator extends AbstractIterator { - @Override - protected FloatCursor fetch() { - return done(); - } - } - } - - /** Iterator over a range of elements in a sorted array. */ - protected static class RangeIterator extends AbstractIterator { - private final float[] buffer; - private final int size; - private final FloatCursor cursor; - private final float maxKey; - - /** Range iterator from {@code fromIndex} (inclusive) to {@code maxKey} (inclusive). */ - protected RangeIterator(FloatArrayList keys, int fromIndex, float maxKey) { - this.buffer = keys.buffer; - this.size = keys.size(); - this.cursor = new FloatCursor(); - this.cursor.index = fromIndex; - this.maxKey = maxKey; - } - - @Override - protected FloatCursor fetch() { - if (cursor.index >= size) { - return done(); - } - cursor.value = buffer[cursor.index++]; - if (cursor.value > maxKey) { - cursor.index = size; - return done(); - } - return cursor; - } - } - - /** Builds a {@link FloatPgmIndex} on a provided sorted list of keys. */ - public static class FloatBuilder implements PlaModel.SegmentConsumer, Accountable { - - protected FloatArrayList keys; - protected int epsilon = EPSILON; - protected int epsilonRecursive = EPSILON_RECURSIVE; - protected PlaModel plam; - protected int size; - protected IntArrayList segmentData; - protected int numSegments; - - /** Sets the sorted list of keys to build the index for; duplicate elements are allowed. */ - public FloatBuilder setSortedKeys(FloatArrayList keys) { - this.keys = keys; - return this; - } - - /** Sets the sorted array of keys to build the index for; duplicate elements are allowed. */ - public FloatBuilder setSortedKeys(float[] keys, int length) { - FloatArrayList keyList = new FloatArrayList(0); - keyList.buffer = keys; - keyList.elementsCount = length; - return setSortedKeys(keyList); - } - - /** Sets the epsilon range to use when learning the segments for the list of keys. */ - public FloatBuilder setEpsilon(int epsilon) { - if (epsilon <= 0) { - throw new IllegalArgumentException("epsilon must be > 0"); - } - this.epsilon = epsilon; - return this; - } - - /** - * Sets the recursive epsilon range to use when learning the segments for the segment levels. - */ - public FloatBuilder setEpsilonRecursive(int epsilonRecursive) { - if (epsilonRecursive <= 0) { - throw new IllegalArgumentException("epsilonRecursive must be > 0"); - } - this.epsilonRecursive = epsilonRecursive; - return this; - } - - /** Builds the {@link FloatPgmIndex}; or {@link #EMPTY} if there are no keys in the list. */ - public FloatPgmIndex build() { - if (keys == null || keys.size() == 0) { - return (FloatPgmIndex) EMPTY; - } - plam = new PlaModel(epsilon); - - int segmentsInitialCapacity = - Math.min( - Math.max(keys.size() / (2 * epsilon * epsilon) * SEGMENT_DATA_SIZE, 16), 1 << 19); - segmentData = new IntArrayList(segmentsInitialCapacity); - IntArrayList levelOffsets = new IntArrayList(16); - - int levelOffset = 0; - levelOffsets.add(levelOffset); - int levelNumSegments = buildFirstLevel(); - while (levelNumSegments > 1) { - int nextLevelOffset = numSegments; - levelOffsets.add(nextLevelOffset); - levelNumSegments = buildUpperLevel(levelOffset, levelNumSegments); - levelOffset = nextLevelOffset; - } - - int[] segmentDataFinal = segmentData.toArray(); - int[] levelOffsetsFinal = levelOffsets.toArray(); - return new FloatPgmIndex( - keys, size, epsilon, epsilonRecursive, levelOffsetsFinal, segmentDataFinal); - } - - private int buildFirstLevel() { - assert numSegments == 0; - int numKeys = keys.size(); - int size = 0; - float key = keys.get(0); - size++; - plam.addKey(key, 0, this); - for (int i = 1; i < numKeys; i++) { - float nextKey = keys.get(i); - if (!(Float.floatToIntBits(nextKey) == Float.floatToIntBits(key))) { - key = nextKey; - plam.addKey(key, i, this); - size++; - } - } - plam.finish(this); - addSentinelSegment(numKeys); - this.size = size; - return numSegments - 1; - } - - private int buildUpperLevel(int levelOffset, int levelNumSegments) { - plam.setEpsilon(epsilonRecursive); - assert numSegments > 0; - int initialNumSegments = numSegments; - int segmentDataIndex = levelOffset * SEGMENT_DATA_SIZE; - float key = getKey(segmentDataIndex, segmentData.buffer); - plam.addKey(key, 0, this); - for (int i = 1; i < levelNumSegments; i++) { - segmentDataIndex += SEGMENT_DATA_SIZE; - float nextKey = getKey(segmentDataIndex, segmentData.buffer); - if (!(Float.floatToIntBits(nextKey) == Float.floatToIntBits(key))) { - key = nextKey; - plam.addKey(key, i, this); - } - } - plam.finish(this); - addSentinelSegment(levelNumSegments); - return numSegments - initialNumSegments - 1; - } - - private float getKey(int segmentDataIndex, int[] segmentData) { - return PgmIndexUtil.getKey(segmentDataIndex + KEY_SIZE, segmentData, 0f); - } - - /** - * Adds a sentinel segment that is used to give a limit for the position approximation, but does - * not count in the number of segments per level. - */ - private void addSentinelSegment(int endIndex) { - // This sentinel segment is used in findSegment(). - accept(Double.MAX_VALUE, 0d, endIndex); - } - - @Override - public void accept(double firstKey, double slope, long intercept) { - PgmIndexUtil.addIntercept(intercept, segmentData, KEY_SIZE); - PgmIndexUtil.addKey((float) firstKey, segmentData); - PgmIndexUtil.addSlope(slope, segmentData, KEY_SIZE); - numSegments++; - assert segmentData.size() == numSegments * SEGMENT_DATA_SIZE; - } - - /** - * Estimates the allocated memory. It does not count the memory for the list of keys, only for - * the builder itself. - */ - @Override - public long ramBytesAllocated() { - // int: epsilon, epsilonRecursive, size, numSegments - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - // + keys.ramBytesAllocated() - + plam.ramBytesAllocated() - + segmentData.ramBytesAllocated(); - } - - /** - * Estimates the bytes that are actually used. It does not count the memory for the list of - * keys, only for the builder itself. - */ - @Override - public long ramBytesUsed() { - // int: epsilon, epsilonRecursive, size, numSegments - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - // + keys.ramBytesUsed() - + plam.ramBytesUsed() - + segmentData.ramBytesUsed(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/FloatStack.java b/src/main/java/com/carrotsearch/hppc/FloatStack.java deleted file mode 100755 index d2377178..00000000 --- a/src/main/java/com/carrotsearch/hppc/FloatStack.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.FloatCursor; - -/** - * A subclass of {@link FloatArrayList} adding stack-related utility methods. The top of the stack - * is at the {@link #size()} - 1 element. - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeStack.java") -public class FloatStack extends FloatArrayList { - /** New instance with sane defaults. */ - public FloatStack() { - super(); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public FloatStack(int expectedElements) { - super(expectedElements); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public FloatStack(int expectedElements, ArraySizingStrategy resizer) { - super(expectedElements, resizer); - } - - /** Create a stack by pushing all elements of another container to it. */ - public FloatStack(FloatContainer container) { - super(container); - } - - /** Adds one float to the stack. */ - public void push(float e1) { - ensureBufferSpace(1); - buffer[elementsCount++] = e1; - } - - /** Adds two floats to the stack. */ - public void push(float e1, float e2) { - ensureBufferSpace(2); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - } - - /** Adds three floats to the stack. */ - public void push(float e1, float e2, float e3) { - ensureBufferSpace(3); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - buffer[elementsCount++] = e3; - } - - /** Adds four floats to the stack. */ - public void push(float e1, float e2, float e3, float e4) { - ensureBufferSpace(4); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - buffer[elementsCount++] = e3; - buffer[elementsCount++] = e4; - } - - /** Add a range of array elements to the stack. */ - public void push(float[] elements, int start, int len) { - assert start >= 0 && len >= 0; - - ensureBufferSpace(len); - System.arraycopy(elements, start, buffer, elementsCount, len); - elementsCount += len; - } - - /** - * Vararg-signature method for pushing elements at the top of the stack. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - */ - public final void push(float... elements) { - push(elements, 0, elements.length); - } - - /** Pushes all elements from another container to the top of the stack. */ - public int pushAll(FloatContainer container) { - return addAll(container); - } - - /** Pushes all elements from another iterable to the top of the stack. */ - public int pushAll(Iterable iterable) { - return addAll(iterable); - } - - /** Discard an arbitrary number of elements from the top of the stack. */ - public void discard(int count) { - assert elementsCount >= count; - - elementsCount -= count; - } - - /** Discard the top element from the stack. */ - public void discard() { - assert elementsCount > 0; - - elementsCount--; - } - - /** Remove the top element from the stack and return it. */ - public float pop() { - return removeLast(); - } - - /** Peek at the top element on the stack. */ - public float peek() { - assert elementsCount > 0; - return buffer[elementsCount - 1]; - } - - /** Create a stack by pushing a variable number of arguments to it. */ - public static FloatStack from(float... elements) { - final FloatStack stack = new FloatStack(elements.length); - stack.push(elements); - return stack; - } - - /** {@inheritDoc} */ - @Override - public FloatStack clone() { - return (FloatStack) super.clone(); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/Generated.java b/src/main/java/com/carrotsearch/hppc/Generated.java deleted file mode 100755 index dd8c66d2..00000000 --- a/src/main/java/com/carrotsearch/hppc/Generated.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc; - -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.*; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -@Documented -@Retention(SOURCE) -@Target({PACKAGE, TYPE, ANNOTATION_TYPE, METHOD, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, PARAMETER}) -public @interface Generated { - /** - * The value element MUST have the name of the code generator. The recommended convention is to - * use the fully qualified name of the code generator. For example: com.acme.generator.CodeGen. - */ - String[] value(); - - /** Date when the source was generated. */ - String date() default ""; - - /** - * A place holder for any comments that the code generator may want to include in the generated - * code. - */ - String comments() default ""; -} diff --git a/src/main/java/com/carrotsearch/hppc/HashContainers.java b/src/main/java/com/carrotsearch/hppc/HashContainers.java deleted file mode 100755 index 7f4be809..00000000 --- a/src/main/java/com/carrotsearch/hppc/HashContainers.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc; - -import java.util.concurrent.atomic.AtomicInteger; - -public final class HashContainers { - /** - * Maximum array size for hash containers (power-of-two and still allocable in Java, not a - * negative int). - */ - public static final int MAX_HASH_ARRAY_LENGTH = 0x80000000 >>> 1; - - /** Minimum hash buffer size. */ - public static final int MIN_HASH_ARRAY_LENGTH = 4; - - /** Default load factor. */ - public static final float DEFAULT_LOAD_FACTOR = 0.75f; - - /** Minimal sane load factor (99 empty slots per 100). */ - public static final float MIN_LOAD_FACTOR = 1 / 100.0f; - - /** Maximum sane load factor (1 empty slot per 100). */ - public static final float MAX_LOAD_FACTOR = 99 / 100.0f; - - private static final AtomicInteger ITERATION_SEED = new AtomicInteger(); - - /** - * Compute and return the maximum number of elements (inclusive) that can be stored in a hash - * container for a given load factor. - */ - public static int maxElements(double loadFactor) { - checkLoadFactor(loadFactor, 0, 1); - return expandAtCount(MAX_HASH_ARRAY_LENGTH, loadFactor) - 1; - } - - /** */ - static int minBufferSize(int elements, double loadFactor) { - if (elements < 0) { - throw new IllegalArgumentException("Number of elements must be >= 0: " + elements); - } - - long length = (long) Math.ceil(elements / loadFactor); - if (length == elements) { - length++; - } - length = Math.max(MIN_HASH_ARRAY_LENGTH, BitUtil.nextHighestPowerOfTwo(length)); - - if (length > MAX_HASH_ARRAY_LENGTH) { - throw new BufferAllocationException( - "Maximum array size exceeded for this load factor (elements: %d, load factor: %f)", - elements, loadFactor); - } - - return (int) length; - } - - /** */ - static int nextBufferSize(int arraySize, int elements, double loadFactor) { - assert checkPowerOfTwo(arraySize); - if (arraySize == MAX_HASH_ARRAY_LENGTH) { - throw new BufferAllocationException( - "Maximum array size exceeded for this load factor (elements: %d, load factor: %f)", - elements, loadFactor); - } - - return (int) arraySize << 1; - } - - /** */ - static int expandAtCount(int arraySize, double loadFactor) { - assert checkPowerOfTwo(arraySize); - // Take care of hash container invariant (there has to be at least one empty slot to ensure - // the lookup loop finds either the element or an empty slot). - return Math.min(arraySize - 1, (int) Math.ceil(arraySize * loadFactor)); - } - - /** */ - static void checkLoadFactor( - double loadFactor, double minAllowedInclusive, double maxAllowedInclusive) { - if (loadFactor < minAllowedInclusive || loadFactor > maxAllowedInclusive) { - throw new BufferAllocationException( - "The load factor should be in range [%.2f, %.2f]: %f", - minAllowedInclusive, maxAllowedInclusive, loadFactor); - } - } - - /** */ - static boolean checkPowerOfTwo(int arraySize) { - // These are internals, we can just assert without retrying. - assert arraySize > 1; - assert BitUtil.nextHighestPowerOfTwo(arraySize) == arraySize; - return true; - } - - /** Provides the next hash iteration order seed. It is simply an incrementing atomic counter. */ - static int nextIterationSeed() { - return ITERATION_SEED.incrementAndGet(); - } - - /** Computes a hash iteration order increment based on the provided seed. */ - static int iterationIncrement(int seed) { - return 29 + ((seed & 7) << 1); // Small odd integer. - } -} diff --git a/src/main/java/com/carrotsearch/hppc/IntArrayDeque.java b/src/main/java/com/carrotsearch/hppc/IntArrayDeque.java deleted file mode 100755 index 06983d90..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntArrayDeque.java +++ /dev/null @@ -1,776 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; - -import com.carrotsearch.hppc.cursors.IntCursor; -import com.carrotsearch.hppc.predicates.IntPredicate; -import com.carrotsearch.hppc.procedures.IntProcedure; -import java.util.*; - -/** An array-backed {@link IntDeque}. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeArrayDeque.java") -public class IntArrayDeque extends AbstractIntCollection - implements IntDeque, Preallocable, Cloneable, Accountable { - - /** Reuse the same strategy instance. */ - private static final BoundedProportionalArraySizingStrategy DEFAULT_SIZING_STRATEGY = - BoundedProportionalArraySizingStrategy.DEFAULT_INSTANCE; - - /** Internal array for storing elements of the deque. */ - public int[] buffer = IntArrayList.EMPTY_ARRAY; - - /** - * The index of the element at the head of the deque or an arbitrary number equal to tail if the - * deque is empty. - */ - public int head; - - /** The index at which the next element would be added to the tail of the deque. */ - public int tail; - - /** Buffer resizing strategy. */ - protected final ArraySizingStrategy resizer; - - /** New instance with sane defaults. */ - public IntArrayDeque() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public IntArrayDeque(int expectedElements) { - this(expectedElements, DEFAULT_SIZING_STRATEGY); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public IntArrayDeque(int expectedElements, ArraySizingStrategy resizer) { - assert resizer != null; - this.resizer = resizer; - ensureCapacity(expectedElements); - } - - /** - * Creates a new deque from elements of another container, appending elements at the end of the - * deque in the iteration order. - */ - public IntArrayDeque(IntContainer container) { - this(container.size()); - addLast(container); - } - - /** {@inheritDoc} */ - @Override - public void addFirst(int e1) { - int h = oneLeft(head, buffer.length); - if (h == tail) { - ensureBufferSpace(1); - h = oneLeft(head, buffer.length); - } - buffer[head = h] = e1; - } - - /** - * Vararg-signature method for adding elements at the front of this deque. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - * - * @param elements The elements to add. - */ - public final void addFirst(int... elements) { - ensureBufferSpace(elements.length); - for (int k : elements) { - addFirst(k); - } - } - - /** - * Inserts all elements from the given container to the front of this deque. - * - * @param container The container to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addFirst(IntContainer container) { - int size = container.size(); - ensureBufferSpace(size); - - for (IntCursor cursor : container) { - addFirst(cursor.value); - } - - return size; - } - - /** - * Inserts all elements from the given iterable to the front of this deque. - * - * @param iterable The iterable to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addFirst(Iterable iterable) { - int size = 0; - for (IntCursor cursor : iterable) { - addFirst(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public void addLast(int e1) { - int t = oneRight(tail, buffer.length); - if (head == t) { - ensureBufferSpace(1); - t = oneRight(tail, buffer.length); - } - buffer[tail] = e1; - tail = t; - } - - /** - * Vararg-signature method for adding elements at the end of this deque. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - * - * @param elements The elements to iterate over. - */ - public final void addLast(int... elements) { - ensureBufferSpace(1); - for (int k : elements) { - addLast(k); - } - } - - /** - * Inserts all elements from the given container to the end of this deque. - * - * @param container The container to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addLast(IntContainer container) { - int size = container.size(); - ensureBufferSpace(size); - - for (IntCursor cursor : container) { - addLast(cursor.value); - } - - return size; - } - - /** - * Inserts all elements from the given iterable to the end of this deque. - * - * @param iterable The iterable to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addLast(Iterable iterable) { - int size = 0; - for (IntCursor cursor : iterable) { - addLast(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public int removeFirst() { - assert size() > 0 : "The deque is empty."; - - final int result = buffer[head]; - buffer[head] = 0; - head = oneRight(head, buffer.length); - return result; - } - - /** {@inheritDoc} */ - @Override - public int removeLast() { - assert size() > 0 : "The deque is empty."; - - tail = oneLeft(tail, buffer.length); - final int result = buffer[tail]; - buffer[tail] = 0; - return result; - } - - /** {@inheritDoc} */ - @Override - public int getFirst() { - assert size() > 0 : "The deque is empty."; - - return buffer[head]; - } - - /** {@inheritDoc} */ - @Override - public int getLast() { - assert size() > 0 : "The deque is empty."; - - return buffer[oneLeft(tail, buffer.length)]; - } - - /** {@inheritDoc} */ - @Override - public int removeFirst(int e1) { - final int index = bufferIndexOf(e1); - if (index >= 0) removeAtBufferIndex(index); - return index; - } - - /** - * Return the index of the first (counting from head) element equal to e1. The index - * points to the {@link #buffer} array. - * - * @param e1 The element to look for. - * @return Returns the index of the first element equal to e1 or -1 if - * not found. - */ - public int bufferIndexOf(int e1) { - final int last = tail; - final int bufLen = buffer.length; - for (int i = head; i != last; i = oneRight(i, bufLen)) { - if (((e1) == (buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int removeLast(int e1) { - final int index = lastBufferIndexOf(e1); - if (index >= 0) { - removeAtBufferIndex(index); - } - return index; - } - - /** - * Return the index of the last (counting from tail) element equal to e1. The index - * points to the {@link #buffer} array. - * - * @param e1 The element to look for. - * @return Returns the index of the first element equal to e1 or -1 if - * not found. - */ - public int lastBufferIndexOf(int e1) { - final int bufLen = buffer.length; - final int last = oneLeft(head, bufLen); - for (int i = oneLeft(tail, bufLen); i != last; i = oneLeft(i, bufLen)) { - if (((e1) == (buffer[i]))) return i; - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(int e1) { - int removed = 0; - final int last = tail; - final int bufLen = buffer.length; - int from, to; - for (from = to = head; from != last; from = oneRight(from, bufLen)) { - if (((e1) == (buffer[from]))) { - buffer[from] = 0; - removed++; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0; - } - - to = oneRight(to, bufLen); - } - - tail = to; - return removed; - } - - /** - * Removes the element at index in the internal {#link {@link #buffer} array, - * returning its value. - * - * @param index Index of the element to remove. The index must be located between {@link #head} - * and {@link #tail} in modulo {@link #buffer} arithmetic. - */ - public void removeAtBufferIndex(int index) { - assert (head <= tail ? index >= head && index < tail : index >= head || index < tail) - : "Index out of range (head=" + head + ", tail=" + tail + ", index=" + index + ")."; - - // Cache fields in locals (hopefully moved to registers). - final int[] buffer = this.buffer; - final int bufLen = buffer.length; - final int lastIndex = bufLen - 1; - final int head = this.head; - final int tail = this.tail; - - final int leftChunk = Math.abs(index - head) % bufLen; - final int rightChunk = Math.abs(tail - index) % bufLen; - - if (leftChunk < rightChunk) { - if (index >= head) { - System.arraycopy(buffer, head, buffer, head + 1, leftChunk); - } else { - System.arraycopy(buffer, 0, buffer, 1, index); - buffer[0] = buffer[lastIndex]; - System.arraycopy(buffer, head, buffer, head + 1, lastIndex - head); - } - buffer[head] = 0; - this.head = oneRight(head, bufLen); - } else { - if (index < tail) { - System.arraycopy(buffer, index + 1, buffer, index, rightChunk); - } else { - System.arraycopy(buffer, index + 1, buffer, index, lastIndex - index); - buffer[lastIndex] = buffer[0]; - System.arraycopy(buffer, 1, buffer, 0, tail); - } - buffer[tail] = 0; - this.tail = oneLeft(tail, bufLen); - } - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int size() { - if (head <= tail) return tail - head; - else return (tail - head + buffer.length); - } - - /** - * {@inheritDoc} - * - *

The internal array buffers are not released as a result of this call. - * - * @see #release() - */ - @Override - public void clear() { - if (head < tail) { - Arrays.fill(buffer, head, tail, 0); - } else { - Arrays.fill(buffer, 0, tail, 0); - Arrays.fill(buffer, head, buffer.length, 0); - } - this.head = tail = 0; - } - - /** Release internal buffers of this deque and reallocate with the default buffer. */ - public void release() { - this.head = tail = 0; - buffer = IntArrayList.EMPTY_ARRAY; - ensureBufferSpace(0); - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - ensureBufferSpace(expectedElements - size()); - } - - /** - * Ensures the internal buffer has enough free slots to store expectedAdditions. - * Increases internal buffer size if needed. - */ - protected void ensureBufferSpace(int expectedAdditions) { - final int bufferLen = buffer.length; - final int elementsCount = size(); - - if (elementsCount + expectedAdditions >= bufferLen) { - final int emptySlot = 1; // deque invariant: always an empty slot. - final int newSize = resizer.grow(bufferLen, elementsCount + emptySlot, expectedAdditions); - assert newSize >= (elementsCount + expectedAdditions + emptySlot) - : "Resizer failed to" - + " return sensible new size: " - + newSize - + " <= " - + (elementsCount + expectedAdditions); - - try { - final int[] newBuffer = (new int[newSize]); - if (bufferLen > 0) { - toArray(newBuffer); - tail = elementsCount; - head = 0; - } - this.buffer = newBuffer; - } catch (OutOfMemoryError e) { - throw new BufferAllocationException( - "Not enough memory to allocate new buffers: %,d -> %,d", e, bufferLen, newSize); - } - } - } - - /** {@inheritDoc} */ - @Override - public int[] toArray() { - - final int size = size(); - return toArray((new int[size])); - } - - /** - * Copies elements of this deque to an array. The content of the target array is - * filled from index 0 (head of the queue) to index size() - 1 (tail of the queue). - * - * @param target The target array must be large enough to hold all elements. - * @return Returns the target argument for chaining. - */ - public int[] toArray(int[] target) { - assert target.length >= size() : "Target array must be >= " + size(); - - if (head < tail) { - // The contents is not wrapped around. Just copy. - System.arraycopy(buffer, head, target, 0, size()); - } else if (head > tail) { - // The contents is split. Merge elements from the following indexes: - // [head...buffer.length - 1][0, tail - 1] - final int rightCount = buffer.length - head; - System.arraycopy(buffer, head, target, 0, rightCount); - System.arraycopy(buffer, 0, target, rightCount, tail); - } - - return target; - } - - /** - * Clone this object. The returned clone will reuse the same hash function and array resizing - * strategy. - */ - @Override - public IntArrayDeque clone() { - try { - - IntArrayDeque cloned = (IntArrayDeque) super.clone(); - cloned.buffer = buffer.clone(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Move one index to the left, wrapping around buffer. */ - protected static int oneLeft(int index, int modulus) { - if (index >= 1) { - return index - 1; - } - return modulus - 1; - } - - /** Move one index to the right, wrapping around buffer. */ - protected static int oneRight(int index, int modulus) { - if (index + 1 == modulus) { - return 0; - } - return index + 1; - } - - @Override - public long ramBytesAllocated() { - // int: head, tail - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES * 2 - + resizer.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(buffer); - } - - @Override - public long ramBytesUsed() { - // int: head, tail - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES * 2 - + resizer.ramBytesUsed() - + RamUsageEstimator.shallowUsedSizeOfArray(buffer, size()); - } - - /** An iterator implementation for {@link ObjectArrayDeque#iterator}. */ - private final class ValueIterator extends AbstractIterator { - private final IntCursor cursor; - private int remaining; - - public ValueIterator() { - cursor = new IntCursor(); - cursor.index = oneLeft(head, buffer.length); - this.remaining = size(); - } - - @Override - protected IntCursor fetch() { - if (remaining == 0) { - return done(); - } - - remaining--; - cursor.value = buffer[cursor.index = oneRight(cursor.index, buffer.length)]; - return cursor; - } - } - - /** An iterator implementation for {@link ObjectArrayDeque#descendingIterator()}. */ - private final class DescendingValueIterator extends AbstractIterator { - private final IntCursor cursor; - private int remaining; - - public DescendingValueIterator() { - cursor = new IntCursor(); - cursor.index = tail; - this.remaining = size(); - } - - @Override - protected IntCursor fetch() { - if (remaining == 0) return done(); - - remaining--; - cursor.value = buffer[cursor.index = oneLeft(cursor.index, buffer.length)]; - return cursor; - } - } - - /** - * Returns a cursor over the values of this deque (in head to tail order). The iterator is - * implemented as a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()} (to avoid boxing of primitive types). To read the current value (or index in - * the deque's buffer) use the cursor's public fields. An example is shown below. - * - *

-   * for (IntValueCursor c : intDeque) {
-   *   System.out.println("buffer index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator iterator() { - return new ValueIterator(); - } - - /** - * Returns a cursor over the values of this deque (in tail to head order). The iterator is - * implemented as a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()} (to avoid boxing of primitive types). To read the current value (or index in - * the deque's buffer) use the cursor's public fields. An example is shown below. - * - *
-   * for (Iterator<IntCursor> i = intDeque.descendingIterator(); i.hasNext();) {
-   *   final IntCursor c = i.next();
-   *   System.out.println("buffer index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator descendingIterator() { - return new DescendingValueIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - forEach(procedure, head, tail); - return procedure; - } - - /** - * Applies procedure to a slice of the deque, fromIndex, inclusive, to - * toIndex, exclusive. - */ - private void forEach(IntProcedure procedure, int fromIndex, final int toIndex) { - final int[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - procedure.apply(buffer[i]); - } - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - int fromIndex = head; - int toIndex = tail; - - final int[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - if (!predicate.apply(buffer[i])) { - break; - } - } - - return predicate; - } - - /** Applies procedure to all elements of this deque, tail to head. */ - @Override - public T descendingForEach(T procedure) { - descendingForEach(procedure, head, tail); - return procedure; - } - - /** - * Applies procedure to a slice of the deque, toIndex, exclusive, down - * to fromIndex, inclusive. - */ - private void descendingForEach(IntProcedure procedure, int fromIndex, final int toIndex) { - if (fromIndex == toIndex) return; - - final int[] buffer = this.buffer; - int i = toIndex; - do { - i = oneLeft(i, buffer.length); - procedure.apply(buffer[i]); - } while (i != fromIndex); - } - - /** {@inheritDoc} */ - @Override - public T descendingForEach(T predicate) { - descendingForEach(predicate, head, tail); - return predicate; - } - - /** - * Applies predicate to a slice of the deque, toIndex, exclusive, down - * to fromIndex, inclusive or until the predicate returns false. - */ - private void descendingForEach(IntPredicate predicate, int fromIndex, final int toIndex) { - if (fromIndex == toIndex) return; - - final int[] buffer = this.buffer; - int i = toIndex; - do { - i = oneLeft(i, buffer.length); - if (!predicate.apply(buffer[i])) { - break; - } - } while (i != fromIndex); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntPredicate predicate) { - final int[] buffer = this.buffer; - final int last = tail; - final int bufLen = buffer.length; - int removed = 0; - int from, to; - from = to = head; - try { - for (from = to = head; from != last; from = oneRight(from, bufLen)) { - if (predicate.apply(buffer[from])) { - buffer[from] = 0; - removed++; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0; - } - - to = oneRight(to, bufLen); - } - } finally { - // Keep the deque in consistent state even if the predicate throws an exception. - for (; from != last; from = oneRight(from, bufLen)) { - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0; - } - - to = oneRight(to, bufLen); - } - tail = to; - } - - return removed; - } - - /** {@inheritDoc} */ - @Override - public boolean contains(int e) { - int fromIndex = head; - int toIndex = tail; - - final int[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - if (((e) == (buffer[i]))) { - return true; - } - } - - return false; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = 1; - int fromIndex = head; - int toIndex = tail; - - final int[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - h = 31 * h + BitMixer.mix(this.buffer[i]); - } - return h; - } - - /** - * Returns true only if the other object is an instance of the same class and with - * the same elements. - */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Compare order-aligned elements against another {@link IntDeque}. */ - protected boolean equalElements(IntArrayDeque other) { - int max = size(); - if (other.size() != max) { - return false; - } - - Iterator i1 = this.iterator(); - Iterator i2 = other.iterator(); - - while (i1.hasNext() && i2.hasNext()) { - if (!((i1.next().value) == (i2.next().value))) { - return false; - } - } - - return !i1.hasNext() && !i2.hasNext(); - } - - /** Create a new deque by pushing a variable number of arguments to the end of it. */ - public static IntArrayDeque from(int... elements) { - final IntArrayDeque coll = new IntArrayDeque(elements.length); - coll.addLast(elements); - return coll; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/IntArrayList.java b/src/main/java/com/carrotsearch/hppc/IntArrayList.java deleted file mode 100755 index 6029be09..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntArrayList.java +++ /dev/null @@ -1,586 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.IntPredicate; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; -import java.util.stream.IntStream; - -/** An array-backed list of ints. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeArrayList.java") -public class IntArrayList extends AbstractIntCollection - implements IntIndexedContainer, Preallocable, Cloneable, Accountable { - /** An immutable empty buffer (array). */ - public static final int[] EMPTY_ARRAY = new int[0]; - - ; - - /** Reuse the same strategy instance. */ - private static final BoundedProportionalArraySizingStrategy DEFAULT_SIZING_STRATEGY = - BoundedProportionalArraySizingStrategy.DEFAULT_INSTANCE; - - /** - * Internal array for storing the list. The array may be larger than the current size ({@link - * #size()}). - */ - public int[] buffer = EMPTY_ARRAY; - - /** Current number of elements stored in {@link #buffer}. */ - public int elementsCount; - - /** Buffer resizing strategy. */ - protected final ArraySizingStrategy resizer; - - /** New instance with sane defaults. */ - public IntArrayList() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public IntArrayList(int expectedElements) { - this(expectedElements, DEFAULT_SIZING_STRATEGY); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public IntArrayList(int expectedElements, ArraySizingStrategy resizer) { - assert resizer != null; - this.resizer = resizer; - buffer = Arrays.copyOf(buffer, expectedElements); - } - - /** Creates a new list from the elements of another container in its iteration order. */ - public IntArrayList(IntContainer container) { - this(container.size()); - addAll(container); - } - - /** {@inheritDoc} */ - @Override - public void add(int e1) { - ensureBufferSpace(1); - buffer[elementsCount++] = e1; - } - - /** - * Appends two elements at the end of the list. To add more than two elements, use add - * (vararg-version) or access the buffer directly (tight loop). - */ - public void add(int e1, int e2) { - ensureBufferSpace(2); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - } - - /** Add all elements from a range of given array to the list. */ - public void add(int[] elements, int start, int length) { - assert length >= 0 : "Length must be >= 0"; - - ensureBufferSpace(length); - System.arraycopy(elements, start, buffer, elementsCount, length); - elementsCount += length; - } - - /** - * Vararg-signature method for adding elements at the end of the list. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - */ - public final void add(int... elements) { - add(elements, 0, elements.length); - } - - /** Adds all elements from another container. */ - public int addAll(IntContainer container) { - final int size = container.size(); - ensureBufferSpace(size); - - for (IntCursor cursor : container) { - add(cursor.value); - } - - return size; - } - - /** Adds all elements from another iterable. */ - public int addAll(Iterable iterable) { - int size = 0; - for (IntCursor cursor : iterable) { - add(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public void insert(int index, int e1) { - assert (index >= 0 && index <= size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + "]."; - - ensureBufferSpace(1); - System.arraycopy(buffer, index, buffer, index + 1, elementsCount - index); - buffer[index] = e1; - elementsCount++; - } - - /** {@inheritDoc} */ - @Override - public int get(int index) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - return buffer[index]; - } - - /** {@inheritDoc} */ - @Override - public int set(int index, int e1) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - final int v = buffer[index]; - buffer[index] = e1; - return v; - } - - /** {@inheritDoc} */ - @Override - public int removeAt(int index) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - final int v = buffer[index]; - System.arraycopy(buffer, index + 1, buffer, index, --elementsCount - index); - - return v; - } - - /** {@inheritDoc} */ - @Override - public int removeLast() { - assert elementsCount > 0; - - final int v = buffer[--elementsCount]; - - return v; - } - - /** {@inheritDoc} */ - @Override - public void removeRange(int fromIndex, int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - System.arraycopy(buffer, toIndex, buffer, fromIndex, elementsCount - toIndex); - final int count = toIndex - fromIndex; - elementsCount -= count; - } - - /** {@inheritDoc} */ - @Override - public boolean removeElement(int e1) { - return removeFirst(e1) != -1; - } - - /** {@inheritDoc} */ - @Override - public int removeFirst(int e1) { - final int index = indexOf(e1); - if (index >= 0) removeAt(index); - return index; - } - - /** {@inheritDoc} */ - @Override - public int removeLast(int e1) { - final int index = lastIndexOf(e1); - if (index >= 0) removeAt(index); - return index; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(int e1) { - int to = 0; - for (int from = 0; from < elementsCount; from++) { - if (((e1) == (buffer[from]))) { - continue; - } - if (to != from) { - buffer[to] = buffer[from]; - } - to++; - } - final int deleted = elementsCount - to; - this.elementsCount = to; - - return deleted; - } - - /** {@inheritDoc} */ - @Override - public boolean contains(int e1) { - return indexOf(e1) >= 0; - } - - /** {@inheritDoc} */ - @Override - public int indexOf(int e1) { - for (int i = 0; i < elementsCount; i++) { - if (((e1) == (buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int lastIndexOf(int e1) { - for (int i = elementsCount - 1; i >= 0; i--) { - if (((e1) == (buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return elementsCount == 0; - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - final int bufferLen = (buffer == null ? 0 : buffer.length); - if (expectedElements > bufferLen) { - ensureBufferSpace(expectedElements - size()); - } - } - - /** - * Ensures the internal buffer has enough free slots to store expectedAdditions. - * Increases internal buffer size if needed. - */ - protected void ensureBufferSpace(int expectedAdditions) { - final int bufferLen = (buffer == null ? 0 : buffer.length); - if (elementsCount + expectedAdditions > bufferLen) { - final int newSize = resizer.grow(bufferLen, elementsCount, expectedAdditions); - assert newSize >= elementsCount + expectedAdditions - : "Resizer failed to" - + " return sensible new size: " - + newSize - + " <= " - + (elementsCount + expectedAdditions); - - this.buffer = Arrays.copyOf(buffer, newSize); - } - } - - /** - * Truncate or expand the list to the new size. If the list is truncated, the buffer will not be - * reallocated (use {@link #trimToSize()} if you need a truncated buffer), but the truncated - * values will be reset to the default value (zero). If the list is expanded, the elements beyond - * the current size are initialized with JVM-defaults (zero or null values). - */ - public void resize(int newSize) { - if (newSize <= buffer.length) { - if (newSize < elementsCount) { - Arrays.fill(buffer, newSize, elementsCount, 0); - } else { - Arrays.fill(buffer, elementsCount, newSize, 0); - } - } else { - ensureCapacity(newSize); - } - this.elementsCount = newSize; - } - - /** {@inheritDoc} */ - @Override - public int size() { - return elementsCount; - } - - /** Trim the internal buffer to the current size. */ - public void trimToSize() { - if (size() != this.buffer.length) { - this.buffer = toArray(); - } - } - - /** - * Sets the number of stored elements to zero. Releases and initializes the internal storage array - * to default values. To clear the list without cleaning the buffer, simply set the {@link - * #elementsCount} field to zero. - */ - @Override - public void clear() { - Arrays.fill(buffer, 0, elementsCount, 0); - this.elementsCount = 0; - } - - /** Sets the number of stored elements to zero and releases the internal storage array. */ - @Override - public void release() { - this.buffer = EMPTY_ARRAY; - this.elementsCount = 0; - } - - /** - * {@inheritDoc} - * - *

The returned array is sized to match exactly the number of elements of the stack. - */ - @Override - public int[] toArray() { - - return Arrays.copyOf(buffer, elementsCount); - } - - @Override - public IntStream stream() { - - return Arrays.stream(buffer, 0, size()); - } - - /** {@inheritDoc} */ - @Override - public IntIndexedContainer sort() { - Arrays.sort(buffer, 0, elementsCount); - return this; - } - - /** {@inheritDoc} */ - @Override - public IntIndexedContainer reverse() { - for (int i = 0, mid = elementsCount >> 1, j = elementsCount - 1; i < mid; i++, j--) { - int tmp = buffer[i]; - buffer[i] = buffer[j]; - buffer[j] = tmp; - } - return this; - } - - /** - * Clone this object. The returned clone will reuse the same hash function and array resizing - * strategy. - */ - @Override - public IntArrayList clone() { - try { - - final IntArrayList cloned = (IntArrayList) super.clone(); - cloned.buffer = buffer.clone(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = 1, max = elementsCount; - for (int i = 0; i < max; i++) { - h = 31 * h + BitMixer.mix(this.buffer[i]); - } - return h; - } - - /** - * Returns true only if the other object is an instance of the same class and with - * the same elements. - */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Compare index-aligned elements against another {@link IntIndexedContainer}. */ - protected boolean equalElements(IntArrayList other) { - int max = size(); - if (other.size() != max) { - return false; - } - - for (int i = 0; i < max; i++) { - if (!((get(i)) == (other.get(i)))) { - return false; - } - } - - return true; - } - - @Override - public long ramBytesAllocated() { - // int: elementsCount - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES - + resizer.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(buffer); - } - - @Override - public long ramBytesUsed() { - // int: elementsCount - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES - + resizer.ramBytesUsed() - + RamUsageEstimator.shallowUsedSizeOfArray(buffer, elementsCount); - } - - /** An iterator implementation for {@link IntArrayList#iterator}. */ - static final class ValueIterator extends AbstractIterator { - private final IntCursor cursor; - - private final int[] buffer; - private final int size; - - public ValueIterator(int[] buffer, int size) { - this.cursor = new IntCursor(); - this.cursor.index = -1; - this.size = size; - this.buffer = buffer; - } - - @Override - protected IntCursor fetch() { - if (cursor.index + 1 == size) return done(); - - cursor.value = buffer[++cursor.index]; - return cursor; - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new ValueIterator(buffer, size()); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - return forEach(procedure, 0, size()); - } - - /** - * Applies procedure to a slice of the list, fromIndex, inclusive, to - * toIndex, exclusive. - */ - public T forEach(T procedure, int fromIndex, final int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - final int[] buffer = this.buffer; - for (int i = fromIndex; i < toIndex; i++) { - procedure.apply(buffer[i]); - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntPredicate predicate) { - final int[] buffer = this.buffer; - final int elementsCount = this.elementsCount; - int to = 0; - int from = 0; - try { - for (; from < elementsCount; from++) { - if (predicate.apply(buffer[from])) { - buffer[from] = 0; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0; - } - to++; - } - } finally { - // Keep the list in a consistent state, even if the predicate throws an exception. - for (; from < elementsCount; from++) { - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0; - } - to++; - } - - this.elementsCount = to; - } - - return elementsCount - to; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - return forEach(predicate, 0, size()); - } - - /** - * Applies predicate to a slice of the list, fromIndex, inclusive, to - * toIndex, exclusive, or until predicate returns false. - */ - public T forEach(T predicate, int fromIndex, final int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - final int[] buffer = this.buffer; - for (int i = fromIndex; i < toIndex; i++) { - if (!predicate.apply(buffer[i])) break; - } - - return predicate; - } - - /** - * Create a list from a variable number of arguments or an array of int. The elements - * are copied from the argument to the internal buffer. - */ - public static IntArrayList from(int... elements) { - final IntArrayList list = new IntArrayList(elements.length); - list.add(elements); - return list; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/IntBufferVisualizer.java b/src/main/java/com/carrotsearch/hppc/IntBufferVisualizer.java deleted file mode 100755 index dddbbfc3..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntBufferVisualizer.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.carrotsearch.hppc; - -/** - * Reused buffer visualization routines. - * - * @see IntSet#visualizeKeyDistribution(int) - * @see IntVTypeMap#visualizeKeyDistribution(int) - */ -class IntBufferVisualizer { - static String visualizeKeyDistribution(int[] buffer, int max, int characters) { - final StringBuilder b = new StringBuilder(); - final char[] chars = ".123456789X".toCharArray(); - for (int i = 1, start = -1; i <= characters; i++) { - int end = (int) ((long) i * max / characters); - - if (start + 1 <= end) { - int taken = 0; - int slots = 0; - for (int slot = start + 1; slot <= end; slot++, slots++) { - if (!((buffer[slot]) == 0)) { - taken++; - } - } - b.append(chars[Math.min(chars.length - 1, taken * chars.length / slots)]); - start = end; - } - } - while (b.length() < characters) { - b.append(' '); - } - return b.toString(); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/IntByteAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/IntByteAssociativeContainer.java deleted file mode 100755 index 474cc041..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntByteAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see IntContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface IntByteAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(int key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntBytePredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link IntByteProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link IntBytePredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public IntCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public ByteContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntByteHashMap.java b/src/main/java/com/carrotsearch/hppc/IntByteHashMap.java deleted file mode 100755 index 2e69d807..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntByteHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of int to byte, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeHashMap.java") -public class IntByteHashMap implements IntByteMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public int[] keys; - - /** The array holding values. */ - public byte[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public IntByteHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public IntByteHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public IntByteHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public IntByteHashMap(IntByteAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public byte put(int key, byte value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - byte previousValue = hasEmptyKey ? values[mask + 1] : ((byte) 0); - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final byte previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return ((byte) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(IntByteAssociativeContainer container) { - final int count = size(); - for (IntByteCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (IntByteCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public byte putOrAdd(int key, byte putValue, byte incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((byte) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public byte addTo(int key, byte incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public byte remove(int key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return ((byte) 0); - } - hasEmptyKey = false; - byte previousValue = values[mask + 1]; - values[mask + 1] = ((byte) 0); - return previousValue; - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final byte previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return ((byte) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof IntLookupContainer) { - if (hasEmptyKey && other.contains(0)) { - hasEmptyKey = false; - values[mask + 1] = ((byte) 0); - } - - final int[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - int existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (IntCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntBytePredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(0, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = ((byte) 0); - } - } - - final int[] keys = this.keys; - final byte[] values = this.values; - for (int slot = 0; slot <= mask; ) { - int existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(0)) { - hasEmptyKey = false; - values[mask + 1] = ((byte) 0); - } - } - - final int[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - int existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public byte get(int key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : ((byte) 0); - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return ((byte) 0); - } - } - - /** {@inheritDoc} */ - @Override - public byte getOrDefault(int key, byte defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(int key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(int key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public byte indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public byte indexReplace(int index, byte newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - byte previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, int key, byte value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public byte indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - byte previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = ((byte) 0); - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, 0); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (IntByteCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(IntByteHashMap other) { - if (other.size() != size()) { - return false; - } - - for (IntByteCursor c : other) { - int key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final int[] prevKeys = this.keys; - final byte[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final IntByteCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new IntByteCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntByteCursor fetch() { - final int mask = IntByteHashMap.this.mask; - while (index <= mask) { - int existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = 0; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final int[] keys = this.keys; - final byte[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(0, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final int[] keys = this.keys; - final byte[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(0, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractIntCollection implements IntLookupContainer { - private final IntByteHashMap owner = IntByteHashMap.this; - - @Override - public boolean contains(int e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((IntByteProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((IntBytePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(IntPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final int e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final IntCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new IntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntCursor fetch() { - final int mask = IntByteHashMap.this.mask; - while (index <= mask) { - int existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = 0; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public ByteCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractByteCollection { - private final IntByteHashMap owner = IntByteHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(byte value) { - for (IntByteCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (IntByteCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (IntByteCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final byte e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final BytePredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ByteCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new ByteCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ByteCursor fetch() { - final int mask = IntByteHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public IntByteHashMap clone() { - try { - - IntByteHashMap cloned = (IntByteHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (IntByteCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return IntBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static IntByteHashMap from(int[] keys, byte[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - IntByteHashMap map = new IntByteHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(int key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(int[] fromKeys, byte[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final int[] keys = this.keys; - final byte[] values = this.values; - final int mask = this.mask; - int existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - int[] prevKeys = this.keys; - byte[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new int[arraySize + emptyElementSlot]); - this.values = (new byte[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, int pendingKey, byte pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final int[] prevKeys = this.keys; - final byte[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final int[] keys = this.keys; - final byte[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final int existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = 0; - values[gapSlot] = ((byte) 0); - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/IntByteMap.java b/src/main/java/com/carrotsearch/hppc/IntByteMap.java deleted file mode 100755 index 06ce8aa8..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntByteMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.IntByteCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface IntByteMap extends IntByteAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public byte get(int key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public byte getOrDefault(int key, byte defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public byte put(int key, byte value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(int key, byte value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(IntByteAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public byte putOrAdd(int key, byte putValue, byte incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public byte addTo(int key, byte additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public byte remove(int key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link IntByteMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(int key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public byte indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public byte indexReplace(int index, byte newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, int key, byte value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public byte indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntCharAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/IntCharAssociativeContainer.java deleted file mode 100755 index adf2f6b1..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntCharAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see IntContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface IntCharAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(int key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntCharPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link IntCharProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link IntCharPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public IntCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public CharContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntCharHashMap.java b/src/main/java/com/carrotsearch/hppc/IntCharHashMap.java deleted file mode 100755 index f3bc23a9..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntCharHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of int to char, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeHashMap.java") -public class IntCharHashMap implements IntCharMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public int[] keys; - - /** The array holding values. */ - public char[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public IntCharHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public IntCharHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public IntCharHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public IntCharHashMap(IntCharAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public char put(int key, char value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - char previousValue = hasEmptyKey ? values[mask + 1] : ((char) 0); - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final char previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return ((char) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(IntCharAssociativeContainer container) { - final int count = size(); - for (IntCharCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (IntCharCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public char putOrAdd(int key, char putValue, char incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((char) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public char addTo(int key, char incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public char remove(int key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return ((char) 0); - } - hasEmptyKey = false; - char previousValue = values[mask + 1]; - values[mask + 1] = ((char) 0); - return previousValue; - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final char previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return ((char) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof IntLookupContainer) { - if (hasEmptyKey && other.contains(0)) { - hasEmptyKey = false; - values[mask + 1] = ((char) 0); - } - - final int[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - int existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (IntCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntCharPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(0, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = ((char) 0); - } - } - - final int[] keys = this.keys; - final char[] values = this.values; - for (int slot = 0; slot <= mask; ) { - int existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(0)) { - hasEmptyKey = false; - values[mask + 1] = ((char) 0); - } - } - - final int[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - int existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public char get(int key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : ((char) 0); - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return ((char) 0); - } - } - - /** {@inheritDoc} */ - @Override - public char getOrDefault(int key, char defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(int key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(int key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public char indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public char indexReplace(int index, char newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - char previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, int key, char value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public char indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - char previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = ((char) 0); - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, 0); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (IntCharCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(IntCharHashMap other) { - if (other.size() != size()) { - return false; - } - - for (IntCharCursor c : other) { - int key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final int[] prevKeys = this.keys; - final char[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final IntCharCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new IntCharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntCharCursor fetch() { - final int mask = IntCharHashMap.this.mask; - while (index <= mask) { - int existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = 0; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final int[] keys = this.keys; - final char[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(0, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final int[] keys = this.keys; - final char[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(0, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractIntCollection implements IntLookupContainer { - private final IntCharHashMap owner = IntCharHashMap.this; - - @Override - public boolean contains(int e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((IntCharProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((IntCharPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(IntPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final int e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final IntCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new IntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntCursor fetch() { - final int mask = IntCharHashMap.this.mask; - while (index <= mask) { - int existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = 0; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public CharCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractCharCollection { - private final IntCharHashMap owner = IntCharHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(char value) { - for (IntCharCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (IntCharCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (IntCharCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final char e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final CharPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final CharCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new CharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharCursor fetch() { - final int mask = IntCharHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public IntCharHashMap clone() { - try { - - IntCharHashMap cloned = (IntCharHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (IntCharCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return IntBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static IntCharHashMap from(int[] keys, char[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - IntCharHashMap map = new IntCharHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(int key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(int[] fromKeys, char[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final int[] keys = this.keys; - final char[] values = this.values; - final int mask = this.mask; - int existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - int[] prevKeys = this.keys; - char[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new int[arraySize + emptyElementSlot]); - this.values = (new char[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, int pendingKey, char pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final int[] prevKeys = this.keys; - final char[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final int[] keys = this.keys; - final char[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final int existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = 0; - values[gapSlot] = ((char) 0); - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/IntCharMap.java b/src/main/java/com/carrotsearch/hppc/IntCharMap.java deleted file mode 100755 index dd06fe44..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntCharMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.IntCharCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface IntCharMap extends IntCharAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public char get(int key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public char getOrDefault(int key, char defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public char put(int key, char value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(int key, char value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(IntCharAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public char putOrAdd(int key, char putValue, char incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public char addTo(int key, char additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public char remove(int key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link IntCharMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(int key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public char indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public char indexReplace(int index, char newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, int key, char value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public char indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntCollection.java b/src/main/java/com/carrotsearch/hppc/IntCollection.java deleted file mode 100755 index 16ae39bf..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntCollection.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.predicates.IntPredicate; - -/** - * A collection allows basic, efficient operations on sets of elements (difference and - * intersection). - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeCollection.java") -public interface IntCollection extends IntContainer { - /** - * Removes all occurrences of e from this collection. - * - * @param e Element to be removed from this collection, if present. - * @return The number of removed elements as a result of this call. - */ - public int removeAll(int e); - - /** - * Removes all elements in this collection that are present in c. - * - * @return Returns the number of removed elements. - */ - public int removeAll(IntLookupContainer c); - - /** - * Removes all elements in this collection for which the given predicate returns true - * . - * - * @return Returns the number of removed elements. - */ - public int removeAll(IntPredicate predicate); - - /** - * Keeps all elements in this collection that are present in c. Runs in time - * proportional to the number of elements in this collection. Equivalent of sets intersection. - * - * @return Returns the number of removed elements. - */ - public int retainAll(IntLookupContainer c); - - /** - * Keeps all elements in this collection for which the given predicate returns true. - * - * @return Returns the number of removed elements. - */ - public int retainAll(IntPredicate predicate); - - /** - * Removes all elements from this collection. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntContainer.java b/src/main/java/com/carrotsearch/hppc/IntContainer.java deleted file mode 100755 index fd8c1b3a..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntContainer.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.IntCursor; -import com.carrotsearch.hppc.predicates.IntPredicate; -import com.carrotsearch.hppc.procedures.IntProcedure; -import java.util.Iterator; - -/** A generic container holding ints. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeContainer.java") -public interface IntContainer extends Iterable { - /** - * Returns an iterator to a cursor traversing the collection. The order of traversal is not - * defined. More than one cursor may be active at a time. The behavior of iterators is undefined - * if structural changes are made to the underlying collection. - * - *

The iterator is implemented as a cursor and it returns the same cursor instance on - * every call to {@link Iterator#next()} (to avoid boxing of primitive types). To read the current - * list's value (or index in the list) use the cursor's public fields. An example is shown below. - * - *

-   * for (IntCursor<int> c : container) {
-   *   System.out.println("index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator iterator(); - - /** - * Lookup a given element in the container. This operation has no speed guarantees (may be linear - * with respect to the size of this container). - * - * @return Returns true if this container has an element equal to e. - */ - public boolean contains(int e); - - /** - * Return the current number of elements in this container. The time for calculating the - * container's size may take O(n) time, although implementing classes should try to - * maintain the current size and return in constant time. - */ - public int size(); - - /** Shortcut for size() == 0. */ - public boolean isEmpty(); - - /** - * Copies all elements of this container to an array. - * - *

The returned array is always a copy, regardless of the storage used by the container. - */ - public int[] toArray(); - - /** - * Applies a procedure to all container elements. Returns the argument (any subclass - * of {@link IntProcedure}. This lets the caller to call methods of the argument by chaining the - * call (even if the argument is an anonymous type) to retrieve computed values, for example - * (IntContainer): - * - *

-   * int count = container.forEach(new IntProcedure() {
-   *   int count; // this is a field declaration in an anonymous class.
-   *
-   *   public void apply(int value) {
-   *     count++;
-   *   }
-   * }).count;
-   * 
- */ - public T forEach(T procedure); - - /** - * Applies a predicate to container elements as long, as the predicate returns - * true. The iteration is interrupted otherwise. - */ - public T forEach(T predicate); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntDeque.java b/src/main/java/com/carrotsearch/hppc/IntDeque.java deleted file mode 100755 index e7981ad5..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntDeque.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.IntCursor; -import com.carrotsearch.hppc.predicates.IntPredicate; -import com.carrotsearch.hppc.procedures.IntProcedure; -import java.util.Deque; -import java.util.Iterator; - -/** - * A linear collection that supports element insertion and removal at both ends. - * - * @see Deque - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeDeque.java") -public interface IntDeque extends IntCollection { - /** - * Removes the first element that equals e. - * - * @return The deleted element's index or -1 if the element was not found. - */ - public int removeFirst(int e); - - /** - * Removes the last element that equals e. - * - * @return The deleted element's index or -1 if the element was not found. - */ - public int removeLast(int e); - - /** Inserts the specified element at the front of this deque. */ - public void addFirst(int e); - - /** Inserts the specified element at the end of this deque. */ - public void addLast(int e); - - /** - * Retrieves and removes the first element of this deque. - * - * @return the head (first) element of this deque. - */ - public int removeFirst(); - - /** - * Retrieves and removes the last element of this deque. - * - * @return the tail of this deque. - */ - public int removeLast(); - - /** - * Retrieves the first element of this deque but does not remove it. - * - * @return the head of this deque. - */ - public int getFirst(); - - /** - * Retrieves the last element of this deque but does not remove it. - * - * @return the head of this deque. - */ - public int getLast(); - - /** - * @return An iterator over elements in this deque in tail-to-head order. - */ - public Iterator descendingIterator(); - - /** Applies a procedure to all elements in tail-to-head order. */ - public T descendingForEach(T procedure); - - /** - * Applies a predicate to container elements as long, as the predicate returns - * true. The iteration is interrupted otherwise. - */ - public T descendingForEach(T predicate); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntDoubleAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/IntDoubleAssociativeContainer.java deleted file mode 100755 index b447931f..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntDoubleAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see IntContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface IntDoubleAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *
-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(int key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntDoublePredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link IntDoubleProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link IntDoublePredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public IntCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public DoubleContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntDoubleHashMap.java b/src/main/java/com/carrotsearch/hppc/IntDoubleHashMap.java deleted file mode 100755 index d070d0f8..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntDoubleHashMap.java +++ /dev/null @@ -1,1082 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of int to double, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeHashMap.java") -public class IntDoubleHashMap implements IntDoubleMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public int[] keys; - - /** The array holding values. */ - public double[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public IntDoubleHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public IntDoubleHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public IntDoubleHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public IntDoubleHashMap(IntDoubleAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public double put(int key, double value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - double previousValue = hasEmptyKey ? values[mask + 1] : 0d; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final double previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0d; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(IntDoubleAssociativeContainer container) { - final int count = size(); - for (IntDoubleCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (IntDoubleCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public double putOrAdd(int key, double putValue, double incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((double) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public double addTo(int key, double incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public double remove(int key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return 0d; - } - hasEmptyKey = false; - double previousValue = values[mask + 1]; - values[mask + 1] = 0d; - return previousValue; - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final double previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0d; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof IntLookupContainer) { - if (hasEmptyKey && other.contains(0)) { - hasEmptyKey = false; - values[mask + 1] = 0d; - } - - final int[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - int existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (IntCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntDoublePredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(0, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0d; - } - } - - final int[] keys = this.keys; - final double[] values = this.values; - for (int slot = 0; slot <= mask; ) { - int existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(0)) { - hasEmptyKey = false; - values[mask + 1] = 0d; - } - } - - final int[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - int existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public double get(int key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : 0d; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0d; - } - } - - /** {@inheritDoc} */ - @Override - public double getOrDefault(int key, double defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(int key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(int key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public double indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public double indexReplace(int index, double newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - double previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, int key, double value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public double indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - double previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0d; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, 0); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (IntDoubleCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(IntDoubleHashMap other) { - if (other.size() != size()) { - return false; - } - - for (IntDoubleCursor c : other) { - int key = c.key; - if (!containsKey(key) - || !(Double.doubleToLongBits(c.value) == Double.doubleToLongBits(get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final int[] prevKeys = this.keys; - final double[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final IntDoubleCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new IntDoubleCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntDoubleCursor fetch() { - final int mask = IntDoubleHashMap.this.mask; - while (index <= mask) { - int existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = 0; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final int[] keys = this.keys; - final double[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(0, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final int[] keys = this.keys; - final double[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(0, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractIntCollection implements IntLookupContainer { - private final IntDoubleHashMap owner = IntDoubleHashMap.this; - - @Override - public boolean contains(int e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((IntDoubleProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((IntDoublePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(IntPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final int e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final IntCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new IntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntCursor fetch() { - final int mask = IntDoubleHashMap.this.mask; - while (index <= mask) { - int existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = 0; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public DoubleCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractDoubleCollection { - private final IntDoubleHashMap owner = IntDoubleHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(double value) { - for (IntDoubleCursor c : owner) { - if ((Double.doubleToLongBits(value) == Double.doubleToLongBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (IntDoubleCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (IntDoubleCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final double e) { - return owner.removeAll( - (key, value) -> (Double.doubleToLongBits(e) == Double.doubleToLongBits(value))); - } - - @Override - public int removeAll(final DoublePredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final DoubleCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new DoubleCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected DoubleCursor fetch() { - final int mask = IntDoubleHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public IntDoubleHashMap clone() { - try { - - IntDoubleHashMap cloned = (IntDoubleHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (IntDoubleCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return IntBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static IntDoubleHashMap from(int[] keys, double[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - IntDoubleHashMap map = new IntDoubleHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(int key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(int[] fromKeys, double[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final int[] keys = this.keys; - final double[] values = this.values; - final int mask = this.mask; - int existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - int[] prevKeys = this.keys; - double[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new int[arraySize + emptyElementSlot]); - this.values = (new double[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, int pendingKey, double pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final int[] prevKeys = this.keys; - final double[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final int[] keys = this.keys; - final double[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final int existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = 0; - values[gapSlot] = 0d; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/IntDoubleMap.java b/src/main/java/com/carrotsearch/hppc/IntDoubleMap.java deleted file mode 100755 index 03691ffa..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntDoubleMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.IntDoubleCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface IntDoubleMap extends IntDoubleAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public double get(int key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public double getOrDefault(int key, double defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public double put(int key, double value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(int key, double value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(IntDoubleAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public double putOrAdd(int key, double putValue, double incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public double addTo(int key, double additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public double remove(int key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link IntDoubleMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(int key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public double indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public double indexReplace(int index, double newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, int key, double value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public double indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntFloatAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/IntFloatAssociativeContainer.java deleted file mode 100755 index 50a4b963..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntFloatAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see IntContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface IntFloatAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(int key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntFloatPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link IntFloatProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link IntFloatPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public IntCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public FloatContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntFloatHashMap.java b/src/main/java/com/carrotsearch/hppc/IntFloatHashMap.java deleted file mode 100755 index f7a3a4f5..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntFloatHashMap.java +++ /dev/null @@ -1,1081 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of int to float, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeHashMap.java") -public class IntFloatHashMap implements IntFloatMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public int[] keys; - - /** The array holding values. */ - public float[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public IntFloatHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public IntFloatHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public IntFloatHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public IntFloatHashMap(IntFloatAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public float put(int key, float value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - float previousValue = hasEmptyKey ? values[mask + 1] : 0f; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final float previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0f; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(IntFloatAssociativeContainer container) { - final int count = size(); - for (IntFloatCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (IntFloatCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public float putOrAdd(int key, float putValue, float incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((float) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public float addTo(int key, float incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public float remove(int key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return 0f; - } - hasEmptyKey = false; - float previousValue = values[mask + 1]; - values[mask + 1] = 0f; - return previousValue; - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final float previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0f; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof IntLookupContainer) { - if (hasEmptyKey && other.contains(0)) { - hasEmptyKey = false; - values[mask + 1] = 0f; - } - - final int[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - int existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (IntCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntFloatPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(0, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0f; - } - } - - final int[] keys = this.keys; - final float[] values = this.values; - for (int slot = 0; slot <= mask; ) { - int existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(0)) { - hasEmptyKey = false; - values[mask + 1] = 0f; - } - } - - final int[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - int existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public float get(int key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : 0f; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0f; - } - } - - /** {@inheritDoc} */ - @Override - public float getOrDefault(int key, float defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(int key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(int key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public float indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public float indexReplace(int index, float newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - float previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, int key, float value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public float indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - float previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0f; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, 0); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (IntFloatCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(IntFloatHashMap other) { - if (other.size() != size()) { - return false; - } - - for (IntFloatCursor c : other) { - int key = c.key; - if (!containsKey(key) || !(Float.floatToIntBits(c.value) == Float.floatToIntBits(get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final int[] prevKeys = this.keys; - final float[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final IntFloatCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new IntFloatCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntFloatCursor fetch() { - final int mask = IntFloatHashMap.this.mask; - while (index <= mask) { - int existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = 0; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final int[] keys = this.keys; - final float[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(0, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final int[] keys = this.keys; - final float[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(0, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractIntCollection implements IntLookupContainer { - private final IntFloatHashMap owner = IntFloatHashMap.this; - - @Override - public boolean contains(int e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((IntFloatProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((IntFloatPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(IntPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final int e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final IntCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new IntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntCursor fetch() { - final int mask = IntFloatHashMap.this.mask; - while (index <= mask) { - int existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = 0; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public FloatCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractFloatCollection { - private final IntFloatHashMap owner = IntFloatHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(float value) { - for (IntFloatCursor c : owner) { - if ((Float.floatToIntBits(value) == Float.floatToIntBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (IntFloatCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (IntFloatCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final float e) { - return owner.removeAll( - (key, value) -> (Float.floatToIntBits(e) == Float.floatToIntBits(value))); - } - - @Override - public int removeAll(final FloatPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final FloatCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new FloatCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected FloatCursor fetch() { - final int mask = IntFloatHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public IntFloatHashMap clone() { - try { - - IntFloatHashMap cloned = (IntFloatHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (IntFloatCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return IntBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static IntFloatHashMap from(int[] keys, float[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - IntFloatHashMap map = new IntFloatHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(int key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(int[] fromKeys, float[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final int[] keys = this.keys; - final float[] values = this.values; - final int mask = this.mask; - int existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - int[] prevKeys = this.keys; - float[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new int[arraySize + emptyElementSlot]); - this.values = (new float[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, int pendingKey, float pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final int[] prevKeys = this.keys; - final float[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final int[] keys = this.keys; - final float[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final int existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = 0; - values[gapSlot] = 0f; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/IntFloatMap.java b/src/main/java/com/carrotsearch/hppc/IntFloatMap.java deleted file mode 100755 index 4a1e2c9b..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntFloatMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.IntFloatCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface IntFloatMap extends IntFloatAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public float get(int key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public float getOrDefault(int key, float defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public float put(int key, float value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(int key, float value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(IntFloatAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public float putOrAdd(int key, float putValue, float incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public float addTo(int key, float additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public float remove(int key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link IntFloatMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(int key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public float indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public float indexReplace(int index, float newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, int key, float value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public float indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntHashSet.java b/src/main/java/com/carrotsearch/hppc/IntHashSet.java deleted file mode 100755 index f6aae01d..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntHashSet.java +++ /dev/null @@ -1,787 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash set of ints, implemented using open addressing with linear probing for - * collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypeHashSet.java") -public class IntHashSet extends AbstractIntCollection - implements IntLookupContainer, IntSet, Preallocable, Cloneable, Accountable { - /** The hash array holding keys. */ - public int[] keys; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any. - * - * @see #size() - * @see #hasEmptyKey - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** - * New instance with sane defaults. - * - * @see #IntHashSet(int, double) - */ - public IntHashSet() { - this(DEFAULT_EXPECTED_ELEMENTS, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with sane defaults. - * - * @see #IntHashSet(int, double) - */ - public IntHashSet(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public IntHashSet(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** New instance copying elements from another {@link IntContainer}. */ - public IntHashSet(IntContainer container) { - this(container.size()); - addAll(container); - } - - /** {@inheritDoc} */ - @Override - public boolean add(int key) { - if (((key) == 0)) { - assert ((keys[mask + 1]) == 0); - boolean added = !hasEmptyKey; - hasEmptyKey = true; - return added; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return false; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key); - } else { - keys[slot] = key; - } - - assigned++; - return true; - } - } - - /** - * Adds all elements from the given list (vararg) to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - */ - public final int addAll(int... elements) { - ensureCapacity(elements.length); - int count = 0; - for (int e : elements) { - if (add(e)) { - count++; - } - } - return count; - } - - /** - * Adds all elements from the given {@link IntContainer} to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - */ - public int addAll(IntContainer container) { - ensureCapacity(container.size()); - return addAll((Iterable) container); - } - - /** - * Adds all elements from the given iterable to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - */ - public int addAll(Iterable iterable) { - int count = 0; - for (IntCursor cursor : iterable) { - if (add(cursor.value)) { - count++; - } - } - return count; - } - - /** {@inheritDoc} */ - @Override - public int[] toArray() { - - final int[] cloned = (new int[size()]); - int j = 0; - if (hasEmptyKey) { - cloned[j++] = 0; - } - - final int[] keys = this.keys; - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - int existing; - if (!((existing = keys[slot]) == 0)) { - cloned[j++] = existing; - } - } - - return cloned; - } - - /** An alias for the (preferred) {@link #removeAll}. */ - public boolean remove(int key) { - if (((key) == 0)) { - boolean hadEmptyKey = hasEmptyKey; - hasEmptyKey = false; - return hadEmptyKey; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - shiftConflictingKeys(slot); - return true; - } - slot = (slot + 1) & mask; - } - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(int key) { - return remove(key) ? 1 : 0; - } - - /** - * Removes all keys present in a given container. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntContainer other) { - final int before = size(); - - // Try to iterate over the smaller set or over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof IntLookupContainer) { - if (hasEmptyKey && other.contains(0)) { - hasEmptyKey = false; - } - - final int[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - int existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (IntCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntPredicate predicate) { - int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(0)) { - hasEmptyKey = false; - } - } - - final int[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - int existing; - if (!((existing = keys[slot]) == 0)) { - if (predicate.apply(existing)) { - shiftConflictingKeys(slot); - continue; // Repeat the check for the same slot i (shifted). - } - } - slot++; - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public boolean contains(int key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - return false; - } - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - Arrays.fill(keys, 0); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - keys = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return size() == 0; - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final int[] prevKeys = this.keys; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys); - } - } - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - final int[] keys = this.keys; - for (int slot = mask; slot >= 0; slot--) { - int existing; - if (!((existing = keys[slot]) == 0)) { - h += BitMixer.mix(existing); - } - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && sameKeys(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - private boolean sameKeys(IntSet other) { - if (other.size() != size()) { - return false; - } - - for (IntCursor c : other) { - if (!contains(c.value)) { - return false; - } - } - - return true; - } - - /** {@inheritDoc} */ - @Override - public IntHashSet clone() { - try { - - IntHashSet cloned = (IntHashSet) super.clone(); - cloned.keys = keys.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - @Override - public long ramBytesAllocated() { - // int: assigned, mask, keyMixer, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys); - } - - @Override - public long ramBytesUsed() { - // int: assigned, mask, keyMixer, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - protected final class EntryIterator extends AbstractIterator { - private final IntCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new IntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntCursor fetch() { - final int mask = IntHashSet.this.mask; - while (index <= mask) { - int existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = 0; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - if (hasEmptyKey) { - procedure.apply(0); - } - - final int[] keys = this.keys; - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - int existing; - if (!((existing = keys[slot]) == 0)) { - procedure.apply(existing); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - if (hasEmptyKey) { - if (!predicate.apply(0)) { - return predicate; - } - } - - final int[] keys = this.keys; - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - int existing; - if (!((existing = keys[slot]) == 0)) { - if (!predicate.apply(existing)) { - break; - } - } - } - - return predicate; - } - - /** - * Create a set from a variable number of arguments or an array of int. The elements - * are copied from the argument to the internal buffer. - */ - public static IntHashSet from(int... elements) { - final IntHashSet set = new IntHashSet(elements.length); - set.addAll(elements); - return set; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(int key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up logic in - * certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between modifications (it will not be affected by read-only - * operations). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the set. - * @return A non-negative value of the logical "index" of the key in the set or a negative value - * if the key did not exist. - */ - public int indexOf(int key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index) { - assert index < 0 || index <= mask || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** - * Returns the exact value of the existing key. This method makes sense for sets of objects which - * define custom key-equality relationship. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the equivalent key currently stored in the set. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public int indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return keys[index]; - } - - /** - * Replaces the existing equivalent key with the given one and returns any previous value stored - * for that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @param equivalentKey The key to put in the set as a replacement. Must be equivalent to the key - * currently stored at the provided index. - * @return Returns the previous key stored in the set. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public int indexReplace(int index, int equivalentKey) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - assert ((keys[index]) == (equivalentKey)); - - int previousValue = keys[index]; - keys[index] = equivalentKey; - return previousValue; - } - - /** - * Inserts a key for an index that is not present in the set. This method may help in avoiding - * double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public void indexInsert(int index, int key) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - assert ((keys[index]) == 0); - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key); - } else { - keys[index] = key; - } - - assigned++; - } - } - - /** - * Removes a key at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public void indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - if (index > mask) { - hasEmptyKey = false; - } else { - shiftConflictingKeys(index); - } - } - - @Override - public String visualizeKeyDistribution(int characters) { - return IntBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(int[] fromKeys) { - assert HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored keys into the new buffers. - final int[] keys = this.keys; - final int mask = this.mask; - int existing; - for (int i = fromKeys.length - 1; --i >= 0; ) { - if (!((existing = fromKeys[i]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - int[] prevKeys = this.keys; - try { - int emptyElementSlot = 1; - this.keys = (new int[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.keys == null ? 0 : size(), arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key to be inserted into the buffer but there is not - * enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, int pendingKey) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final int[] prevKeys = this.keys; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - - // Rehash old keys, including the pending key. - rehash(prevKeys); - } - - /** Shift all the slot-conflicting keys allocated to (and including) slot. */ - protected void shiftConflictingKeys(int gapSlot) { - final int[] keys = this.keys; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final int existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = 0; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/IntIndexedContainer.java b/src/main/java/com/carrotsearch/hppc/IntIndexedContainer.java deleted file mode 100755 index 0eb076ea..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntIndexedContainer.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.carrotsearch.hppc; - -import java.util.RandomAccess; -import java.util.stream.IntStream; - -/** - * An indexed container provides random access to elements based on an index. Indexes - * are zero-based. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeIndexedContainer.java") -public interface IntIndexedContainer extends IntCollection, RandomAccess { - /** - * Removes the first element that equals e1, returning whether an element has been - * removed. - */ - public boolean removeElement(int e1); - - /** - * Removes the first element that equals e1, returning its deleted position or - * -1 if the element was not found. - */ - public int removeFirst(int e1); - - /** - * Removes the last element that equals e1, returning its deleted position or - * -1 if the element was not found. - */ - public int removeLast(int e1); - - /** - * Returns the index of the first occurrence of the specified element in this list, or -1 if this - * list does not contain the element. - */ - public int indexOf(int e1); - - /** - * Returns the index of the last occurrence of the specified element in this list, or -1 if this - * list does not contain the element. - */ - public int lastIndexOf(int e1); - - /** Adds an element to the end of this container (the last index is incremented by one). */ - public void add(int e1); - - /** - * Inserts the specified element at the specified position in this list. - * - * @param index The index at which the element should be inserted, shifting any existing and - * subsequent elements to the right. - */ - public void insert(int index, int e1); - - /** - * Replaces the element at the specified position in this list with the specified element. - * - * @return Returns the previous value in the list. - */ - public int set(int index, int e1); - - /** - * @return Returns the element at index index from the list. - */ - public int get(int index); - - /** - * Removes the element at the specified position in this container and returns it. - * - * @see #removeFirst - * @see #removeLast - * @see #removeAll - */ - public int removeAt(int index); - - /** Removes and returns the last element of this container. This container must not be empty. */ - public int removeLast(); - - /** - * Removes from this container all of the elements with indexes between fromIndex, - * inclusive, and toIndex, exclusive. - */ - public void removeRange(int fromIndex, int toIndex); - - /** Returns this container elements as a stream. */ - public IntStream stream(); - - /** Sorts the elements in this container and returns this container. */ - public IntIndexedContainer sort(); - - /** Reverses the elements in this container and returns this container. */ - public IntIndexedContainer reverse(); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntIntAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/IntIntAssociativeContainer.java deleted file mode 100755 index fdb8772a..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntIntAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see IntContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface IntIntAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(int key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntIntPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link IntIntProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link IntIntPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public IntCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public IntContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntIntHashMap.java b/src/main/java/com/carrotsearch/hppc/IntIntHashMap.java deleted file mode 100755 index 5380245c..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntIntHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of int to int, implemented using open addressing with linear - * probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeHashMap.java") -public class IntIntHashMap implements IntIntMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public int[] keys; - - /** The array holding values. */ - public int[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public IntIntHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public IntIntHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public IntIntHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public IntIntHashMap(IntIntAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public int put(int key, int value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - int previousValue = hasEmptyKey ? values[mask + 1] : 0; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final int previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(IntIntAssociativeContainer container) { - final int count = size(); - for (IntIntCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (IntIntCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public int putOrAdd(int key, int putValue, int incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((int) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public int addTo(int key, int incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public int remove(int key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return 0; - } - hasEmptyKey = false; - int previousValue = values[mask + 1]; - values[mask + 1] = 0; - return previousValue; - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final int previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof IntLookupContainer) { - if (hasEmptyKey && other.contains(0)) { - hasEmptyKey = false; - values[mask + 1] = 0; - } - - final int[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - int existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (IntCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntIntPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(0, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0; - } - } - - final int[] keys = this.keys; - final int[] values = this.values; - for (int slot = 0; slot <= mask; ) { - int existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(0)) { - hasEmptyKey = false; - values[mask + 1] = 0; - } - } - - final int[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - int existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int get(int key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : 0; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0; - } - } - - /** {@inheritDoc} */ - @Override - public int getOrDefault(int key, int defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(int key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(int key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public int indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public int indexReplace(int index, int newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - int previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, int key, int value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public int indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - int previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, 0); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (IntIntCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(IntIntHashMap other) { - if (other.size() != size()) { - return false; - } - - for (IntIntCursor c : other) { - int key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final int[] prevKeys = this.keys; - final int[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final IntIntCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new IntIntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntIntCursor fetch() { - final int mask = IntIntHashMap.this.mask; - while (index <= mask) { - int existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = 0; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final int[] keys = this.keys; - final int[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(0, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final int[] keys = this.keys; - final int[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(0, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractIntCollection implements IntLookupContainer { - private final IntIntHashMap owner = IntIntHashMap.this; - - @Override - public boolean contains(int e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((IntIntProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((IntIntPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(IntPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final int e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final IntCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new IntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntCursor fetch() { - final int mask = IntIntHashMap.this.mask; - while (index <= mask) { - int existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = 0; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public IntCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractIntCollection { - private final IntIntHashMap owner = IntIntHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(int value) { - for (IntIntCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (IntIntCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (IntIntCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final int e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final IntPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final IntCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new IntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntCursor fetch() { - final int mask = IntIntHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public IntIntHashMap clone() { - try { - - IntIntHashMap cloned = (IntIntHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (IntIntCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return IntBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static IntIntHashMap from(int[] keys, int[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - IntIntHashMap map = new IntIntHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(int key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(int[] fromKeys, int[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final int[] keys = this.keys; - final int[] values = this.values; - final int mask = this.mask; - int existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - int[] prevKeys = this.keys; - int[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new int[arraySize + emptyElementSlot]); - this.values = (new int[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, int pendingKey, int pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final int[] prevKeys = this.keys; - final int[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final int[] keys = this.keys; - final int[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final int existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = 0; - values[gapSlot] = 0; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/IntIntMap.java b/src/main/java/com/carrotsearch/hppc/IntIntMap.java deleted file mode 100755 index 29097ecf..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntIntMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.IntIntCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface IntIntMap extends IntIntAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public int get(int key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public int getOrDefault(int key, int defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public int put(int key, int value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(int key, int value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(IntIntAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public int putOrAdd(int key, int putValue, int incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public int addTo(int key, int additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public int remove(int key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link IntIntMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(int key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public int indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public int indexReplace(int index, int newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, int key, int value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public int indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntLongAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/IntLongAssociativeContainer.java deleted file mode 100755 index 37f3887c..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntLongAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see IntContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface IntLongAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(int key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntLongPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link IntLongProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link IntLongPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public IntCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public LongContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntLongHashMap.java b/src/main/java/com/carrotsearch/hppc/IntLongHashMap.java deleted file mode 100755 index 8a595142..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntLongHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of int to long, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeHashMap.java") -public class IntLongHashMap implements IntLongMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public int[] keys; - - /** The array holding values. */ - public long[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public IntLongHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public IntLongHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public IntLongHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public IntLongHashMap(IntLongAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public long put(int key, long value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - long previousValue = hasEmptyKey ? values[mask + 1] : 0L; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final long previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0L; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(IntLongAssociativeContainer container) { - final int count = size(); - for (IntLongCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (IntLongCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public long putOrAdd(int key, long putValue, long incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((long) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public long addTo(int key, long incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public long remove(int key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return 0L; - } - hasEmptyKey = false; - long previousValue = values[mask + 1]; - values[mask + 1] = 0L; - return previousValue; - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final long previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0L; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof IntLookupContainer) { - if (hasEmptyKey && other.contains(0)) { - hasEmptyKey = false; - values[mask + 1] = 0L; - } - - final int[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - int existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (IntCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntLongPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(0, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0L; - } - } - - final int[] keys = this.keys; - final long[] values = this.values; - for (int slot = 0; slot <= mask; ) { - int existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(0)) { - hasEmptyKey = false; - values[mask + 1] = 0L; - } - } - - final int[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - int existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public long get(int key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : 0L; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0L; - } - } - - /** {@inheritDoc} */ - @Override - public long getOrDefault(int key, long defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(int key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(int key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public long indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public long indexReplace(int index, long newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - long previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, int key, long value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public long indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - long previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0L; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, 0); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (IntLongCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(IntLongHashMap other) { - if (other.size() != size()) { - return false; - } - - for (IntLongCursor c : other) { - int key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final int[] prevKeys = this.keys; - final long[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final IntLongCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new IntLongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntLongCursor fetch() { - final int mask = IntLongHashMap.this.mask; - while (index <= mask) { - int existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = 0; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final int[] keys = this.keys; - final long[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(0, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final int[] keys = this.keys; - final long[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(0, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractIntCollection implements IntLookupContainer { - private final IntLongHashMap owner = IntLongHashMap.this; - - @Override - public boolean contains(int e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((IntLongProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((IntLongPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(IntPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final int e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final IntCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new IntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntCursor fetch() { - final int mask = IntLongHashMap.this.mask; - while (index <= mask) { - int existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = 0; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public LongCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractLongCollection { - private final IntLongHashMap owner = IntLongHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(long value) { - for (IntLongCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (IntLongCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (IntLongCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final long e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final LongPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final LongCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new LongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongCursor fetch() { - final int mask = IntLongHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public IntLongHashMap clone() { - try { - - IntLongHashMap cloned = (IntLongHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (IntLongCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return IntBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static IntLongHashMap from(int[] keys, long[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - IntLongHashMap map = new IntLongHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(int key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(int[] fromKeys, long[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final int[] keys = this.keys; - final long[] values = this.values; - final int mask = this.mask; - int existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - int[] prevKeys = this.keys; - long[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new int[arraySize + emptyElementSlot]); - this.values = (new long[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, int pendingKey, long pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final int[] prevKeys = this.keys; - final long[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final int[] keys = this.keys; - final long[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final int existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = 0; - values[gapSlot] = 0L; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/IntLongMap.java b/src/main/java/com/carrotsearch/hppc/IntLongMap.java deleted file mode 100755 index debb6908..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntLongMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.IntLongCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface IntLongMap extends IntLongAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public long get(int key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public long getOrDefault(int key, long defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public long put(int key, long value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(int key, long value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(IntLongAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public long putOrAdd(int key, long putValue, long incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public long addTo(int key, long additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public long remove(int key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link IntLongMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(int key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public long indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public long indexReplace(int index, long newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, int key, long value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public long indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntLookupContainer.java b/src/main/java/com/carrotsearch/hppc/IntLookupContainer.java deleted file mode 100755 index 7eee508f..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntLookupContainer.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.carrotsearch.hppc; - -/** - * Marker interface for containers that can check if they contain a given object in at least time - * O(log n) and ideally in amortized constant time O(1). - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeLookupContainer.java") -public interface IntLookupContainer extends IntContainer { - public boolean contains(int e); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntObjectAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/IntObjectAssociativeContainer.java deleted file mode 100755 index 7be49222..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntObjectAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see IntContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface IntObjectAssociativeContainer extends Iterable> { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator> iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(int key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntObjectPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link IntObjectProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public > T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link IntObjectPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public > T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public IntCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public ObjectContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntObjectHashMap.java b/src/main/java/com/carrotsearch/hppc/IntObjectHashMap.java deleted file mode 100755 index 8fb8f64f..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntObjectHashMap.java +++ /dev/null @@ -1,1050 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of int to Object, implemented using open addressing with - * linear probing for collision resolution. Supports null values. - * - * @see HPPC interfaces diagram - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class IntObjectHashMap - implements IntObjectMap, Preallocable, Cloneable, Accountable { - /** The array holding keys. */ - public int[] keys; - - /** The array holding values. */ - public Object[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public IntObjectHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public IntObjectHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public IntObjectHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public IntObjectHashMap(IntObjectAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public VType put(int key, VType value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - VType previousValue = hasEmptyKey ? (VType) values[mask + 1] : null; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final VType previousValue = (VType) values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return null; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(IntObjectAssociativeContainer container) { - final int count = size(); - for (IntObjectCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable> iterable) { - final int count = size(); - for (IntObjectCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** {@inheritDoc} */ - @Override - public VType remove(int key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return null; - } - hasEmptyKey = false; - VType previousValue = (VType) values[mask + 1]; - values[mask + 1] = null; - return previousValue; - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final VType previousValue = (VType) values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return null; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof IntLookupContainer) { - if (hasEmptyKey && other.contains(0)) { - hasEmptyKey = false; - values[mask + 1] = null; - } - - final int[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - int existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (IntCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntObjectPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(0, (VType) values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = null; - } - } - - final int[] keys = this.keys; - final VType[] values = (VType[]) this.values; - for (int slot = 0; slot <= mask; ) { - int existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(0)) { - hasEmptyKey = false; - values[mask + 1] = null; - } - } - - final int[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - int existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public VType get(int key) { - if (((key) == 0)) { - return hasEmptyKey ? (VType) values[mask + 1] : null; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return (VType) values[slot]; - } - slot = (slot + 1) & mask; - } - - return null; - } - } - - /** {@inheritDoc} */ - @Override - public VType getOrDefault(int key, VType defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? (VType) values[mask + 1] : defaultValue; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return (VType) values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(int key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(int key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public VType indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return (VType) values[index]; - } - - /** {@inheritDoc} */ - @Override - public VType indexReplace(int index, VType newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - VType previousValue = (VType) values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, int key, VType value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public VType indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - VType previousValue = (VType) values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = null; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, 0); - - Arrays.fill(values, null); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (IntObjectCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** - * Return true if all keys of some other container exist in this container. Values are compared - * using {@link Objects#equals(Object)} method. - */ - protected boolean equalElements(IntObjectHashMap other) { - if (other.size() != size()) { - return false; - } - - for (IntObjectCursor c : other) { - int key = c.key; - if (!containsKey(key) || !java.util.Objects.equals(c.value, get(key))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final int[] prevKeys = this.keys; - final VType[] prevValues = (VType[]) this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final IntObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new IntObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntObjectCursor fetch() { - final int mask = IntObjectHashMap.this.mask; - while (index <= mask) { - int existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = (VType) values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = 0; - cursor.value = (VType) values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator> iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T procedure) { - final int[] keys = this.keys; - final VType[] values = (VType[]) this.values; - - if (hasEmptyKey) { - procedure.apply(0, (VType) values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T predicate) { - final int[] keys = this.keys; - final VType[] values = (VType[]) this.values; - - if (hasEmptyKey) { - if (!predicate.apply(0, (VType) values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractIntCollection implements IntLookupContainer { - private final IntObjectHashMap owner = IntObjectHashMap.this; - - @Override - public boolean contains(int e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((IntObjectProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((IntObjectPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(IntPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final int e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final IntCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new IntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntCursor fetch() { - final int mask = IntObjectHashMap.this.mask; - while (index <= mask) { - int existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = 0; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public ObjectCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractObjectCollection { - private final IntObjectHashMap owner = IntObjectHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(VType value) { - for (IntObjectCursor c : owner) { - if (java.util.Objects.equals(value, c.value)) { - return true; - } - } - return false; - } - - @Override - public > T forEach(T procedure) { - for (IntObjectCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public > T forEach(T predicate) { - for (IntObjectCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator> iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final VType e) { - return owner.removeAll((key, value) -> java.util.Objects.equals(e, value)); - } - - @Override - public int removeAll(final ObjectPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator> { - private final ObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new ObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectCursor fetch() { - final int mask = IntObjectHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = (VType) values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = (VType) values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public IntObjectHashMap clone() { - try { - - IntObjectHashMap cloned = (IntObjectHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (IntObjectCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return IntBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static IntObjectHashMap from(int[] keys, VType[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - IntObjectHashMap map = new IntObjectHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(int key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(int[] fromKeys, VType[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final int[] keys = this.keys; - final VType[] values = (VType[]) this.values; - final int mask = this.mask; - int existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - int[] prevKeys = this.keys; - VType[] prevValues = (VType[]) this.values; - try { - int emptyElementSlot = 1; - this.keys = (new int[arraySize + emptyElementSlot]); - this.values = ((VType[]) new Object[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, int pendingKey, VType pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final int[] prevKeys = this.keys; - final VType[] prevValues = (VType[]) this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final int[] keys = this.keys; - final VType[] values = (VType[]) this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final int existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = 0; - values[gapSlot] = null; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/IntObjectMap.java b/src/main/java/com/carrotsearch/hppc/IntObjectMap.java deleted file mode 100755 index 86c778c7..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntObjectMap.java +++ /dev/null @@ -1,181 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.IntObjectCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface IntObjectMap extends IntObjectAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public VType get(int key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public VType getOrDefault(int key, VType defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public VType put(int key, VType value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(int key, VType value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(IntObjectAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable> iterable); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public VType remove(int key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link IntObjectMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(int key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public VType indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public VType indexReplace(int index, VType newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, int key, VType value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public VType indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntPgmIndex.java b/src/main/java/com/carrotsearch/hppc/IntPgmIndex.java deleted file mode 100755 index da7dd0d2..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntPgmIndex.java +++ /dev/null @@ -1,600 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.IntCursor; -import com.carrotsearch.hppc.procedures.IntProcedure; -import java.util.Arrays; -import java.util.Iterator; - -/** - * Space-efficient index that enables fast rank/range search operations on a sorted sequence of - * int. - * - *

Implementation of the PGM-Index described at https://pgm.di.unipi.it/, based on the paper - * - *

- *   Paolo Ferragina and Giorgio Vinciguerra.
- *   The PGM-index: a fully-dynamic compressed learned index with provable worst-case bounds.
- *   PVLDB, 13(8): 1162-1175, 2020.
- * 
- * - * It provides {@code rank} and {@code range} search operations. {@code indexOf()} is faster than - * B+Tree, and the index is much more compact. {@code contains()} is between 4x to 7x slower than - * {@code IntHashSet#contains()}, but between 2.5x to 3x faster than {@link Arrays#binarySearch}. - * - *

Its compactness (40KB for 200MB of keys) makes it efficient for very large collections, the - * index fitting easily in the L2 cache. The {@code epsilon} parameter should be set according to - * the desired space-time trade-off. A smaller value makes the estimation more precise and the range - * smaller but at the cost of increased space usage. In practice, {@code epsilon} 64 is a good sweet - * spot. - * - *

Internally the index uses an optimal piecewise linear mapping from keys to their position in - * the sorted order. This mapping is represented as a sequence of linear models (segments) which are - * themselves recursively indexed by other piecewise linear mappings. - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypePgmIndex.java") -public class IntPgmIndex implements Accountable { - - /** Empty immutable IntPgmIndex. */ - public static final IntPgmIndex EMPTY = new IntEmptyPgmIndex(); - - /** - * Epsilon approximation range when searching the list of keys. Controls the size of the returned - * search range, strictly greater than 0. It should be set according to the desired space-time - * trade-off. A smaller value makes the estimation more precise and the range smaller but at the - * cost of increased space usage. - * - *

With EPSILON=64 the benchmark with 200MB of keys shows that this PGM index requires only 2% - * additional memory on average (40KB). It depends on the distribution of the keys. This epsilon - * value is good even for 2MB of keys. With EPSILON=32: +5% speed, but 4x space (160KB). - */ - public static final int EPSILON = 64; - - /** - * Epsilon approximation range for the segments layers. Controls the size of the search range in - * the hierarchical segment lists, strictly greater than 0. - */ - public static final int EPSILON_RECURSIVE = 32; - - /** Size of a key, measured in {@link Integer#BYTES} because the key is stored in an int[]. */ - public static final int KEY_SIZE = - RamUsageEstimator.primitiveSizes.get(int.class) / Integer.BYTES; - - /** 2x {@link #KEY_SIZE}. */ - public static final int DOUBLE_KEY_SIZE = KEY_SIZE * 2; - - /** - * Data size of a segment, measured in {@link Integer#BYTES}, because segments are stored in an - * int[]. - */ - public static final int SEGMENT_DATA_SIZE = KEY_SIZE * 3; - - /** Initial value of the exponential jump when scanning out of the epsilon range. */ - public static final int BEYOND_EPSILON_JUMP = 16; - - /** - * The list of keys for which this index is built. It is sorted and may contain duplicate - * elements. - */ - public final IntArrayList keys; - - /** The size of the key set. That is, the number of distinct elements in {@link #keys}. */ - public final int size; - - /** The lowest key in {@link #keys}. */ - public final int firstKey; - - /** The highest key in {@link #keys}. */ - public final int lastKey; - - /** The epsilon range used to build this index. */ - public final int epsilon; - - /** The recursive epsilon range used to build this index. */ - public final int epsilonRecursive; - - /** The offsets in {@link #segmentData} of the first segment of each segment level. */ - public final int[] levelOffsets; - - /** The index data. It contains all the segments for all the levels. */ - public final int[] segmentData; - - private IntPgmIndex( - IntArrayList keys, - int size, - int epsilon, - int epsilonRecursive, - int[] levelOffsets, - int[] segmentData) { - assert keys.size() > 0; - assert size > 0 && size <= keys.size(); - assert epsilon > 0; - assert epsilonRecursive > 0; - this.keys = keys; - this.size = size; - firstKey = keys.get(0); - lastKey = keys.get(keys.size() - 1); - this.epsilon = epsilon; - this.epsilonRecursive = epsilonRecursive; - this.levelOffsets = levelOffsets; - this.segmentData = segmentData; - } - - /** Empty set constructor. */ - private IntPgmIndex() { - keys = new IntArrayList(0); - size = 0; - firstKey = 0; - lastKey = 0; - epsilon = 0; - epsilonRecursive = 0; - levelOffsets = new int[0]; - segmentData = levelOffsets; - } - - /** Returns the size of the key set. That is, the number of distinct elements in {@link #keys}. */ - public int size() { - return size; - } - - /** Returns whether this key set is empty. */ - public boolean isEmpty() { - return size() == 0; - } - - /** Returns whether this key set contains the given key. */ - public boolean contains(int key) { - return indexOf(key) >= 0; - } - - /** - * Searches the specified key, and returns its index in the element list. If multiple elements are - * equal to the specified key, there is no guarantee which one will be found. - * - * @return The index of the searched key if it is present; otherwise, {@code (-(insertion - * point) - 1)}. The insertion point is defined as the point at which the key would - * be inserted into the list: the index of the first element greater than the key, or {@link - * #keys}#{@code size()} if all the elements are less than the specified key. Note that this - * guarantees that the return value will be >= 0 if and only if the key is found. - */ - public int indexOf(int key) { - if (key < firstKey) { - return -1; - } - if (key > lastKey) { - return -keys.size() - 1; - } - final int[] segmentData = this.segmentData; - int segmentDataIndex = findSegment(key); - int nextIntercept = (int) getIntercept(segmentDataIndex + SEGMENT_DATA_SIZE, segmentData); - int index = - Math.min( - approximateIndex(key, segmentDataIndex, segmentData), - Math.min(nextIntercept, keys.size() - 1)); - assert index >= 0 && index < keys.size(); - int k = keys.get(index); - if (key < k) { - // Scan sequentially before the approximated index, within epsilon range. - final int fromIndex = Math.max(index - epsilon - 1, 0); - while (--index >= fromIndex) { - k = keys.get(index); - if (key > k) { - return -index - 2; - } - if (((key) == (k))) { - return index; - } - } - // Continue scanning out of the epsilon range. - // This might happen in rare cases of precision error during the approximation - // computation for longs (we don't have long double 128 bits in Java). - // This might also happen in rare corner cases of large duplicate elements - // sequence at the epsilon range boundary. - index++; - int jump = BEYOND_EPSILON_JUMP; - do { - int loIndex = Math.max(index - jump, 0); - if (key >= keys.get(loIndex)) { - return Arrays.binarySearch(keys.buffer, loIndex, index, key); - } - index = loIndex; - jump <<= 1; - } while (index > 0); - return -1; - } else if (((key) == (k))) { - return index; - } else { - // Scan sequentially after the approximated index, within epsilon range. - final int toIndex = Math.min(index + epsilon + 3, keys.size()); - while (++index < toIndex) { - k = keys.get(index); - if (key < k) { - return -index - 1; - } - if (((key) == (k))) { - return index; - } - } - // Continue scanning out of the epsilon range. - int jump = BEYOND_EPSILON_JUMP; - do { - int hiIndex = Math.min(index + jump, keys.size()); - if (key <= keys.get(hiIndex)) { - return Arrays.binarySearch(keys.buffer, index, hiIndex, key); - } - index = hiIndex; - jump <<= 1; - } while (index < keys.size()); - return -keys.size() - 1; - } - } - - /** - * Returns, for any value {@code x}, the number of keys in the sorted list which are smaller than - * {@code x}. It is equal to {@link #indexOf} if {@code x} belongs to the list, or -{@link - * #indexOf}-1 otherwise. - * - *

If multiple elements are equal to the specified key, there is no guarantee which one will be - * found. - * - * @return The index of the searched key if it is present; otherwise, the {@code insertion point}. - * The insertion point is defined as the point at which the key would be inserted into - * the list: the index of the first element greater than the key, or {@link #keys}#{@code - * size()} if all the elements are less than the specified key. Note that this method always - * returns a value >= 0. - */ - public int rank(int x) { - int index = indexOf(x); - return index >= 0 ? index : -index - 1; - } - - /** - * Returns the number of keys in the list that are greater than or equal to {@code minKey} - * (inclusive), and less than or equal to {@code maxKey} (inclusive). - */ - public int rangeCardinality(int minKey, int maxKey) { - int fromIndex = rank(minKey); - int maxIndex = indexOf(maxKey); - int toIndex = maxIndex >= 0 ? maxIndex + 1 : -maxIndex - 1; - return Math.max(toIndex - fromIndex, 0); - } - - /** - * Returns an iterator over the keys in the list that are greater than or equal to {@code minKey} - * (inclusive), and less than or equal to {@code maxKey} (inclusive). - */ - public Iterator rangeIterator(int minKey, int maxKey) { - int fromIndex = rank(minKey); - return new RangeIterator(keys, fromIndex, maxKey); - } - - /** - * Applies {@code procedure} to the keys in the list that are greater than or equal to {@code - * minKey} (inclusive), and less than or equal to {@code maxKey} (inclusive). - */ - public T forEachInRange(T procedure, int minKey, int maxKey) { - final int[] buffer = keys.buffer; - int k; - for (int i = rank(minKey), size = keys.size(); i < size && (k = buffer[i]) <= maxKey; i++) { - procedure.apply(k); - } - return procedure; - } - - /** - * Estimates the allocated memory. It does not count the memory for the list of keys, only for the - * index itself. - */ - @Override - public long ramBytesAllocated() { - // int: size, epsilon, epsilonRecursive - // int: firstKey, lastKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 3 * Integer.BYTES - + 2L * KEY_SIZE * Integer.BYTES - // + keys.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(levelOffsets) - + RamUsageEstimator.shallowSizeOfArray(segmentData); - } - - /** - * Estimates the bytes that are actually used. It does not count the memory for the list of keys, - * only for the index itself. - */ - @Override - public long ramBytesUsed() { - // int: size, epsilon, epsilonRecursive - // int: firstKey, lastKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 3 * Integer.BYTES - + 2L * KEY_SIZE * Integer.BYTES - // + keys.ramBytesUsed() - + RamUsageEstimator.shallowSizeOfArray(levelOffsets) - + RamUsageEstimator.shallowSizeOfArray(segmentData); - } - - /** - * Finds the segment responsible for a given key, that is, the rightmost segment having its first - * key <= the searched key. - * - * @return the segment data index; or -1 if none. - */ - private int findSegment(int key) { - assert key >= firstKey && key <= lastKey; - final int epsilonRecursive = this.epsilonRecursive; - final int[] levelOffsets = this.levelOffsets; - final int[] segmentData = this.segmentData; - int level = levelOffsets.length - 1; - int segmentDataIndex = levelOffsets[level] * SEGMENT_DATA_SIZE; - while (--level >= 0) { - int nextIntercept = (int) getIntercept(segmentDataIndex + SEGMENT_DATA_SIZE, segmentData); - int index = Math.min(approximateIndex(key, segmentDataIndex, segmentData), nextIntercept); - assert index >= 0 && index <= levelOffsets[level + 1] - levelOffsets[level] - 1; - int sdIndex = (levelOffsets[level] + index) * SEGMENT_DATA_SIZE; - if (getKey(sdIndex, segmentData) <= key) { - // Scan sequentially segments after the approximated index, within the epsilon range. - final int levelNumSegments = levelOffsets[level + 1] - levelOffsets[level] - 1; - final int toIndex = Math.min(index + epsilonRecursive + 3, levelNumSegments); - while (index++ < toIndex && getKey(sdIndex + SEGMENT_DATA_SIZE, segmentData) <= key) { - sdIndex += SEGMENT_DATA_SIZE; - } - } else { - // Scan sequentially segments before the approximated index, within the epsilon range. - final int fromIndex = Math.max(index - epsilonRecursive - 1, 0); - while (index-- > fromIndex) { - sdIndex -= SEGMENT_DATA_SIZE; - if (getKey(sdIndex, segmentData) <= key) { - break; - } - } - } - segmentDataIndex = sdIndex; - } - assert segmentDataIndex >= 0; - return segmentDataIndex; - } - - private int approximateIndex(int key, int segmentDataIndex, int[] segmentData) { - long intercept = getIntercept(segmentDataIndex, segmentData); - int sKey = getKey(segmentDataIndex, segmentData); - double slope = getSlope(segmentDataIndex, segmentData); - int index = (int) (slope * ((double) key - sKey) + intercept); - return Math.max(index, 0); - } - - private static long getIntercept(int segmentDataIndex, int[] segmentData) { - return PgmIndexUtil.getIntercept(segmentDataIndex, segmentData, KEY_SIZE); - } - - private int getKey(int segmentDataIndex, int[] segmentData) { - return PgmIndexUtil.getKey(segmentDataIndex + KEY_SIZE, segmentData, 0); - } - - private static double getSlope(int segmentDataIndex, int[] segmentData) { - return PgmIndexUtil.getSlope(segmentDataIndex + DOUBLE_KEY_SIZE, segmentData, KEY_SIZE); - } - - /** Empty immutable PGM Index. */ - private static class IntEmptyPgmIndex extends IntPgmIndex { - - private final Iterator emptyIterator = new IntEmptyIterator(); - - @Override - public int indexOf(int key) { - return -1; - } - - @Override - public Iterator rangeIterator(int minKey, int maxKey) { - return emptyIterator; - } - - @Override - public T forEachInRange(T procedure, int minKey, int maxKey) { - return procedure; - } - - private static class IntEmptyIterator extends AbstractIterator { - @Override - protected IntCursor fetch() { - return done(); - } - } - } - - /** Iterator over a range of elements in a sorted array. */ - protected static class RangeIterator extends AbstractIterator { - private final int[] buffer; - private final int size; - private final IntCursor cursor; - private final int maxKey; - - /** Range iterator from {@code fromIndex} (inclusive) to {@code maxKey} (inclusive). */ - protected RangeIterator(IntArrayList keys, int fromIndex, int maxKey) { - this.buffer = keys.buffer; - this.size = keys.size(); - this.cursor = new IntCursor(); - this.cursor.index = fromIndex; - this.maxKey = maxKey; - } - - @Override - protected IntCursor fetch() { - if (cursor.index >= size) { - return done(); - } - cursor.value = buffer[cursor.index++]; - if (cursor.value > maxKey) { - cursor.index = size; - return done(); - } - return cursor; - } - } - - /** Builds a {@link IntPgmIndex} on a provided sorted list of keys. */ - public static class IntBuilder implements PlaModel.SegmentConsumer, Accountable { - - protected IntArrayList keys; - protected int epsilon = EPSILON; - protected int epsilonRecursive = EPSILON_RECURSIVE; - protected PlaModel plam; - protected int size; - protected IntArrayList segmentData; - protected int numSegments; - - /** Sets the sorted list of keys to build the index for; duplicate elements are allowed. */ - public IntBuilder setSortedKeys(IntArrayList keys) { - this.keys = keys; - return this; - } - - /** Sets the sorted array of keys to build the index for; duplicate elements are allowed. */ - public IntBuilder setSortedKeys(int[] keys, int length) { - IntArrayList keyList = new IntArrayList(0); - keyList.buffer = keys; - keyList.elementsCount = length; - return setSortedKeys(keyList); - } - - /** Sets the epsilon range to use when learning the segments for the list of keys. */ - public IntBuilder setEpsilon(int epsilon) { - if (epsilon <= 0) { - throw new IllegalArgumentException("epsilon must be > 0"); - } - this.epsilon = epsilon; - return this; - } - - /** - * Sets the recursive epsilon range to use when learning the segments for the segment levels. - */ - public IntBuilder setEpsilonRecursive(int epsilonRecursive) { - if (epsilonRecursive <= 0) { - throw new IllegalArgumentException("epsilonRecursive must be > 0"); - } - this.epsilonRecursive = epsilonRecursive; - return this; - } - - /** Builds the {@link IntPgmIndex}; or {@link #EMPTY} if there are no keys in the list. */ - public IntPgmIndex build() { - if (keys == null || keys.size() == 0) { - return (IntPgmIndex) EMPTY; - } - plam = new PlaModel(epsilon); - - int segmentsInitialCapacity = - Math.min( - Math.max(keys.size() / (2 * epsilon * epsilon) * SEGMENT_DATA_SIZE, 16), 1 << 19); - segmentData = new IntArrayList(segmentsInitialCapacity); - IntArrayList levelOffsets = new IntArrayList(16); - - int levelOffset = 0; - levelOffsets.add(levelOffset); - int levelNumSegments = buildFirstLevel(); - while (levelNumSegments > 1) { - int nextLevelOffset = numSegments; - levelOffsets.add(nextLevelOffset); - levelNumSegments = buildUpperLevel(levelOffset, levelNumSegments); - levelOffset = nextLevelOffset; - } - - int[] segmentDataFinal = segmentData.toArray(); - int[] levelOffsetsFinal = levelOffsets.toArray(); - return new IntPgmIndex( - keys, size, epsilon, epsilonRecursive, levelOffsetsFinal, segmentDataFinal); - } - - private int buildFirstLevel() { - assert numSegments == 0; - int numKeys = keys.size(); - int size = 0; - int key = keys.get(0); - size++; - plam.addKey(key, 0, this); - for (int i = 1; i < numKeys; i++) { - int nextKey = keys.get(i); - if (!((nextKey) == (key))) { - key = nextKey; - plam.addKey(key, i, this); - size++; - } - } - plam.finish(this); - addSentinelSegment(numKeys); - this.size = size; - return numSegments - 1; - } - - private int buildUpperLevel(int levelOffset, int levelNumSegments) { - plam.setEpsilon(epsilonRecursive); - assert numSegments > 0; - int initialNumSegments = numSegments; - int segmentDataIndex = levelOffset * SEGMENT_DATA_SIZE; - int key = getKey(segmentDataIndex, segmentData.buffer); - plam.addKey(key, 0, this); - for (int i = 1; i < levelNumSegments; i++) { - segmentDataIndex += SEGMENT_DATA_SIZE; - int nextKey = getKey(segmentDataIndex, segmentData.buffer); - if (!((nextKey) == (key))) { - key = nextKey; - plam.addKey(key, i, this); - } - } - plam.finish(this); - addSentinelSegment(levelNumSegments); - return numSegments - initialNumSegments - 1; - } - - private int getKey(int segmentDataIndex, int[] segmentData) { - return PgmIndexUtil.getKey(segmentDataIndex + KEY_SIZE, segmentData, 0); - } - - /** - * Adds a sentinel segment that is used to give a limit for the position approximation, but does - * not count in the number of segments per level. - */ - private void addSentinelSegment(int endIndex) { - // This sentinel segment is used in findSegment(). - accept(Double.MAX_VALUE, 0d, endIndex); - } - - @Override - public void accept(double firstKey, double slope, long intercept) { - PgmIndexUtil.addIntercept(intercept, segmentData, KEY_SIZE); - PgmIndexUtil.addKey((int) firstKey, segmentData); - PgmIndexUtil.addSlope(slope, segmentData, KEY_SIZE); - numSegments++; - assert segmentData.size() == numSegments * SEGMENT_DATA_SIZE; - } - - /** - * Estimates the allocated memory. It does not count the memory for the list of keys, only for - * the builder itself. - */ - @Override - public long ramBytesAllocated() { - // int: epsilon, epsilonRecursive, size, numSegments - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - // + keys.ramBytesAllocated() - + plam.ramBytesAllocated() - + segmentData.ramBytesAllocated(); - } - - /** - * Estimates the bytes that are actually used. It does not count the memory for the list of - * keys, only for the builder itself. - */ - @Override - public long ramBytesUsed() { - // int: epsilon, epsilonRecursive, size, numSegments - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - // + keys.ramBytesUsed() - + plam.ramBytesUsed() - + segmentData.ramBytesUsed(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/IntSet.java b/src/main/java/com/carrotsearch/hppc/IntSet.java deleted file mode 100755 index 500b376e..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntSet.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.carrotsearch.hppc; - -/** A set of ints. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeSet.java") -public interface IntSet extends IntCollection { - /** - * Adds k to the set. - * - * @return Returns true if this element was not part of the set before. Returns - * false if an equal element is already part of the set, does not replace the - * existing element with the argument. - */ - public boolean add(int k); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); - - /** - * Adds all elements from the given {@link IntContainer} to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - * @since 0.9.1 - */ - public int addAll(IntContainer container); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntShortAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/IntShortAssociativeContainer.java deleted file mode 100755 index 918efde7..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntShortAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see IntContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface IntShortAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(int key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(IntShortPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link IntShortProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link IntShortPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public IntCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public ShortContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntShortHashMap.java b/src/main/java/com/carrotsearch/hppc/IntShortHashMap.java deleted file mode 100755 index c36b0fe2..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntShortHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of int to short, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeHashMap.java") -public class IntShortHashMap implements IntShortMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public int[] keys; - - /** The array holding values. */ - public short[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public IntShortHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public IntShortHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public IntShortHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public IntShortHashMap(IntShortAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public short put(int key, short value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - short previousValue = hasEmptyKey ? values[mask + 1] : ((short) 0); - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final short previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return ((short) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(IntShortAssociativeContainer container) { - final int count = size(); - for (IntShortCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (IntShortCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public short putOrAdd(int key, short putValue, short incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((short) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public short addTo(int key, short incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public short remove(int key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return ((short) 0); - } - hasEmptyKey = false; - short previousValue = values[mask + 1]; - values[mask + 1] = ((short) 0); - return previousValue; - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final short previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return ((short) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof IntLookupContainer) { - if (hasEmptyKey && other.contains(0)) { - hasEmptyKey = false; - values[mask + 1] = ((short) 0); - } - - final int[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - int existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (IntCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntShortPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(0, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = ((short) 0); - } - } - - final int[] keys = this.keys; - final short[] values = this.values; - for (int slot = 0; slot <= mask; ) { - int existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(IntPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(0)) { - hasEmptyKey = false; - values[mask + 1] = ((short) 0); - } - } - - final int[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - int existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public short get(int key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : ((short) 0); - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return ((short) 0); - } - } - - /** {@inheritDoc} */ - @Override - public short getOrDefault(int key, short defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(int key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final int[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(int key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final int[] keys = this.keys; - int slot = hashKey(key) & mask; - - int existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public short indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public short indexReplace(int index, short newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - short previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, int key, short value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public short indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - short previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = ((short) 0); - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, 0); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (IntShortCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(IntShortHashMap other) { - if (other.size() != size()) { - return false; - } - - for (IntShortCursor c : other) { - int key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final int[] prevKeys = this.keys; - final short[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final IntShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new IntShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntShortCursor fetch() { - final int mask = IntShortHashMap.this.mask; - while (index <= mask) { - int existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = 0; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final int[] keys = this.keys; - final short[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(0, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final int[] keys = this.keys; - final short[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(0, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractIntCollection implements IntLookupContainer { - private final IntShortHashMap owner = IntShortHashMap.this; - - @Override - public boolean contains(int e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((IntShortProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((IntShortPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(IntPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final int e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final IntCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new IntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntCursor fetch() { - final int mask = IntShortHashMap.this.mask; - while (index <= mask) { - int existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = 0; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public ShortCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractShortCollection { - private final IntShortHashMap owner = IntShortHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(short value) { - for (IntShortCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (IntShortCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (IntShortCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final short e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final ShortPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new ShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortCursor fetch() { - final int mask = IntShortHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public IntShortHashMap clone() { - try { - - IntShortHashMap cloned = (IntShortHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (IntShortCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return IntBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static IntShortHashMap from(int[] keys, short[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - IntShortHashMap map = new IntShortHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(int key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(int[] fromKeys, short[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final int[] keys = this.keys; - final short[] values = this.values; - final int mask = this.mask; - int existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - int[] prevKeys = this.keys; - short[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new int[arraySize + emptyElementSlot]); - this.values = (new short[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, int pendingKey, short pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final int[] prevKeys = this.keys; - final short[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final int[] keys = this.keys; - final short[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final int existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = 0; - values[gapSlot] = ((short) 0); - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/IntShortMap.java b/src/main/java/com/carrotsearch/hppc/IntShortMap.java deleted file mode 100755 index b0a5813a..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntShortMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.IntShortCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface IntShortMap extends IntShortAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public short get(int key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public short getOrDefault(int key, short defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public short put(int key, short value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(int key, short value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(IntShortAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public short putOrAdd(int key, short putValue, short incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public short addTo(int key, short additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public short remove(int key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link IntShortMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(int key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public short indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public short indexReplace(int index, short newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, int key, short value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public short indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/IntStack.java b/src/main/java/com/carrotsearch/hppc/IntStack.java deleted file mode 100755 index cd65ff61..00000000 --- a/src/main/java/com/carrotsearch/hppc/IntStack.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.IntCursor; - -/** - * A subclass of {@link IntArrayList} adding stack-related utility methods. The top of the stack is - * at the {@link #size()} - 1 element. - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeStack.java") -public class IntStack extends IntArrayList { - /** New instance with sane defaults. */ - public IntStack() { - super(); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public IntStack(int expectedElements) { - super(expectedElements); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public IntStack(int expectedElements, ArraySizingStrategy resizer) { - super(expectedElements, resizer); - } - - /** Create a stack by pushing all elements of another container to it. */ - public IntStack(IntContainer container) { - super(container); - } - - /** Adds one int to the stack. */ - public void push(int e1) { - ensureBufferSpace(1); - buffer[elementsCount++] = e1; - } - - /** Adds two ints to the stack. */ - public void push(int e1, int e2) { - ensureBufferSpace(2); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - } - - /** Adds three ints to the stack. */ - public void push(int e1, int e2, int e3) { - ensureBufferSpace(3); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - buffer[elementsCount++] = e3; - } - - /** Adds four ints to the stack. */ - public void push(int e1, int e2, int e3, int e4) { - ensureBufferSpace(4); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - buffer[elementsCount++] = e3; - buffer[elementsCount++] = e4; - } - - /** Add a range of array elements to the stack. */ - public void push(int[] elements, int start, int len) { - assert start >= 0 && len >= 0; - - ensureBufferSpace(len); - System.arraycopy(elements, start, buffer, elementsCount, len); - elementsCount += len; - } - - /** - * Vararg-signature method for pushing elements at the top of the stack. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - */ - public final void push(int... elements) { - push(elements, 0, elements.length); - } - - /** Pushes all elements from another container to the top of the stack. */ - public int pushAll(IntContainer container) { - return addAll(container); - } - - /** Pushes all elements from another iterable to the top of the stack. */ - public int pushAll(Iterable iterable) { - return addAll(iterable); - } - - /** Discard an arbitrary number of elements from the top of the stack. */ - public void discard(int count) { - assert elementsCount >= count; - - elementsCount -= count; - } - - /** Discard the top element from the stack. */ - public void discard() { - assert elementsCount > 0; - - elementsCount--; - } - - /** Remove the top element from the stack and return it. */ - public int pop() { - return removeLast(); - } - - /** Peek at the top element on the stack. */ - public int peek() { - assert elementsCount > 0; - return buffer[elementsCount - 1]; - } - - /** Create a stack by pushing a variable number of arguments to it. */ - public static IntStack from(int... elements) { - final IntStack stack = new IntStack(elements.length); - stack.push(elements); - return stack; - } - - /** {@inheritDoc} */ - @Override - public IntStack clone() { - return (IntStack) super.clone(); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/LongArrayDeque.java b/src/main/java/com/carrotsearch/hppc/LongArrayDeque.java deleted file mode 100755 index 8065aafd..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongArrayDeque.java +++ /dev/null @@ -1,776 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; - -import com.carrotsearch.hppc.cursors.LongCursor; -import com.carrotsearch.hppc.predicates.LongPredicate; -import com.carrotsearch.hppc.procedures.LongProcedure; -import java.util.*; - -/** An array-backed {@link LongDeque}. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeArrayDeque.java") -public class LongArrayDeque extends AbstractLongCollection - implements LongDeque, Preallocable, Cloneable, Accountable { - - /** Reuse the same strategy instance. */ - private static final BoundedProportionalArraySizingStrategy DEFAULT_SIZING_STRATEGY = - BoundedProportionalArraySizingStrategy.DEFAULT_INSTANCE; - - /** Internal array for storing elements of the deque. */ - public long[] buffer = LongArrayList.EMPTY_ARRAY; - - /** - * The index of the element at the head of the deque or an arbitrary number equal to tail if the - * deque is empty. - */ - public int head; - - /** The index at which the next element would be added to the tail of the deque. */ - public int tail; - - /** Buffer resizing strategy. */ - protected final ArraySizingStrategy resizer; - - /** New instance with sane defaults. */ - public LongArrayDeque() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public LongArrayDeque(int expectedElements) { - this(expectedElements, DEFAULT_SIZING_STRATEGY); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public LongArrayDeque(int expectedElements, ArraySizingStrategy resizer) { - assert resizer != null; - this.resizer = resizer; - ensureCapacity(expectedElements); - } - - /** - * Creates a new deque from elements of another container, appending elements at the end of the - * deque in the iteration order. - */ - public LongArrayDeque(LongContainer container) { - this(container.size()); - addLast(container); - } - - /** {@inheritDoc} */ - @Override - public void addFirst(long e1) { - int h = oneLeft(head, buffer.length); - if (h == tail) { - ensureBufferSpace(1); - h = oneLeft(head, buffer.length); - } - buffer[head = h] = e1; - } - - /** - * Vararg-signature method for adding elements at the front of this deque. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - * - * @param elements The elements to add. - */ - public final void addFirst(long... elements) { - ensureBufferSpace(elements.length); - for (long k : elements) { - addFirst(k); - } - } - - /** - * Inserts all elements from the given container to the front of this deque. - * - * @param container The container to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addFirst(LongContainer container) { - int size = container.size(); - ensureBufferSpace(size); - - for (LongCursor cursor : container) { - addFirst(cursor.value); - } - - return size; - } - - /** - * Inserts all elements from the given iterable to the front of this deque. - * - * @param iterable The iterable to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addFirst(Iterable iterable) { - int size = 0; - for (LongCursor cursor : iterable) { - addFirst(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public void addLast(long e1) { - int t = oneRight(tail, buffer.length); - if (head == t) { - ensureBufferSpace(1); - t = oneRight(tail, buffer.length); - } - buffer[tail] = e1; - tail = t; - } - - /** - * Vararg-signature method for adding elements at the end of this deque. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - * - * @param elements The elements to iterate over. - */ - public final void addLast(long... elements) { - ensureBufferSpace(1); - for (long k : elements) { - addLast(k); - } - } - - /** - * Inserts all elements from the given container to the end of this deque. - * - * @param container The container to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addLast(LongContainer container) { - int size = container.size(); - ensureBufferSpace(size); - - for (LongCursor cursor : container) { - addLast(cursor.value); - } - - return size; - } - - /** - * Inserts all elements from the given iterable to the end of this deque. - * - * @param iterable The iterable to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addLast(Iterable iterable) { - int size = 0; - for (LongCursor cursor : iterable) { - addLast(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public long removeFirst() { - assert size() > 0 : "The deque is empty."; - - final long result = buffer[head]; - buffer[head] = 0L; - head = oneRight(head, buffer.length); - return result; - } - - /** {@inheritDoc} */ - @Override - public long removeLast() { - assert size() > 0 : "The deque is empty."; - - tail = oneLeft(tail, buffer.length); - final long result = buffer[tail]; - buffer[tail] = 0L; - return result; - } - - /** {@inheritDoc} */ - @Override - public long getFirst() { - assert size() > 0 : "The deque is empty."; - - return buffer[head]; - } - - /** {@inheritDoc} */ - @Override - public long getLast() { - assert size() > 0 : "The deque is empty."; - - return buffer[oneLeft(tail, buffer.length)]; - } - - /** {@inheritDoc} */ - @Override - public int removeFirst(long e1) { - final int index = bufferIndexOf(e1); - if (index >= 0) removeAtBufferIndex(index); - return index; - } - - /** - * Return the index of the first (counting from head) element equal to e1. The index - * points to the {@link #buffer} array. - * - * @param e1 The element to look for. - * @return Returns the index of the first element equal to e1 or -1 if - * not found. - */ - public int bufferIndexOf(long e1) { - final int last = tail; - final int bufLen = buffer.length; - for (int i = head; i != last; i = oneRight(i, bufLen)) { - if (((e1) == (buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int removeLast(long e1) { - final int index = lastBufferIndexOf(e1); - if (index >= 0) { - removeAtBufferIndex(index); - } - return index; - } - - /** - * Return the index of the last (counting from tail) element equal to e1. The index - * points to the {@link #buffer} array. - * - * @param e1 The element to look for. - * @return Returns the index of the first element equal to e1 or -1 if - * not found. - */ - public int lastBufferIndexOf(long e1) { - final int bufLen = buffer.length; - final int last = oneLeft(head, bufLen); - for (int i = oneLeft(tail, bufLen); i != last; i = oneLeft(i, bufLen)) { - if (((e1) == (buffer[i]))) return i; - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(long e1) { - int removed = 0; - final int last = tail; - final int bufLen = buffer.length; - int from, to; - for (from = to = head; from != last; from = oneRight(from, bufLen)) { - if (((e1) == (buffer[from]))) { - buffer[from] = 0L; - removed++; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0L; - } - - to = oneRight(to, bufLen); - } - - tail = to; - return removed; - } - - /** - * Removes the element at index in the internal {#link {@link #buffer} array, - * returning its value. - * - * @param index Index of the element to remove. The index must be located between {@link #head} - * and {@link #tail} in modulo {@link #buffer} arithmetic. - */ - public void removeAtBufferIndex(int index) { - assert (head <= tail ? index >= head && index < tail : index >= head || index < tail) - : "Index out of range (head=" + head + ", tail=" + tail + ", index=" + index + ")."; - - // Cache fields in locals (hopefully moved to registers). - final long[] buffer = this.buffer; - final int bufLen = buffer.length; - final int lastIndex = bufLen - 1; - final int head = this.head; - final int tail = this.tail; - - final int leftChunk = Math.abs(index - head) % bufLen; - final int rightChunk = Math.abs(tail - index) % bufLen; - - if (leftChunk < rightChunk) { - if (index >= head) { - System.arraycopy(buffer, head, buffer, head + 1, leftChunk); - } else { - System.arraycopy(buffer, 0, buffer, 1, index); - buffer[0] = buffer[lastIndex]; - System.arraycopy(buffer, head, buffer, head + 1, lastIndex - head); - } - buffer[head] = 0L; - this.head = oneRight(head, bufLen); - } else { - if (index < tail) { - System.arraycopy(buffer, index + 1, buffer, index, rightChunk); - } else { - System.arraycopy(buffer, index + 1, buffer, index, lastIndex - index); - buffer[lastIndex] = buffer[0]; - System.arraycopy(buffer, 1, buffer, 0, tail); - } - buffer[tail] = 0L; - this.tail = oneLeft(tail, bufLen); - } - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int size() { - if (head <= tail) return tail - head; - else return (tail - head + buffer.length); - } - - /** - * {@inheritDoc} - * - *

The internal array buffers are not released as a result of this call. - * - * @see #release() - */ - @Override - public void clear() { - if (head < tail) { - Arrays.fill(buffer, head, tail, 0L); - } else { - Arrays.fill(buffer, 0, tail, 0L); - Arrays.fill(buffer, head, buffer.length, 0L); - } - this.head = tail = 0; - } - - /** Release internal buffers of this deque and reallocate with the default buffer. */ - public void release() { - this.head = tail = 0; - buffer = LongArrayList.EMPTY_ARRAY; - ensureBufferSpace(0); - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - ensureBufferSpace(expectedElements - size()); - } - - /** - * Ensures the internal buffer has enough free slots to store expectedAdditions. - * Increases internal buffer size if needed. - */ - protected void ensureBufferSpace(int expectedAdditions) { - final int bufferLen = buffer.length; - final int elementsCount = size(); - - if (elementsCount + expectedAdditions >= bufferLen) { - final int emptySlot = 1; // deque invariant: always an empty slot. - final int newSize = resizer.grow(bufferLen, elementsCount + emptySlot, expectedAdditions); - assert newSize >= (elementsCount + expectedAdditions + emptySlot) - : "Resizer failed to" - + " return sensible new size: " - + newSize - + " <= " - + (elementsCount + expectedAdditions); - - try { - final long[] newBuffer = (new long[newSize]); - if (bufferLen > 0) { - toArray(newBuffer); - tail = elementsCount; - head = 0; - } - this.buffer = newBuffer; - } catch (OutOfMemoryError e) { - throw new BufferAllocationException( - "Not enough memory to allocate new buffers: %,d -> %,d", e, bufferLen, newSize); - } - } - } - - /** {@inheritDoc} */ - @Override - public long[] toArray() { - - final int size = size(); - return toArray((new long[size])); - } - - /** - * Copies elements of this deque to an array. The content of the target array is - * filled from index 0 (head of the queue) to index size() - 1 (tail of the queue). - * - * @param target The target array must be large enough to hold all elements. - * @return Returns the target argument for chaining. - */ - public long[] toArray(long[] target) { - assert target.length >= size() : "Target array must be >= " + size(); - - if (head < tail) { - // The contents is not wrapped around. Just copy. - System.arraycopy(buffer, head, target, 0, size()); - } else if (head > tail) { - // The contents is split. Merge elements from the following indexes: - // [head...buffer.length - 1][0, tail - 1] - final int rightCount = buffer.length - head; - System.arraycopy(buffer, head, target, 0, rightCount); - System.arraycopy(buffer, 0, target, rightCount, tail); - } - - return target; - } - - /** - * Clone this object. The returned clone will reuse the same hash function and array resizing - * strategy. - */ - @Override - public LongArrayDeque clone() { - try { - - LongArrayDeque cloned = (LongArrayDeque) super.clone(); - cloned.buffer = buffer.clone(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Move one index to the left, wrapping around buffer. */ - protected static int oneLeft(int index, int modulus) { - if (index >= 1) { - return index - 1; - } - return modulus - 1; - } - - /** Move one index to the right, wrapping around buffer. */ - protected static int oneRight(int index, int modulus) { - if (index + 1 == modulus) { - return 0; - } - return index + 1; - } - - @Override - public long ramBytesAllocated() { - // int: head, tail - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES * 2 - + resizer.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(buffer); - } - - @Override - public long ramBytesUsed() { - // int: head, tail - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES * 2 - + resizer.ramBytesUsed() - + RamUsageEstimator.shallowUsedSizeOfArray(buffer, size()); - } - - /** An iterator implementation for {@link ObjectArrayDeque#iterator}. */ - private final class ValueIterator extends AbstractIterator { - private final LongCursor cursor; - private int remaining; - - public ValueIterator() { - cursor = new LongCursor(); - cursor.index = oneLeft(head, buffer.length); - this.remaining = size(); - } - - @Override - protected LongCursor fetch() { - if (remaining == 0) { - return done(); - } - - remaining--; - cursor.value = buffer[cursor.index = oneRight(cursor.index, buffer.length)]; - return cursor; - } - } - - /** An iterator implementation for {@link ObjectArrayDeque#descendingIterator()}. */ - private final class DescendingValueIterator extends AbstractIterator { - private final LongCursor cursor; - private int remaining; - - public DescendingValueIterator() { - cursor = new LongCursor(); - cursor.index = tail; - this.remaining = size(); - } - - @Override - protected LongCursor fetch() { - if (remaining == 0) return done(); - - remaining--; - cursor.value = buffer[cursor.index = oneLeft(cursor.index, buffer.length)]; - return cursor; - } - } - - /** - * Returns a cursor over the values of this deque (in head to tail order). The iterator is - * implemented as a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()} (to avoid boxing of primitive types). To read the current value (or index in - * the deque's buffer) use the cursor's public fields. An example is shown below. - * - *

-   * for (IntValueCursor c : intDeque) {
-   *   System.out.println("buffer index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator iterator() { - return new ValueIterator(); - } - - /** - * Returns a cursor over the values of this deque (in tail to head order). The iterator is - * implemented as a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()} (to avoid boxing of primitive types). To read the current value (or index in - * the deque's buffer) use the cursor's public fields. An example is shown below. - * - *
-   * for (Iterator<IntCursor> i = intDeque.descendingIterator(); i.hasNext();) {
-   *   final IntCursor c = i.next();
-   *   System.out.println("buffer index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator descendingIterator() { - return new DescendingValueIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - forEach(procedure, head, tail); - return procedure; - } - - /** - * Applies procedure to a slice of the deque, fromIndex, inclusive, to - * toIndex, exclusive. - */ - private void forEach(LongProcedure procedure, int fromIndex, final int toIndex) { - final long[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - procedure.apply(buffer[i]); - } - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - int fromIndex = head; - int toIndex = tail; - - final long[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - if (!predicate.apply(buffer[i])) { - break; - } - } - - return predicate; - } - - /** Applies procedure to all elements of this deque, tail to head. */ - @Override - public T descendingForEach(T procedure) { - descendingForEach(procedure, head, tail); - return procedure; - } - - /** - * Applies procedure to a slice of the deque, toIndex, exclusive, down - * to fromIndex, inclusive. - */ - private void descendingForEach(LongProcedure procedure, int fromIndex, final int toIndex) { - if (fromIndex == toIndex) return; - - final long[] buffer = this.buffer; - int i = toIndex; - do { - i = oneLeft(i, buffer.length); - procedure.apply(buffer[i]); - } while (i != fromIndex); - } - - /** {@inheritDoc} */ - @Override - public T descendingForEach(T predicate) { - descendingForEach(predicate, head, tail); - return predicate; - } - - /** - * Applies predicate to a slice of the deque, toIndex, exclusive, down - * to fromIndex, inclusive or until the predicate returns false. - */ - private void descendingForEach(LongPredicate predicate, int fromIndex, final int toIndex) { - if (fromIndex == toIndex) return; - - final long[] buffer = this.buffer; - int i = toIndex; - do { - i = oneLeft(i, buffer.length); - if (!predicate.apply(buffer[i])) { - break; - } - } while (i != fromIndex); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongPredicate predicate) { - final long[] buffer = this.buffer; - final int last = tail; - final int bufLen = buffer.length; - int removed = 0; - int from, to; - from = to = head; - try { - for (from = to = head; from != last; from = oneRight(from, bufLen)) { - if (predicate.apply(buffer[from])) { - buffer[from] = 0L; - removed++; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0L; - } - - to = oneRight(to, bufLen); - } - } finally { - // Keep the deque in consistent state even if the predicate throws an exception. - for (; from != last; from = oneRight(from, bufLen)) { - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0L; - } - - to = oneRight(to, bufLen); - } - tail = to; - } - - return removed; - } - - /** {@inheritDoc} */ - @Override - public boolean contains(long e) { - int fromIndex = head; - int toIndex = tail; - - final long[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - if (((e) == (buffer[i]))) { - return true; - } - } - - return false; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = 1; - int fromIndex = head; - int toIndex = tail; - - final long[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - h = 31 * h + BitMixer.mix(this.buffer[i]); - } - return h; - } - - /** - * Returns true only if the other object is an instance of the same class and with - * the same elements. - */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Compare order-aligned elements against another {@link LongDeque}. */ - protected boolean equalElements(LongArrayDeque other) { - int max = size(); - if (other.size() != max) { - return false; - } - - Iterator i1 = this.iterator(); - Iterator i2 = other.iterator(); - - while (i1.hasNext() && i2.hasNext()) { - if (!((i1.next().value) == (i2.next().value))) { - return false; - } - } - - return !i1.hasNext() && !i2.hasNext(); - } - - /** Create a new deque by pushing a variable number of arguments to the end of it. */ - public static LongArrayDeque from(long... elements) { - final LongArrayDeque coll = new LongArrayDeque(elements.length); - coll.addLast(elements); - return coll; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/LongArrayList.java b/src/main/java/com/carrotsearch/hppc/LongArrayList.java deleted file mode 100755 index ea99bfeb..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongArrayList.java +++ /dev/null @@ -1,586 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.LongPredicate; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; -import java.util.stream.LongStream; - -/** An array-backed list of longs. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeArrayList.java") -public class LongArrayList extends AbstractLongCollection - implements LongIndexedContainer, Preallocable, Cloneable, Accountable { - /** An immutable empty buffer (array). */ - public static final long[] EMPTY_ARRAY = new long[0]; - - ; - - /** Reuse the same strategy instance. */ - private static final BoundedProportionalArraySizingStrategy DEFAULT_SIZING_STRATEGY = - BoundedProportionalArraySizingStrategy.DEFAULT_INSTANCE; - - /** - * Internal array for storing the list. The array may be larger than the current size ({@link - * #size()}). - */ - public long[] buffer = EMPTY_ARRAY; - - /** Current number of elements stored in {@link #buffer}. */ - public int elementsCount; - - /** Buffer resizing strategy. */ - protected final ArraySizingStrategy resizer; - - /** New instance with sane defaults. */ - public LongArrayList() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public LongArrayList(int expectedElements) { - this(expectedElements, DEFAULT_SIZING_STRATEGY); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public LongArrayList(int expectedElements, ArraySizingStrategy resizer) { - assert resizer != null; - this.resizer = resizer; - buffer = Arrays.copyOf(buffer, expectedElements); - } - - /** Creates a new list from the elements of another container in its iteration order. */ - public LongArrayList(LongContainer container) { - this(container.size()); - addAll(container); - } - - /** {@inheritDoc} */ - @Override - public void add(long e1) { - ensureBufferSpace(1); - buffer[elementsCount++] = e1; - } - - /** - * Appends two elements at the end of the list. To add more than two elements, use add - * (vararg-version) or access the buffer directly (tight loop). - */ - public void add(long e1, long e2) { - ensureBufferSpace(2); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - } - - /** Add all elements from a range of given array to the list. */ - public void add(long[] elements, int start, int length) { - assert length >= 0 : "Length must be >= 0"; - - ensureBufferSpace(length); - System.arraycopy(elements, start, buffer, elementsCount, length); - elementsCount += length; - } - - /** - * Vararg-signature method for adding elements at the end of the list. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - */ - public final void add(long... elements) { - add(elements, 0, elements.length); - } - - /** Adds all elements from another container. */ - public int addAll(LongContainer container) { - final int size = container.size(); - ensureBufferSpace(size); - - for (LongCursor cursor : container) { - add(cursor.value); - } - - return size; - } - - /** Adds all elements from another iterable. */ - public int addAll(Iterable iterable) { - int size = 0; - for (LongCursor cursor : iterable) { - add(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public void insert(int index, long e1) { - assert (index >= 0 && index <= size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + "]."; - - ensureBufferSpace(1); - System.arraycopy(buffer, index, buffer, index + 1, elementsCount - index); - buffer[index] = e1; - elementsCount++; - } - - /** {@inheritDoc} */ - @Override - public long get(int index) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - return buffer[index]; - } - - /** {@inheritDoc} */ - @Override - public long set(int index, long e1) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - final long v = buffer[index]; - buffer[index] = e1; - return v; - } - - /** {@inheritDoc} */ - @Override - public long removeAt(int index) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - final long v = buffer[index]; - System.arraycopy(buffer, index + 1, buffer, index, --elementsCount - index); - - return v; - } - - /** {@inheritDoc} */ - @Override - public long removeLast() { - assert elementsCount > 0; - - final long v = buffer[--elementsCount]; - - return v; - } - - /** {@inheritDoc} */ - @Override - public void removeRange(int fromIndex, int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - System.arraycopy(buffer, toIndex, buffer, fromIndex, elementsCount - toIndex); - final int count = toIndex - fromIndex; - elementsCount -= count; - } - - /** {@inheritDoc} */ - @Override - public boolean removeElement(long e1) { - return removeFirst(e1) != -1; - } - - /** {@inheritDoc} */ - @Override - public int removeFirst(long e1) { - final int index = indexOf(e1); - if (index >= 0) removeAt(index); - return index; - } - - /** {@inheritDoc} */ - @Override - public int removeLast(long e1) { - final int index = lastIndexOf(e1); - if (index >= 0) removeAt(index); - return index; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(long e1) { - int to = 0; - for (int from = 0; from < elementsCount; from++) { - if (((e1) == (buffer[from]))) { - continue; - } - if (to != from) { - buffer[to] = buffer[from]; - } - to++; - } - final int deleted = elementsCount - to; - this.elementsCount = to; - - return deleted; - } - - /** {@inheritDoc} */ - @Override - public boolean contains(long e1) { - return indexOf(e1) >= 0; - } - - /** {@inheritDoc} */ - @Override - public int indexOf(long e1) { - for (int i = 0; i < elementsCount; i++) { - if (((e1) == (buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int lastIndexOf(long e1) { - for (int i = elementsCount - 1; i >= 0; i--) { - if (((e1) == (buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return elementsCount == 0; - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - final int bufferLen = (buffer == null ? 0 : buffer.length); - if (expectedElements > bufferLen) { - ensureBufferSpace(expectedElements - size()); - } - } - - /** - * Ensures the internal buffer has enough free slots to store expectedAdditions. - * Increases internal buffer size if needed. - */ - protected void ensureBufferSpace(int expectedAdditions) { - final int bufferLen = (buffer == null ? 0 : buffer.length); - if (elementsCount + expectedAdditions > bufferLen) { - final int newSize = resizer.grow(bufferLen, elementsCount, expectedAdditions); - assert newSize >= elementsCount + expectedAdditions - : "Resizer failed to" - + " return sensible new size: " - + newSize - + " <= " - + (elementsCount + expectedAdditions); - - this.buffer = Arrays.copyOf(buffer, newSize); - } - } - - /** - * Truncate or expand the list to the new size. If the list is truncated, the buffer will not be - * reallocated (use {@link #trimToSize()} if you need a truncated buffer), but the truncated - * values will be reset to the default value (zero). If the list is expanded, the elements beyond - * the current size are initialized with JVM-defaults (zero or null values). - */ - public void resize(int newSize) { - if (newSize <= buffer.length) { - if (newSize < elementsCount) { - Arrays.fill(buffer, newSize, elementsCount, 0L); - } else { - Arrays.fill(buffer, elementsCount, newSize, 0L); - } - } else { - ensureCapacity(newSize); - } - this.elementsCount = newSize; - } - - /** {@inheritDoc} */ - @Override - public int size() { - return elementsCount; - } - - /** Trim the internal buffer to the current size. */ - public void trimToSize() { - if (size() != this.buffer.length) { - this.buffer = toArray(); - } - } - - /** - * Sets the number of stored elements to zero. Releases and initializes the internal storage array - * to default values. To clear the list without cleaning the buffer, simply set the {@link - * #elementsCount} field to zero. - */ - @Override - public void clear() { - Arrays.fill(buffer, 0, elementsCount, 0L); - this.elementsCount = 0; - } - - /** Sets the number of stored elements to zero and releases the internal storage array. */ - @Override - public void release() { - this.buffer = EMPTY_ARRAY; - this.elementsCount = 0; - } - - /** - * {@inheritDoc} - * - *

The returned array is sized to match exactly the number of elements of the stack. - */ - @Override - public long[] toArray() { - - return Arrays.copyOf(buffer, elementsCount); - } - - @Override - public LongStream stream() { - - return Arrays.stream(buffer, 0, size()); - } - - /** {@inheritDoc} */ - @Override - public LongIndexedContainer sort() { - Arrays.sort(buffer, 0, elementsCount); - return this; - } - - /** {@inheritDoc} */ - @Override - public LongIndexedContainer reverse() { - for (int i = 0, mid = elementsCount >> 1, j = elementsCount - 1; i < mid; i++, j--) { - long tmp = buffer[i]; - buffer[i] = buffer[j]; - buffer[j] = tmp; - } - return this; - } - - /** - * Clone this object. The returned clone will reuse the same hash function and array resizing - * strategy. - */ - @Override - public LongArrayList clone() { - try { - - final LongArrayList cloned = (LongArrayList) super.clone(); - cloned.buffer = buffer.clone(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = 1, max = elementsCount; - for (int i = 0; i < max; i++) { - h = 31 * h + BitMixer.mix(this.buffer[i]); - } - return h; - } - - /** - * Returns true only if the other object is an instance of the same class and with - * the same elements. - */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Compare index-aligned elements against another {@link LongIndexedContainer}. */ - protected boolean equalElements(LongArrayList other) { - int max = size(); - if (other.size() != max) { - return false; - } - - for (int i = 0; i < max; i++) { - if (!((get(i)) == (other.get(i)))) { - return false; - } - } - - return true; - } - - @Override - public long ramBytesAllocated() { - // int: elementsCount - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES - + resizer.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(buffer); - } - - @Override - public long ramBytesUsed() { - // int: elementsCount - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES - + resizer.ramBytesUsed() - + RamUsageEstimator.shallowUsedSizeOfArray(buffer, elementsCount); - } - - /** An iterator implementation for {@link LongArrayList#iterator}. */ - static final class ValueIterator extends AbstractIterator { - private final LongCursor cursor; - - private final long[] buffer; - private final int size; - - public ValueIterator(long[] buffer, int size) { - this.cursor = new LongCursor(); - this.cursor.index = -1; - this.size = size; - this.buffer = buffer; - } - - @Override - protected LongCursor fetch() { - if (cursor.index + 1 == size) return done(); - - cursor.value = buffer[++cursor.index]; - return cursor; - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new ValueIterator(buffer, size()); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - return forEach(procedure, 0, size()); - } - - /** - * Applies procedure to a slice of the list, fromIndex, inclusive, to - * toIndex, exclusive. - */ - public T forEach(T procedure, int fromIndex, final int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - final long[] buffer = this.buffer; - for (int i = fromIndex; i < toIndex; i++) { - procedure.apply(buffer[i]); - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongPredicate predicate) { - final long[] buffer = this.buffer; - final int elementsCount = this.elementsCount; - int to = 0; - int from = 0; - try { - for (; from < elementsCount; from++) { - if (predicate.apply(buffer[from])) { - buffer[from] = 0L; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0L; - } - to++; - } - } finally { - // Keep the list in a consistent state, even if the predicate throws an exception. - for (; from < elementsCount; from++) { - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = 0L; - } - to++; - } - - this.elementsCount = to; - } - - return elementsCount - to; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - return forEach(predicate, 0, size()); - } - - /** - * Applies predicate to a slice of the list, fromIndex, inclusive, to - * toIndex, exclusive, or until predicate returns false. - */ - public T forEach(T predicate, int fromIndex, final int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - final long[] buffer = this.buffer; - for (int i = fromIndex; i < toIndex; i++) { - if (!predicate.apply(buffer[i])) break; - } - - return predicate; - } - - /** - * Create a list from a variable number of arguments or an array of long. The - * elements are copied from the argument to the internal buffer. - */ - public static LongArrayList from(long... elements) { - final LongArrayList list = new LongArrayList(elements.length); - list.add(elements); - return list; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/LongBufferVisualizer.java b/src/main/java/com/carrotsearch/hppc/LongBufferVisualizer.java deleted file mode 100755 index 1c9e0af9..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongBufferVisualizer.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.carrotsearch.hppc; - -/** - * Reused buffer visualization routines. - * - * @see LongSet#visualizeKeyDistribution(int) - * @see LongVTypeMap#visualizeKeyDistribution(int) - */ -class LongBufferVisualizer { - static String visualizeKeyDistribution(long[] buffer, int max, int characters) { - final StringBuilder b = new StringBuilder(); - final char[] chars = ".123456789X".toCharArray(); - for (int i = 1, start = -1; i <= characters; i++) { - int end = (int) ((long) i * max / characters); - - if (start + 1 <= end) { - int taken = 0; - int slots = 0; - for (int slot = start + 1; slot <= end; slot++, slots++) { - if (!((buffer[slot]) == 0)) { - taken++; - } - } - b.append(chars[Math.min(chars.length - 1, taken * chars.length / slots)]); - start = end; - } - } - while (b.length() < characters) { - b.append(' '); - } - return b.toString(); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/LongByteAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/LongByteAssociativeContainer.java deleted file mode 100755 index b3e17bfa..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongByteAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see LongContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface LongByteAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(long key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongBytePredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link LongByteProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link LongBytePredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public LongCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public ByteContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongByteHashMap.java b/src/main/java/com/carrotsearch/hppc/LongByteHashMap.java deleted file mode 100755 index eeaf5028..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongByteHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of long to byte, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeHashMap.java") -public class LongByteHashMap implements LongByteMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public long[] keys; - - /** The array holding values. */ - public byte[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public LongByteHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public LongByteHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public LongByteHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public LongByteHashMap(LongByteAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public byte put(long key, byte value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - byte previousValue = hasEmptyKey ? values[mask + 1] : ((byte) 0); - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final byte previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return ((byte) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(LongByteAssociativeContainer container) { - final int count = size(); - for (LongByteCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (LongByteCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public byte putOrAdd(long key, byte putValue, byte incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((byte) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public byte addTo(long key, byte incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public byte remove(long key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return ((byte) 0); - } - hasEmptyKey = false; - byte previousValue = values[mask + 1]; - values[mask + 1] = ((byte) 0); - return previousValue; - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final byte previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return ((byte) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof LongLookupContainer) { - if (hasEmptyKey && other.contains(0L)) { - hasEmptyKey = false; - values[mask + 1] = ((byte) 0); - } - - final long[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - long existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (LongCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongBytePredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(0L, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = ((byte) 0); - } - } - - final long[] keys = this.keys; - final byte[] values = this.values; - for (int slot = 0; slot <= mask; ) { - long existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(0L)) { - hasEmptyKey = false; - values[mask + 1] = ((byte) 0); - } - } - - final long[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - long existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public byte get(long key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : ((byte) 0); - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return ((byte) 0); - } - } - - /** {@inheritDoc} */ - @Override - public byte getOrDefault(long key, byte defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(long key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(long key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public byte indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public byte indexReplace(int index, byte newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - byte previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, long key, byte value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public byte indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - byte previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = ((byte) 0); - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, 0L); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (LongByteCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(LongByteHashMap other) { - if (other.size() != size()) { - return false; - } - - for (LongByteCursor c : other) { - long key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final long[] prevKeys = this.keys; - final byte[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final LongByteCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new LongByteCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongByteCursor fetch() { - final int mask = LongByteHashMap.this.mask; - while (index <= mask) { - long existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = 0L; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final long[] keys = this.keys; - final byte[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(0L, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final long[] keys = this.keys; - final byte[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(0L, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractLongCollection implements LongLookupContainer { - private final LongByteHashMap owner = LongByteHashMap.this; - - @Override - public boolean contains(long e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((LongByteProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((LongBytePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(LongPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final long e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final LongCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new LongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongCursor fetch() { - final int mask = LongByteHashMap.this.mask; - while (index <= mask) { - long existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = 0L; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public ByteCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractByteCollection { - private final LongByteHashMap owner = LongByteHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(byte value) { - for (LongByteCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (LongByteCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (LongByteCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final byte e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final BytePredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ByteCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new ByteCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ByteCursor fetch() { - final int mask = LongByteHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public LongByteHashMap clone() { - try { - - LongByteHashMap cloned = (LongByteHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (LongByteCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return LongBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static LongByteHashMap from(long[] keys, byte[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - LongByteHashMap map = new LongByteHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(long key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(long[] fromKeys, byte[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final long[] keys = this.keys; - final byte[] values = this.values; - final int mask = this.mask; - long existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - long[] prevKeys = this.keys; - byte[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new long[arraySize + emptyElementSlot]); - this.values = (new byte[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, long pendingKey, byte pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final long[] prevKeys = this.keys; - final byte[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final long[] keys = this.keys; - final byte[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final long existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = 0L; - values[gapSlot] = ((byte) 0); - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/LongByteMap.java b/src/main/java/com/carrotsearch/hppc/LongByteMap.java deleted file mode 100755 index 1291678d..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongByteMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.LongByteCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface LongByteMap extends LongByteAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public byte get(long key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public byte getOrDefault(long key, byte defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public byte put(long key, byte value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(long key, byte value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(LongByteAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public byte putOrAdd(long key, byte putValue, byte incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public byte addTo(long key, byte additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public byte remove(long key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link LongByteMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(long key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public byte indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public byte indexReplace(int index, byte newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, long key, byte value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public byte indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongCharAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/LongCharAssociativeContainer.java deleted file mode 100755 index 9188f4e5..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongCharAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see LongContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface LongCharAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(long key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongCharPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link LongCharProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link LongCharPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public LongCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public CharContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongCharHashMap.java b/src/main/java/com/carrotsearch/hppc/LongCharHashMap.java deleted file mode 100755 index c5262ab9..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongCharHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of long to char, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeHashMap.java") -public class LongCharHashMap implements LongCharMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public long[] keys; - - /** The array holding values. */ - public char[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public LongCharHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public LongCharHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public LongCharHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public LongCharHashMap(LongCharAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public char put(long key, char value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - char previousValue = hasEmptyKey ? values[mask + 1] : ((char) 0); - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final char previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return ((char) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(LongCharAssociativeContainer container) { - final int count = size(); - for (LongCharCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (LongCharCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public char putOrAdd(long key, char putValue, char incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((char) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public char addTo(long key, char incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public char remove(long key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return ((char) 0); - } - hasEmptyKey = false; - char previousValue = values[mask + 1]; - values[mask + 1] = ((char) 0); - return previousValue; - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final char previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return ((char) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof LongLookupContainer) { - if (hasEmptyKey && other.contains(0L)) { - hasEmptyKey = false; - values[mask + 1] = ((char) 0); - } - - final long[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - long existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (LongCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongCharPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(0L, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = ((char) 0); - } - } - - final long[] keys = this.keys; - final char[] values = this.values; - for (int slot = 0; slot <= mask; ) { - long existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(0L)) { - hasEmptyKey = false; - values[mask + 1] = ((char) 0); - } - } - - final long[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - long existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public char get(long key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : ((char) 0); - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return ((char) 0); - } - } - - /** {@inheritDoc} */ - @Override - public char getOrDefault(long key, char defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(long key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(long key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public char indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public char indexReplace(int index, char newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - char previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, long key, char value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public char indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - char previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = ((char) 0); - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, 0L); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (LongCharCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(LongCharHashMap other) { - if (other.size() != size()) { - return false; - } - - for (LongCharCursor c : other) { - long key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final long[] prevKeys = this.keys; - final char[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final LongCharCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new LongCharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongCharCursor fetch() { - final int mask = LongCharHashMap.this.mask; - while (index <= mask) { - long existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = 0L; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final long[] keys = this.keys; - final char[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(0L, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final long[] keys = this.keys; - final char[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(0L, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractLongCollection implements LongLookupContainer { - private final LongCharHashMap owner = LongCharHashMap.this; - - @Override - public boolean contains(long e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((LongCharProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((LongCharPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(LongPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final long e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final LongCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new LongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongCursor fetch() { - final int mask = LongCharHashMap.this.mask; - while (index <= mask) { - long existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = 0L; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public CharCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractCharCollection { - private final LongCharHashMap owner = LongCharHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(char value) { - for (LongCharCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (LongCharCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (LongCharCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final char e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final CharPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final CharCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new CharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharCursor fetch() { - final int mask = LongCharHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public LongCharHashMap clone() { - try { - - LongCharHashMap cloned = (LongCharHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (LongCharCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return LongBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static LongCharHashMap from(long[] keys, char[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - LongCharHashMap map = new LongCharHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(long key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(long[] fromKeys, char[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final long[] keys = this.keys; - final char[] values = this.values; - final int mask = this.mask; - long existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - long[] prevKeys = this.keys; - char[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new long[arraySize + emptyElementSlot]); - this.values = (new char[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, long pendingKey, char pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final long[] prevKeys = this.keys; - final char[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final long[] keys = this.keys; - final char[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final long existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = 0L; - values[gapSlot] = ((char) 0); - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/LongCharMap.java b/src/main/java/com/carrotsearch/hppc/LongCharMap.java deleted file mode 100755 index fbc9d681..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongCharMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.LongCharCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface LongCharMap extends LongCharAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public char get(long key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public char getOrDefault(long key, char defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public char put(long key, char value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(long key, char value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(LongCharAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public char putOrAdd(long key, char putValue, char incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public char addTo(long key, char additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public char remove(long key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link LongCharMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(long key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public char indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public char indexReplace(int index, char newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, long key, char value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public char indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongCollection.java b/src/main/java/com/carrotsearch/hppc/LongCollection.java deleted file mode 100755 index 65869b85..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongCollection.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.predicates.LongPredicate; - -/** - * A collection allows basic, efficient operations on sets of elements (difference and - * intersection). - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeCollection.java") -public interface LongCollection extends LongContainer { - /** - * Removes all occurrences of e from this collection. - * - * @param e Element to be removed from this collection, if present. - * @return The number of removed elements as a result of this call. - */ - public int removeAll(long e); - - /** - * Removes all elements in this collection that are present in c. - * - * @return Returns the number of removed elements. - */ - public int removeAll(LongLookupContainer c); - - /** - * Removes all elements in this collection for which the given predicate returns true - * . - * - * @return Returns the number of removed elements. - */ - public int removeAll(LongPredicate predicate); - - /** - * Keeps all elements in this collection that are present in c. Runs in time - * proportional to the number of elements in this collection. Equivalent of sets intersection. - * - * @return Returns the number of removed elements. - */ - public int retainAll(LongLookupContainer c); - - /** - * Keeps all elements in this collection for which the given predicate returns true. - * - * @return Returns the number of removed elements. - */ - public int retainAll(LongPredicate predicate); - - /** - * Removes all elements from this collection. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongContainer.java b/src/main/java/com/carrotsearch/hppc/LongContainer.java deleted file mode 100755 index cc10b05a..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongContainer.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.LongCursor; -import com.carrotsearch.hppc.predicates.LongPredicate; -import com.carrotsearch.hppc.procedures.LongProcedure; -import java.util.Iterator; - -/** A generic container holding longs. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeContainer.java") -public interface LongContainer extends Iterable { - /** - * Returns an iterator to a cursor traversing the collection. The order of traversal is not - * defined. More than one cursor may be active at a time. The behavior of iterators is undefined - * if structural changes are made to the underlying collection. - * - *

The iterator is implemented as a cursor and it returns the same cursor instance on - * every call to {@link Iterator#next()} (to avoid boxing of primitive types). To read the current - * list's value (or index in the list) use the cursor's public fields. An example is shown below. - * - *

-   * for (LongCursor<long> c : container) {
-   *   System.out.println("index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator iterator(); - - /** - * Lookup a given element in the container. This operation has no speed guarantees (may be linear - * with respect to the size of this container). - * - * @return Returns true if this container has an element equal to e. - */ - public boolean contains(long e); - - /** - * Return the current number of elements in this container. The time for calculating the - * container's size may take O(n) time, although implementing classes should try to - * maintain the current size and return in constant time. - */ - public int size(); - - /** Shortcut for size() == 0. */ - public boolean isEmpty(); - - /** - * Copies all elements of this container to an array. - * - *

The returned array is always a copy, regardless of the storage used by the container. - */ - public long[] toArray(); - - /** - * Applies a procedure to all container elements. Returns the argument (any subclass - * of {@link LongProcedure}. This lets the caller to call methods of the argument by chaining the - * call (even if the argument is an anonymous type) to retrieve computed values, for example - * (IntContainer): - * - *

-   * int count = container.forEach(new IntProcedure() {
-   *   int count; // this is a field declaration in an anonymous class.
-   *
-   *   public void apply(int value) {
-   *     count++;
-   *   }
-   * }).count;
-   * 
- */ - public T forEach(T procedure); - - /** - * Applies a predicate to container elements as long, as the predicate returns - * true. The iteration is interrupted otherwise. - */ - public T forEach(T predicate); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongDeque.java b/src/main/java/com/carrotsearch/hppc/LongDeque.java deleted file mode 100755 index a2a202b9..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongDeque.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.LongCursor; -import com.carrotsearch.hppc.predicates.LongPredicate; -import com.carrotsearch.hppc.procedures.LongProcedure; -import java.util.Deque; -import java.util.Iterator; - -/** - * A linear collection that supports element insertion and removal at both ends. - * - * @see Deque - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeDeque.java") -public interface LongDeque extends LongCollection { - /** - * Removes the first element that equals e. - * - * @return The deleted element's index or -1 if the element was not found. - */ - public int removeFirst(long e); - - /** - * Removes the last element that equals e. - * - * @return The deleted element's index or -1 if the element was not found. - */ - public int removeLast(long e); - - /** Inserts the specified element at the front of this deque. */ - public void addFirst(long e); - - /** Inserts the specified element at the end of this deque. */ - public void addLast(long e); - - /** - * Retrieves and removes the first element of this deque. - * - * @return the head (first) element of this deque. - */ - public long removeFirst(); - - /** - * Retrieves and removes the last element of this deque. - * - * @return the tail of this deque. - */ - public long removeLast(); - - /** - * Retrieves the first element of this deque but does not remove it. - * - * @return the head of this deque. - */ - public long getFirst(); - - /** - * Retrieves the last element of this deque but does not remove it. - * - * @return the head of this deque. - */ - public long getLast(); - - /** - * @return An iterator over elements in this deque in tail-to-head order. - */ - public Iterator descendingIterator(); - - /** Applies a procedure to all elements in tail-to-head order. */ - public T descendingForEach(T procedure); - - /** - * Applies a predicate to container elements as long, as the predicate returns - * true. The iteration is interrupted otherwise. - */ - public T descendingForEach(T predicate); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongDoubleAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/LongDoubleAssociativeContainer.java deleted file mode 100755 index f0ec0558..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongDoubleAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see LongContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface LongDoubleAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *
-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(long key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongDoublePredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link LongDoubleProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link LongDoublePredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public LongCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public DoubleContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongDoubleHashMap.java b/src/main/java/com/carrotsearch/hppc/LongDoubleHashMap.java deleted file mode 100755 index 2df0b5ab..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongDoubleHashMap.java +++ /dev/null @@ -1,1082 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of long to double, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeHashMap.java") -public class LongDoubleHashMap implements LongDoubleMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public long[] keys; - - /** The array holding values. */ - public double[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public LongDoubleHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public LongDoubleHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public LongDoubleHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public LongDoubleHashMap(LongDoubleAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public double put(long key, double value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - double previousValue = hasEmptyKey ? values[mask + 1] : 0d; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final double previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0d; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(LongDoubleAssociativeContainer container) { - final int count = size(); - for (LongDoubleCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (LongDoubleCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public double putOrAdd(long key, double putValue, double incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((double) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public double addTo(long key, double incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public double remove(long key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return 0d; - } - hasEmptyKey = false; - double previousValue = values[mask + 1]; - values[mask + 1] = 0d; - return previousValue; - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final double previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0d; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof LongLookupContainer) { - if (hasEmptyKey && other.contains(0L)) { - hasEmptyKey = false; - values[mask + 1] = 0d; - } - - final long[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - long existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (LongCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongDoublePredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(0L, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0d; - } - } - - final long[] keys = this.keys; - final double[] values = this.values; - for (int slot = 0; slot <= mask; ) { - long existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(0L)) { - hasEmptyKey = false; - values[mask + 1] = 0d; - } - } - - final long[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - long existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public double get(long key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : 0d; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0d; - } - } - - /** {@inheritDoc} */ - @Override - public double getOrDefault(long key, double defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(long key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(long key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public double indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public double indexReplace(int index, double newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - double previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, long key, double value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public double indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - double previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0d; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, 0L); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (LongDoubleCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(LongDoubleHashMap other) { - if (other.size() != size()) { - return false; - } - - for (LongDoubleCursor c : other) { - long key = c.key; - if (!containsKey(key) - || !(Double.doubleToLongBits(c.value) == Double.doubleToLongBits(get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final long[] prevKeys = this.keys; - final double[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final LongDoubleCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new LongDoubleCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongDoubleCursor fetch() { - final int mask = LongDoubleHashMap.this.mask; - while (index <= mask) { - long existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = 0L; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final long[] keys = this.keys; - final double[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(0L, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final long[] keys = this.keys; - final double[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(0L, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractLongCollection implements LongLookupContainer { - private final LongDoubleHashMap owner = LongDoubleHashMap.this; - - @Override - public boolean contains(long e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((LongDoubleProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((LongDoublePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(LongPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final long e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final LongCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new LongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongCursor fetch() { - final int mask = LongDoubleHashMap.this.mask; - while (index <= mask) { - long existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = 0L; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public DoubleCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractDoubleCollection { - private final LongDoubleHashMap owner = LongDoubleHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(double value) { - for (LongDoubleCursor c : owner) { - if ((Double.doubleToLongBits(value) == Double.doubleToLongBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (LongDoubleCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (LongDoubleCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final double e) { - return owner.removeAll( - (key, value) -> (Double.doubleToLongBits(e) == Double.doubleToLongBits(value))); - } - - @Override - public int removeAll(final DoublePredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final DoubleCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new DoubleCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected DoubleCursor fetch() { - final int mask = LongDoubleHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public LongDoubleHashMap clone() { - try { - - LongDoubleHashMap cloned = (LongDoubleHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (LongDoubleCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return LongBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static LongDoubleHashMap from(long[] keys, double[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - LongDoubleHashMap map = new LongDoubleHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(long key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(long[] fromKeys, double[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final long[] keys = this.keys; - final double[] values = this.values; - final int mask = this.mask; - long existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - long[] prevKeys = this.keys; - double[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new long[arraySize + emptyElementSlot]); - this.values = (new double[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, long pendingKey, double pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final long[] prevKeys = this.keys; - final double[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final long[] keys = this.keys; - final double[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final long existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = 0L; - values[gapSlot] = 0d; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/LongDoubleMap.java b/src/main/java/com/carrotsearch/hppc/LongDoubleMap.java deleted file mode 100755 index 7c83d309..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongDoubleMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.LongDoubleCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface LongDoubleMap extends LongDoubleAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public double get(long key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public double getOrDefault(long key, double defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public double put(long key, double value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(long key, double value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(LongDoubleAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public double putOrAdd(long key, double putValue, double incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public double addTo(long key, double additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public double remove(long key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link LongDoubleMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(long key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public double indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public double indexReplace(int index, double newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, long key, double value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public double indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongFloatAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/LongFloatAssociativeContainer.java deleted file mode 100755 index 80cc2da1..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongFloatAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see LongContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface LongFloatAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(long key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongFloatPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link LongFloatProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link LongFloatPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public LongCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public FloatContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongFloatHashMap.java b/src/main/java/com/carrotsearch/hppc/LongFloatHashMap.java deleted file mode 100755 index bc0d5f2d..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongFloatHashMap.java +++ /dev/null @@ -1,1081 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of long to float, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeHashMap.java") -public class LongFloatHashMap implements LongFloatMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public long[] keys; - - /** The array holding values. */ - public float[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public LongFloatHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public LongFloatHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public LongFloatHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public LongFloatHashMap(LongFloatAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public float put(long key, float value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - float previousValue = hasEmptyKey ? values[mask + 1] : 0f; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final float previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0f; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(LongFloatAssociativeContainer container) { - final int count = size(); - for (LongFloatCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (LongFloatCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public float putOrAdd(long key, float putValue, float incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((float) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public float addTo(long key, float incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public float remove(long key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return 0f; - } - hasEmptyKey = false; - float previousValue = values[mask + 1]; - values[mask + 1] = 0f; - return previousValue; - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final float previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0f; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof LongLookupContainer) { - if (hasEmptyKey && other.contains(0L)) { - hasEmptyKey = false; - values[mask + 1] = 0f; - } - - final long[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - long existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (LongCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongFloatPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(0L, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0f; - } - } - - final long[] keys = this.keys; - final float[] values = this.values; - for (int slot = 0; slot <= mask; ) { - long existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(0L)) { - hasEmptyKey = false; - values[mask + 1] = 0f; - } - } - - final long[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - long existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public float get(long key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : 0f; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0f; - } - } - - /** {@inheritDoc} */ - @Override - public float getOrDefault(long key, float defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(long key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(long key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public float indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public float indexReplace(int index, float newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - float previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, long key, float value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public float indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - float previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0f; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, 0L); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (LongFloatCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(LongFloatHashMap other) { - if (other.size() != size()) { - return false; - } - - for (LongFloatCursor c : other) { - long key = c.key; - if (!containsKey(key) || !(Float.floatToIntBits(c.value) == Float.floatToIntBits(get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final long[] prevKeys = this.keys; - final float[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final LongFloatCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new LongFloatCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongFloatCursor fetch() { - final int mask = LongFloatHashMap.this.mask; - while (index <= mask) { - long existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = 0L; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final long[] keys = this.keys; - final float[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(0L, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final long[] keys = this.keys; - final float[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(0L, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractLongCollection implements LongLookupContainer { - private final LongFloatHashMap owner = LongFloatHashMap.this; - - @Override - public boolean contains(long e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((LongFloatProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((LongFloatPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(LongPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final long e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final LongCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new LongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongCursor fetch() { - final int mask = LongFloatHashMap.this.mask; - while (index <= mask) { - long existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = 0L; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public FloatCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractFloatCollection { - private final LongFloatHashMap owner = LongFloatHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(float value) { - for (LongFloatCursor c : owner) { - if ((Float.floatToIntBits(value) == Float.floatToIntBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (LongFloatCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (LongFloatCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final float e) { - return owner.removeAll( - (key, value) -> (Float.floatToIntBits(e) == Float.floatToIntBits(value))); - } - - @Override - public int removeAll(final FloatPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final FloatCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new FloatCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected FloatCursor fetch() { - final int mask = LongFloatHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public LongFloatHashMap clone() { - try { - - LongFloatHashMap cloned = (LongFloatHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (LongFloatCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return LongBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static LongFloatHashMap from(long[] keys, float[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - LongFloatHashMap map = new LongFloatHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(long key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(long[] fromKeys, float[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final long[] keys = this.keys; - final float[] values = this.values; - final int mask = this.mask; - long existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - long[] prevKeys = this.keys; - float[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new long[arraySize + emptyElementSlot]); - this.values = (new float[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, long pendingKey, float pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final long[] prevKeys = this.keys; - final float[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final long[] keys = this.keys; - final float[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final long existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = 0L; - values[gapSlot] = 0f; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/LongFloatMap.java b/src/main/java/com/carrotsearch/hppc/LongFloatMap.java deleted file mode 100755 index dc4cf199..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongFloatMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.LongFloatCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface LongFloatMap extends LongFloatAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public float get(long key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public float getOrDefault(long key, float defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public float put(long key, float value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(long key, float value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(LongFloatAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public float putOrAdd(long key, float putValue, float incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public float addTo(long key, float additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public float remove(long key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link LongFloatMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(long key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public float indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public float indexReplace(int index, float newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, long key, float value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public float indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongHashSet.java b/src/main/java/com/carrotsearch/hppc/LongHashSet.java deleted file mode 100755 index 8957939d..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongHashSet.java +++ /dev/null @@ -1,787 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash set of longs, implemented using open addressing with linear probing for - * collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypeHashSet.java") -public class LongHashSet extends AbstractLongCollection - implements LongLookupContainer, LongSet, Preallocable, Cloneable, Accountable { - /** The hash array holding keys. */ - public long[] keys; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any. - * - * @see #size() - * @see #hasEmptyKey - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** - * New instance with sane defaults. - * - * @see #LongHashSet(int, double) - */ - public LongHashSet() { - this(DEFAULT_EXPECTED_ELEMENTS, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with sane defaults. - * - * @see #LongHashSet(int, double) - */ - public LongHashSet(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public LongHashSet(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** New instance copying elements from another {@link LongContainer}. */ - public LongHashSet(LongContainer container) { - this(container.size()); - addAll(container); - } - - /** {@inheritDoc} */ - @Override - public boolean add(long key) { - if (((key) == 0)) { - assert ((keys[mask + 1]) == 0); - boolean added = !hasEmptyKey; - hasEmptyKey = true; - return added; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return false; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key); - } else { - keys[slot] = key; - } - - assigned++; - return true; - } - } - - /** - * Adds all elements from the given list (vararg) to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - */ - public final int addAll(long... elements) { - ensureCapacity(elements.length); - int count = 0; - for (long e : elements) { - if (add(e)) { - count++; - } - } - return count; - } - - /** - * Adds all elements from the given {@link LongContainer} to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - */ - public int addAll(LongContainer container) { - ensureCapacity(container.size()); - return addAll((Iterable) container); - } - - /** - * Adds all elements from the given iterable to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - */ - public int addAll(Iterable iterable) { - int count = 0; - for (LongCursor cursor : iterable) { - if (add(cursor.value)) { - count++; - } - } - return count; - } - - /** {@inheritDoc} */ - @Override - public long[] toArray() { - - final long[] cloned = (new long[size()]); - int j = 0; - if (hasEmptyKey) { - cloned[j++] = 0L; - } - - final long[] keys = this.keys; - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - long existing; - if (!((existing = keys[slot]) == 0)) { - cloned[j++] = existing; - } - } - - return cloned; - } - - /** An alias for the (preferred) {@link #removeAll}. */ - public boolean remove(long key) { - if (((key) == 0)) { - boolean hadEmptyKey = hasEmptyKey; - hasEmptyKey = false; - return hadEmptyKey; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - shiftConflictingKeys(slot); - return true; - } - slot = (slot + 1) & mask; - } - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(long key) { - return remove(key) ? 1 : 0; - } - - /** - * Removes all keys present in a given container. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongContainer other) { - final int before = size(); - - // Try to iterate over the smaller set or over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof LongLookupContainer) { - if (hasEmptyKey && other.contains(0L)) { - hasEmptyKey = false; - } - - final long[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - long existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (LongCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongPredicate predicate) { - int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(0L)) { - hasEmptyKey = false; - } - } - - final long[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - long existing; - if (!((existing = keys[slot]) == 0)) { - if (predicate.apply(existing)) { - shiftConflictingKeys(slot); - continue; // Repeat the check for the same slot i (shifted). - } - } - slot++; - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public boolean contains(long key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - return false; - } - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - Arrays.fill(keys, 0L); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - keys = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return size() == 0; - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final long[] prevKeys = this.keys; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys); - } - } - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - final long[] keys = this.keys; - for (int slot = mask; slot >= 0; slot--) { - long existing; - if (!((existing = keys[slot]) == 0)) { - h += BitMixer.mix(existing); - } - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && sameKeys(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - private boolean sameKeys(LongSet other) { - if (other.size() != size()) { - return false; - } - - for (LongCursor c : other) { - if (!contains(c.value)) { - return false; - } - } - - return true; - } - - /** {@inheritDoc} */ - @Override - public LongHashSet clone() { - try { - - LongHashSet cloned = (LongHashSet) super.clone(); - cloned.keys = keys.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - @Override - public long ramBytesAllocated() { - // int: assigned, mask, keyMixer, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys); - } - - @Override - public long ramBytesUsed() { - // int: assigned, mask, keyMixer, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - protected final class EntryIterator extends AbstractIterator { - private final LongCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new LongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongCursor fetch() { - final int mask = LongHashSet.this.mask; - while (index <= mask) { - long existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = 0L; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - if (hasEmptyKey) { - procedure.apply(0L); - } - - final long[] keys = this.keys; - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - long existing; - if (!((existing = keys[slot]) == 0)) { - procedure.apply(existing); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - if (hasEmptyKey) { - if (!predicate.apply(0L)) { - return predicate; - } - } - - final long[] keys = this.keys; - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - long existing; - if (!((existing = keys[slot]) == 0)) { - if (!predicate.apply(existing)) { - break; - } - } - } - - return predicate; - } - - /** - * Create a set from a variable number of arguments or an array of long. The elements - * are copied from the argument to the internal buffer. - */ - public static LongHashSet from(long... elements) { - final LongHashSet set = new LongHashSet(elements.length); - set.addAll(elements); - return set; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(long key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up logic in - * certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between modifications (it will not be affected by read-only - * operations). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the set. - * @return A non-negative value of the logical "index" of the key in the set or a negative value - * if the key did not exist. - */ - public int indexOf(long key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index) { - assert index < 0 || index <= mask || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** - * Returns the exact value of the existing key. This method makes sense for sets of objects which - * define custom key-equality relationship. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the equivalent key currently stored in the set. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public long indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return keys[index]; - } - - /** - * Replaces the existing equivalent key with the given one and returns any previous value stored - * for that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @param equivalentKey The key to put in the set as a replacement. Must be equivalent to the key - * currently stored at the provided index. - * @return Returns the previous key stored in the set. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public long indexReplace(int index, long equivalentKey) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - assert ((keys[index]) == (equivalentKey)); - - long previousValue = keys[index]; - keys[index] = equivalentKey; - return previousValue; - } - - /** - * Inserts a key for an index that is not present in the set. This method may help in avoiding - * double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public void indexInsert(int index, long key) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - assert ((keys[index]) == 0); - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key); - } else { - keys[index] = key; - } - - assigned++; - } - } - - /** - * Removes a key at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public void indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - if (index > mask) { - hasEmptyKey = false; - } else { - shiftConflictingKeys(index); - } - } - - @Override - public String visualizeKeyDistribution(int characters) { - return LongBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(long[] fromKeys) { - assert HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored keys into the new buffers. - final long[] keys = this.keys; - final int mask = this.mask; - long existing; - for (int i = fromKeys.length - 1; --i >= 0; ) { - if (!((existing = fromKeys[i]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - long[] prevKeys = this.keys; - try { - int emptyElementSlot = 1; - this.keys = (new long[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.keys == null ? 0 : size(), arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key to be inserted into the buffer but there is not - * enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, long pendingKey) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final long[] prevKeys = this.keys; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - - // Rehash old keys, including the pending key. - rehash(prevKeys); - } - - /** Shift all the slot-conflicting keys allocated to (and including) slot. */ - protected void shiftConflictingKeys(int gapSlot) { - final long[] keys = this.keys; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final long existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = 0L; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/LongIndexedContainer.java b/src/main/java/com/carrotsearch/hppc/LongIndexedContainer.java deleted file mode 100755 index ba0c78a2..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongIndexedContainer.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.carrotsearch.hppc; - -import java.util.RandomAccess; -import java.util.stream.LongStream; - -/** - * An indexed container provides random access to elements based on an index. Indexes - * are zero-based. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeIndexedContainer.java") -public interface LongIndexedContainer extends LongCollection, RandomAccess { - /** - * Removes the first element that equals e1, returning whether an element has been - * removed. - */ - public boolean removeElement(long e1); - - /** - * Removes the first element that equals e1, returning its deleted position or - * -1 if the element was not found. - */ - public int removeFirst(long e1); - - /** - * Removes the last element that equals e1, returning its deleted position or - * -1 if the element was not found. - */ - public int removeLast(long e1); - - /** - * Returns the index of the first occurrence of the specified element in this list, or -1 if this - * list does not contain the element. - */ - public int indexOf(long e1); - - /** - * Returns the index of the last occurrence of the specified element in this list, or -1 if this - * list does not contain the element. - */ - public int lastIndexOf(long e1); - - /** Adds an element to the end of this container (the last index is incremented by one). */ - public void add(long e1); - - /** - * Inserts the specified element at the specified position in this list. - * - * @param index The index at which the element should be inserted, shifting any existing and - * subsequent elements to the right. - */ - public void insert(int index, long e1); - - /** - * Replaces the element at the specified position in this list with the specified element. - * - * @return Returns the previous value in the list. - */ - public long set(int index, long e1); - - /** - * @return Returns the element at index index from the list. - */ - public long get(int index); - - /** - * Removes the element at the specified position in this container and returns it. - * - * @see #removeFirst - * @see #removeLast - * @see #removeAll - */ - public long removeAt(int index); - - /** Removes and returns the last element of this container. This container must not be empty. */ - public long removeLast(); - - /** - * Removes from this container all of the elements with indexes between fromIndex, - * inclusive, and toIndex, exclusive. - */ - public void removeRange(int fromIndex, int toIndex); - - /** Returns this container elements as a stream. */ - public LongStream stream(); - - /** Sorts the elements in this container and returns this container. */ - public LongIndexedContainer sort(); - - /** Reverses the elements in this container and returns this container. */ - public LongIndexedContainer reverse(); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongIntAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/LongIntAssociativeContainer.java deleted file mode 100755 index 97467873..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongIntAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see LongContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface LongIntAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(long key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongIntPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link LongIntProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link LongIntPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public LongCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public IntContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongIntHashMap.java b/src/main/java/com/carrotsearch/hppc/LongIntHashMap.java deleted file mode 100755 index de4d34b4..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongIntHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of long to int, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeHashMap.java") -public class LongIntHashMap implements LongIntMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public long[] keys; - - /** The array holding values. */ - public int[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public LongIntHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public LongIntHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public LongIntHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public LongIntHashMap(LongIntAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public int put(long key, int value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - int previousValue = hasEmptyKey ? values[mask + 1] : 0; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final int previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(LongIntAssociativeContainer container) { - final int count = size(); - for (LongIntCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (LongIntCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public int putOrAdd(long key, int putValue, int incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((int) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public int addTo(long key, int incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public int remove(long key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return 0; - } - hasEmptyKey = false; - int previousValue = values[mask + 1]; - values[mask + 1] = 0; - return previousValue; - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final int previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof LongLookupContainer) { - if (hasEmptyKey && other.contains(0L)) { - hasEmptyKey = false; - values[mask + 1] = 0; - } - - final long[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - long existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (LongCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongIntPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(0L, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0; - } - } - - final long[] keys = this.keys; - final int[] values = this.values; - for (int slot = 0; slot <= mask; ) { - long existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(0L)) { - hasEmptyKey = false; - values[mask + 1] = 0; - } - } - - final long[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - long existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int get(long key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : 0; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0; - } - } - - /** {@inheritDoc} */ - @Override - public int getOrDefault(long key, int defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(long key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(long key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public int indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public int indexReplace(int index, int newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - int previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, long key, int value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public int indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - int previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, 0L); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (LongIntCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(LongIntHashMap other) { - if (other.size() != size()) { - return false; - } - - for (LongIntCursor c : other) { - long key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final long[] prevKeys = this.keys; - final int[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final LongIntCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new LongIntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongIntCursor fetch() { - final int mask = LongIntHashMap.this.mask; - while (index <= mask) { - long existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = 0L; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final long[] keys = this.keys; - final int[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(0L, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final long[] keys = this.keys; - final int[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(0L, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractLongCollection implements LongLookupContainer { - private final LongIntHashMap owner = LongIntHashMap.this; - - @Override - public boolean contains(long e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((LongIntProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((LongIntPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(LongPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final long e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final LongCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new LongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongCursor fetch() { - final int mask = LongIntHashMap.this.mask; - while (index <= mask) { - long existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = 0L; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public IntCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractIntCollection { - private final LongIntHashMap owner = LongIntHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(int value) { - for (LongIntCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (LongIntCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (LongIntCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final int e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final IntPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final IntCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new IntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntCursor fetch() { - final int mask = LongIntHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public LongIntHashMap clone() { - try { - - LongIntHashMap cloned = (LongIntHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (LongIntCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return LongBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static LongIntHashMap from(long[] keys, int[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - LongIntHashMap map = new LongIntHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(long key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(long[] fromKeys, int[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final long[] keys = this.keys; - final int[] values = this.values; - final int mask = this.mask; - long existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - long[] prevKeys = this.keys; - int[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new long[arraySize + emptyElementSlot]); - this.values = (new int[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, long pendingKey, int pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final long[] prevKeys = this.keys; - final int[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final long[] keys = this.keys; - final int[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final long existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = 0L; - values[gapSlot] = 0; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/LongIntMap.java b/src/main/java/com/carrotsearch/hppc/LongIntMap.java deleted file mode 100755 index 6fb8c866..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongIntMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.LongIntCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface LongIntMap extends LongIntAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public int get(long key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public int getOrDefault(long key, int defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public int put(long key, int value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(long key, int value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(LongIntAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public int putOrAdd(long key, int putValue, int incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public int addTo(long key, int additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public int remove(long key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link LongIntMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(long key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public int indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public int indexReplace(int index, int newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, long key, int value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public int indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongLongAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/LongLongAssociativeContainer.java deleted file mode 100755 index 24be1a2e..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongLongAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see LongContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface LongLongAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(long key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongLongPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link LongLongProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link LongLongPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public LongCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public LongContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongLongHashMap.java b/src/main/java/com/carrotsearch/hppc/LongLongHashMap.java deleted file mode 100755 index c1937803..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongLongHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of long to long, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeHashMap.java") -public class LongLongHashMap implements LongLongMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public long[] keys; - - /** The array holding values. */ - public long[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public LongLongHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public LongLongHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public LongLongHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public LongLongHashMap(LongLongAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public long put(long key, long value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - long previousValue = hasEmptyKey ? values[mask + 1] : 0L; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final long previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0L; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(LongLongAssociativeContainer container) { - final int count = size(); - for (LongLongCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (LongLongCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public long putOrAdd(long key, long putValue, long incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((long) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public long addTo(long key, long incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public long remove(long key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return 0L; - } - hasEmptyKey = false; - long previousValue = values[mask + 1]; - values[mask + 1] = 0L; - return previousValue; - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final long previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0L; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof LongLookupContainer) { - if (hasEmptyKey && other.contains(0L)) { - hasEmptyKey = false; - values[mask + 1] = 0L; - } - - final long[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - long existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (LongCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongLongPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(0L, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0L; - } - } - - final long[] keys = this.keys; - final long[] values = this.values; - for (int slot = 0; slot <= mask; ) { - long existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(0L)) { - hasEmptyKey = false; - values[mask + 1] = 0L; - } - } - - final long[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - long existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public long get(long key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : 0L; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0L; - } - } - - /** {@inheritDoc} */ - @Override - public long getOrDefault(long key, long defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(long key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(long key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public long indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public long indexReplace(int index, long newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - long previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, long key, long value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public long indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - long previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0L; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, 0L); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (LongLongCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(LongLongHashMap other) { - if (other.size() != size()) { - return false; - } - - for (LongLongCursor c : other) { - long key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final long[] prevKeys = this.keys; - final long[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final LongLongCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new LongLongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongLongCursor fetch() { - final int mask = LongLongHashMap.this.mask; - while (index <= mask) { - long existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = 0L; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final long[] keys = this.keys; - final long[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(0L, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final long[] keys = this.keys; - final long[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(0L, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractLongCollection implements LongLookupContainer { - private final LongLongHashMap owner = LongLongHashMap.this; - - @Override - public boolean contains(long e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((LongLongProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((LongLongPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(LongPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final long e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final LongCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new LongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongCursor fetch() { - final int mask = LongLongHashMap.this.mask; - while (index <= mask) { - long existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = 0L; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public LongCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractLongCollection { - private final LongLongHashMap owner = LongLongHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(long value) { - for (LongLongCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (LongLongCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (LongLongCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final long e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final LongPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final LongCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new LongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongCursor fetch() { - final int mask = LongLongHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public LongLongHashMap clone() { - try { - - LongLongHashMap cloned = (LongLongHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (LongLongCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return LongBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static LongLongHashMap from(long[] keys, long[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - LongLongHashMap map = new LongLongHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(long key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(long[] fromKeys, long[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final long[] keys = this.keys; - final long[] values = this.values; - final int mask = this.mask; - long existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - long[] prevKeys = this.keys; - long[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new long[arraySize + emptyElementSlot]); - this.values = (new long[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, long pendingKey, long pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final long[] prevKeys = this.keys; - final long[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final long[] keys = this.keys; - final long[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final long existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = 0L; - values[gapSlot] = 0L; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/LongLongMap.java b/src/main/java/com/carrotsearch/hppc/LongLongMap.java deleted file mode 100755 index 3ea093c9..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongLongMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.LongLongCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface LongLongMap extends LongLongAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public long get(long key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public long getOrDefault(long key, long defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public long put(long key, long value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(long key, long value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(LongLongAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public long putOrAdd(long key, long putValue, long incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public long addTo(long key, long additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public long remove(long key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link LongLongMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(long key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public long indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public long indexReplace(int index, long newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, long key, long value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public long indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongLookupContainer.java b/src/main/java/com/carrotsearch/hppc/LongLookupContainer.java deleted file mode 100755 index 70a99942..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongLookupContainer.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.carrotsearch.hppc; - -/** - * Marker interface for containers that can check if they contain a given object in at least time - * O(log n) and ideally in amortized constant time O(1). - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeLookupContainer.java") -public interface LongLookupContainer extends LongContainer { - public boolean contains(long e); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongObjectAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/LongObjectAssociativeContainer.java deleted file mode 100755 index 9e805ffc..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongObjectAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see LongContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface LongObjectAssociativeContainer extends Iterable> { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator> iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(long key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongObjectPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link LongObjectProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public > T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link LongObjectPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public > T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public LongCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public ObjectContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongObjectHashMap.java b/src/main/java/com/carrotsearch/hppc/LongObjectHashMap.java deleted file mode 100755 index e8647afd..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongObjectHashMap.java +++ /dev/null @@ -1,1050 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of long to Object, implemented using open addressing with - * linear probing for collision resolution. Supports null values. - * - * @see HPPC interfaces diagram - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeHashMap.java") -public class LongObjectHashMap - implements LongObjectMap, Preallocable, Cloneable, Accountable { - /** The array holding keys. */ - public long[] keys; - - /** The array holding values. */ - public Object[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public LongObjectHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public LongObjectHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public LongObjectHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public LongObjectHashMap(LongObjectAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public VType put(long key, VType value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - VType previousValue = hasEmptyKey ? (VType) values[mask + 1] : null; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final VType previousValue = (VType) values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return null; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(LongObjectAssociativeContainer container) { - final int count = size(); - for (LongObjectCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable> iterable) { - final int count = size(); - for (LongObjectCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** {@inheritDoc} */ - @Override - public VType remove(long key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return null; - } - hasEmptyKey = false; - VType previousValue = (VType) values[mask + 1]; - values[mask + 1] = null; - return previousValue; - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final VType previousValue = (VType) values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return null; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof LongLookupContainer) { - if (hasEmptyKey && other.contains(0L)) { - hasEmptyKey = false; - values[mask + 1] = null; - } - - final long[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - long existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (LongCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongObjectPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(0L, (VType) values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = null; - } - } - - final long[] keys = this.keys; - final VType[] values = (VType[]) this.values; - for (int slot = 0; slot <= mask; ) { - long existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(0L)) { - hasEmptyKey = false; - values[mask + 1] = null; - } - } - - final long[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - long existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public VType get(long key) { - if (((key) == 0)) { - return hasEmptyKey ? (VType) values[mask + 1] : null; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return (VType) values[slot]; - } - slot = (slot + 1) & mask; - } - - return null; - } - } - - /** {@inheritDoc} */ - @Override - public VType getOrDefault(long key, VType defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? (VType) values[mask + 1] : defaultValue; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return (VType) values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(long key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(long key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public VType indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return (VType) values[index]; - } - - /** {@inheritDoc} */ - @Override - public VType indexReplace(int index, VType newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - VType previousValue = (VType) values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, long key, VType value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public VType indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - VType previousValue = (VType) values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = null; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, 0L); - - Arrays.fill(values, null); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (LongObjectCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** - * Return true if all keys of some other container exist in this container. Values are compared - * using {@link Objects#equals(Object)} method. - */ - protected boolean equalElements(LongObjectHashMap other) { - if (other.size() != size()) { - return false; - } - - for (LongObjectCursor c : other) { - long key = c.key; - if (!containsKey(key) || !java.util.Objects.equals(c.value, get(key))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final long[] prevKeys = this.keys; - final VType[] prevValues = (VType[]) this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final LongObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new LongObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongObjectCursor fetch() { - final int mask = LongObjectHashMap.this.mask; - while (index <= mask) { - long existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = (VType) values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = 0L; - cursor.value = (VType) values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator> iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T procedure) { - final long[] keys = this.keys; - final VType[] values = (VType[]) this.values; - - if (hasEmptyKey) { - procedure.apply(0L, (VType) values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T predicate) { - final long[] keys = this.keys; - final VType[] values = (VType[]) this.values; - - if (hasEmptyKey) { - if (!predicate.apply(0L, (VType) values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractLongCollection implements LongLookupContainer { - private final LongObjectHashMap owner = LongObjectHashMap.this; - - @Override - public boolean contains(long e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((LongObjectProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((LongObjectPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(LongPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final long e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final LongCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new LongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongCursor fetch() { - final int mask = LongObjectHashMap.this.mask; - while (index <= mask) { - long existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = 0L; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public ObjectCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractObjectCollection { - private final LongObjectHashMap owner = LongObjectHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(VType value) { - for (LongObjectCursor c : owner) { - if (java.util.Objects.equals(value, c.value)) { - return true; - } - } - return false; - } - - @Override - public > T forEach(T procedure) { - for (LongObjectCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public > T forEach(T predicate) { - for (LongObjectCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator> iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final VType e) { - return owner.removeAll((key, value) -> java.util.Objects.equals(e, value)); - } - - @Override - public int removeAll(final ObjectPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator> { - private final ObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new ObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectCursor fetch() { - final int mask = LongObjectHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = (VType) values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = (VType) values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public LongObjectHashMap clone() { - try { - - LongObjectHashMap cloned = (LongObjectHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (LongObjectCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return LongBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static LongObjectHashMap from(long[] keys, VType[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - LongObjectHashMap map = new LongObjectHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(long key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(long[] fromKeys, VType[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final long[] keys = this.keys; - final VType[] values = (VType[]) this.values; - final int mask = this.mask; - long existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - long[] prevKeys = this.keys; - VType[] prevValues = (VType[]) this.values; - try { - int emptyElementSlot = 1; - this.keys = (new long[arraySize + emptyElementSlot]); - this.values = ((VType[]) new Object[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, long pendingKey, VType pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final long[] prevKeys = this.keys; - final VType[] prevValues = (VType[]) this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final long[] keys = this.keys; - final VType[] values = (VType[]) this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final long existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = 0L; - values[gapSlot] = null; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/LongObjectMap.java b/src/main/java/com/carrotsearch/hppc/LongObjectMap.java deleted file mode 100755 index 712d01a5..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongObjectMap.java +++ /dev/null @@ -1,181 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.LongObjectCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface LongObjectMap extends LongObjectAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public VType get(long key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public VType getOrDefault(long key, VType defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public VType put(long key, VType value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(long key, VType value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(LongObjectAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable> iterable); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public VType remove(long key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link LongObjectMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(long key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public VType indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public VType indexReplace(int index, VType newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, long key, VType value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public VType indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongPgmIndex.java b/src/main/java/com/carrotsearch/hppc/LongPgmIndex.java deleted file mode 100755 index dc80b159..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongPgmIndex.java +++ /dev/null @@ -1,600 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.LongCursor; -import com.carrotsearch.hppc.procedures.LongProcedure; -import java.util.Arrays; -import java.util.Iterator; - -/** - * Space-efficient index that enables fast rank/range search operations on a sorted sequence of - * long. - * - *

Implementation of the PGM-Index described at https://pgm.di.unipi.it/, based on the paper - * - *

- *   Paolo Ferragina and Giorgio Vinciguerra.
- *   The PGM-index: a fully-dynamic compressed learned index with provable worst-case bounds.
- *   PVLDB, 13(8): 1162-1175, 2020.
- * 
- * - * It provides {@code rank} and {@code range} search operations. {@code indexOf()} is faster than - * B+Tree, and the index is much more compact. {@code contains()} is between 4x to 7x slower than - * {@code IntHashSet#contains()}, but between 2.5x to 3x faster than {@link Arrays#binarySearch}. - * - *

Its compactness (40KB for 200MB of keys) makes it efficient for very large collections, the - * index fitting easily in the L2 cache. The {@code epsilon} parameter should be set according to - * the desired space-time trade-off. A smaller value makes the estimation more precise and the range - * smaller but at the cost of increased space usage. In practice, {@code epsilon} 64 is a good sweet - * spot. - * - *

Internally the index uses an optimal piecewise linear mapping from keys to their position in - * the sorted order. This mapping is represented as a sequence of linear models (segments) which are - * themselves recursively indexed by other piecewise linear mappings. - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypePgmIndex.java") -public class LongPgmIndex implements Accountable { - - /** Empty immutable LongPgmIndex. */ - public static final LongPgmIndex EMPTY = new LongEmptyPgmIndex(); - - /** - * Epsilon approximation range when searching the list of keys. Controls the size of the returned - * search range, strictly greater than 0. It should be set according to the desired space-time - * trade-off. A smaller value makes the estimation more precise and the range smaller but at the - * cost of increased space usage. - * - *

With EPSILON=64 the benchmark with 200MB of keys shows that this PGM index requires only 2% - * additional memory on average (40KB). It depends on the distribution of the keys. This epsilon - * value is good even for 2MB of keys. With EPSILON=32: +5% speed, but 4x space (160KB). - */ - public static final int EPSILON = 64; - - /** - * Epsilon approximation range for the segments layers. Controls the size of the search range in - * the hierarchical segment lists, strictly greater than 0. - */ - public static final int EPSILON_RECURSIVE = 32; - - /** Size of a key, measured in {@link Integer#BYTES} because the key is stored in an int[]. */ - public static final int KEY_SIZE = - RamUsageEstimator.primitiveSizes.get(long.class) / Integer.BYTES; - - /** 2x {@link #KEY_SIZE}. */ - public static final int DOUBLE_KEY_SIZE = KEY_SIZE * 2; - - /** - * Data size of a segment, measured in {@link Integer#BYTES}, because segments are stored in an - * int[]. - */ - public static final int SEGMENT_DATA_SIZE = KEY_SIZE * 3; - - /** Initial value of the exponential jump when scanning out of the epsilon range. */ - public static final int BEYOND_EPSILON_JUMP = 16; - - /** - * The list of keys for which this index is built. It is sorted and may contain duplicate - * elements. - */ - public final LongArrayList keys; - - /** The size of the key set. That is, the number of distinct elements in {@link #keys}. */ - public final int size; - - /** The lowest key in {@link #keys}. */ - public final long firstKey; - - /** The highest key in {@link #keys}. */ - public final long lastKey; - - /** The epsilon range used to build this index. */ - public final int epsilon; - - /** The recursive epsilon range used to build this index. */ - public final int epsilonRecursive; - - /** The offsets in {@link #segmentData} of the first segment of each segment level. */ - public final int[] levelOffsets; - - /** The index data. It contains all the segments for all the levels. */ - public final int[] segmentData; - - private LongPgmIndex( - LongArrayList keys, - int size, - int epsilon, - int epsilonRecursive, - int[] levelOffsets, - int[] segmentData) { - assert keys.size() > 0; - assert size > 0 && size <= keys.size(); - assert epsilon > 0; - assert epsilonRecursive > 0; - this.keys = keys; - this.size = size; - firstKey = keys.get(0); - lastKey = keys.get(keys.size() - 1); - this.epsilon = epsilon; - this.epsilonRecursive = epsilonRecursive; - this.levelOffsets = levelOffsets; - this.segmentData = segmentData; - } - - /** Empty set constructor. */ - private LongPgmIndex() { - keys = new LongArrayList(0); - size = 0; - firstKey = 0L; - lastKey = 0L; - epsilon = 0; - epsilonRecursive = 0; - levelOffsets = new int[0]; - segmentData = levelOffsets; - } - - /** Returns the size of the key set. That is, the number of distinct elements in {@link #keys}. */ - public int size() { - return size; - } - - /** Returns whether this key set is empty. */ - public boolean isEmpty() { - return size() == 0; - } - - /** Returns whether this key set contains the given key. */ - public boolean contains(long key) { - return indexOf(key) >= 0; - } - - /** - * Searches the specified key, and returns its index in the element list. If multiple elements are - * equal to the specified key, there is no guarantee which one will be found. - * - * @return The index of the searched key if it is present; otherwise, {@code (-(insertion - * point) - 1)}. The insertion point is defined as the point at which the key would - * be inserted into the list: the index of the first element greater than the key, or {@link - * #keys}#{@code size()} if all the elements are less than the specified key. Note that this - * guarantees that the return value will be >= 0 if and only if the key is found. - */ - public int indexOf(long key) { - if (key < firstKey) { - return -1; - } - if (key > lastKey) { - return -keys.size() - 1; - } - final int[] segmentData = this.segmentData; - int segmentDataIndex = findSegment(key); - int nextIntercept = (int) getIntercept(segmentDataIndex + SEGMENT_DATA_SIZE, segmentData); - int index = - Math.min( - approximateIndex(key, segmentDataIndex, segmentData), - Math.min(nextIntercept, keys.size() - 1)); - assert index >= 0 && index < keys.size(); - long k = keys.get(index); - if (key < k) { - // Scan sequentially before the approximated index, within epsilon range. - final int fromIndex = Math.max(index - epsilon - 1, 0); - while (--index >= fromIndex) { - k = keys.get(index); - if (key > k) { - return -index - 2; - } - if (((key) == (k))) { - return index; - } - } - // Continue scanning out of the epsilon range. - // This might happen in rare cases of precision error during the approximation - // computation for longs (we don't have long double 128 bits in Java). - // This might also happen in rare corner cases of large duplicate elements - // sequence at the epsilon range boundary. - index++; - int jump = BEYOND_EPSILON_JUMP; - do { - int loIndex = Math.max(index - jump, 0); - if (key >= keys.get(loIndex)) { - return Arrays.binarySearch(keys.buffer, loIndex, index, key); - } - index = loIndex; - jump <<= 1; - } while (index > 0); - return -1; - } else if (((key) == (k))) { - return index; - } else { - // Scan sequentially after the approximated index, within epsilon range. - final int toIndex = Math.min(index + epsilon + 3, keys.size()); - while (++index < toIndex) { - k = keys.get(index); - if (key < k) { - return -index - 1; - } - if (((key) == (k))) { - return index; - } - } - // Continue scanning out of the epsilon range. - int jump = BEYOND_EPSILON_JUMP; - do { - int hiIndex = Math.min(index + jump, keys.size()); - if (key <= keys.get(hiIndex)) { - return Arrays.binarySearch(keys.buffer, index, hiIndex, key); - } - index = hiIndex; - jump <<= 1; - } while (index < keys.size()); - return -keys.size() - 1; - } - } - - /** - * Returns, for any value {@code x}, the number of keys in the sorted list which are smaller than - * {@code x}. It is equal to {@link #indexOf} if {@code x} belongs to the list, or -{@link - * #indexOf}-1 otherwise. - * - *

If multiple elements are equal to the specified key, there is no guarantee which one will be - * found. - * - * @return The index of the searched key if it is present; otherwise, the {@code insertion point}. - * The insertion point is defined as the point at which the key would be inserted into - * the list: the index of the first element greater than the key, or {@link #keys}#{@code - * size()} if all the elements are less than the specified key. Note that this method always - * returns a value >= 0. - */ - public int rank(long x) { - int index = indexOf(x); - return index >= 0 ? index : -index - 1; - } - - /** - * Returns the number of keys in the list that are greater than or equal to {@code minKey} - * (inclusive), and less than or equal to {@code maxKey} (inclusive). - */ - public int rangeCardinality(long minKey, long maxKey) { - int fromIndex = rank(minKey); - int maxIndex = indexOf(maxKey); - int toIndex = maxIndex >= 0 ? maxIndex + 1 : -maxIndex - 1; - return Math.max(toIndex - fromIndex, 0); - } - - /** - * Returns an iterator over the keys in the list that are greater than or equal to {@code minKey} - * (inclusive), and less than or equal to {@code maxKey} (inclusive). - */ - public Iterator rangeIterator(long minKey, long maxKey) { - int fromIndex = rank(minKey); - return new RangeIterator(keys, fromIndex, maxKey); - } - - /** - * Applies {@code procedure} to the keys in the list that are greater than or equal to {@code - * minKey} (inclusive), and less than or equal to {@code maxKey} (inclusive). - */ - public T forEachInRange(T procedure, long minKey, long maxKey) { - final long[] buffer = keys.buffer; - long k; - for (int i = rank(minKey), size = keys.size(); i < size && (k = buffer[i]) <= maxKey; i++) { - procedure.apply(k); - } - return procedure; - } - - /** - * Estimates the allocated memory. It does not count the memory for the list of keys, only for the - * index itself. - */ - @Override - public long ramBytesAllocated() { - // int: size, epsilon, epsilonRecursive - // long: firstKey, lastKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 3 * Integer.BYTES - + 2L * KEY_SIZE * Integer.BYTES - // + keys.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(levelOffsets) - + RamUsageEstimator.shallowSizeOfArray(segmentData); - } - - /** - * Estimates the bytes that are actually used. It does not count the memory for the list of keys, - * only for the index itself. - */ - @Override - public long ramBytesUsed() { - // int: size, epsilon, epsilonRecursive - // long: firstKey, lastKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 3 * Integer.BYTES - + 2L * KEY_SIZE * Integer.BYTES - // + keys.ramBytesUsed() - + RamUsageEstimator.shallowSizeOfArray(levelOffsets) - + RamUsageEstimator.shallowSizeOfArray(segmentData); - } - - /** - * Finds the segment responsible for a given key, that is, the rightmost segment having its first - * key <= the searched key. - * - * @return the segment data index; or -1 if none. - */ - private int findSegment(long key) { - assert key >= firstKey && key <= lastKey; - final int epsilonRecursive = this.epsilonRecursive; - final int[] levelOffsets = this.levelOffsets; - final int[] segmentData = this.segmentData; - int level = levelOffsets.length - 1; - int segmentDataIndex = levelOffsets[level] * SEGMENT_DATA_SIZE; - while (--level >= 0) { - int nextIntercept = (int) getIntercept(segmentDataIndex + SEGMENT_DATA_SIZE, segmentData); - int index = Math.min(approximateIndex(key, segmentDataIndex, segmentData), nextIntercept); - assert index >= 0 && index <= levelOffsets[level + 1] - levelOffsets[level] - 1; - int sdIndex = (levelOffsets[level] + index) * SEGMENT_DATA_SIZE; - if (getKey(sdIndex, segmentData) <= key) { - // Scan sequentially segments after the approximated index, within the epsilon range. - final int levelNumSegments = levelOffsets[level + 1] - levelOffsets[level] - 1; - final int toIndex = Math.min(index + epsilonRecursive + 3, levelNumSegments); - while (index++ < toIndex && getKey(sdIndex + SEGMENT_DATA_SIZE, segmentData) <= key) { - sdIndex += SEGMENT_DATA_SIZE; - } - } else { - // Scan sequentially segments before the approximated index, within the epsilon range. - final int fromIndex = Math.max(index - epsilonRecursive - 1, 0); - while (index-- > fromIndex) { - sdIndex -= SEGMENT_DATA_SIZE; - if (getKey(sdIndex, segmentData) <= key) { - break; - } - } - } - segmentDataIndex = sdIndex; - } - assert segmentDataIndex >= 0; - return segmentDataIndex; - } - - private int approximateIndex(long key, int segmentDataIndex, int[] segmentData) { - long intercept = getIntercept(segmentDataIndex, segmentData); - long sKey = getKey(segmentDataIndex, segmentData); - double slope = getSlope(segmentDataIndex, segmentData); - int index = (int) (slope * ((double) key - sKey) + intercept); - return Math.max(index, 0); - } - - private static long getIntercept(int segmentDataIndex, int[] segmentData) { - return PgmIndexUtil.getIntercept(segmentDataIndex, segmentData, KEY_SIZE); - } - - private long getKey(int segmentDataIndex, int[] segmentData) { - return PgmIndexUtil.getKey(segmentDataIndex + KEY_SIZE, segmentData, 0L); - } - - private static double getSlope(int segmentDataIndex, int[] segmentData) { - return PgmIndexUtil.getSlope(segmentDataIndex + DOUBLE_KEY_SIZE, segmentData, KEY_SIZE); - } - - /** Empty immutable PGM Index. */ - private static class LongEmptyPgmIndex extends LongPgmIndex { - - private final Iterator emptyIterator = new LongEmptyIterator(); - - @Override - public int indexOf(long key) { - return -1; - } - - @Override - public Iterator rangeIterator(long minKey, long maxKey) { - return emptyIterator; - } - - @Override - public T forEachInRange(T procedure, long minKey, long maxKey) { - return procedure; - } - - private static class LongEmptyIterator extends AbstractIterator { - @Override - protected LongCursor fetch() { - return done(); - } - } - } - - /** Iterator over a range of elements in a sorted array. */ - protected static class RangeIterator extends AbstractIterator { - private final long[] buffer; - private final int size; - private final LongCursor cursor; - private final long maxKey; - - /** Range iterator from {@code fromIndex} (inclusive) to {@code maxKey} (inclusive). */ - protected RangeIterator(LongArrayList keys, int fromIndex, long maxKey) { - this.buffer = keys.buffer; - this.size = keys.size(); - this.cursor = new LongCursor(); - this.cursor.index = fromIndex; - this.maxKey = maxKey; - } - - @Override - protected LongCursor fetch() { - if (cursor.index >= size) { - return done(); - } - cursor.value = buffer[cursor.index++]; - if (cursor.value > maxKey) { - cursor.index = size; - return done(); - } - return cursor; - } - } - - /** Builds a {@link LongPgmIndex} on a provided sorted list of keys. */ - public static class LongBuilder implements PlaModel.SegmentConsumer, Accountable { - - protected LongArrayList keys; - protected int epsilon = EPSILON; - protected int epsilonRecursive = EPSILON_RECURSIVE; - protected PlaModel plam; - protected int size; - protected IntArrayList segmentData; - protected int numSegments; - - /** Sets the sorted list of keys to build the index for; duplicate elements are allowed. */ - public LongBuilder setSortedKeys(LongArrayList keys) { - this.keys = keys; - return this; - } - - /** Sets the sorted array of keys to build the index for; duplicate elements are allowed. */ - public LongBuilder setSortedKeys(long[] keys, int length) { - LongArrayList keyList = new LongArrayList(0); - keyList.buffer = keys; - keyList.elementsCount = length; - return setSortedKeys(keyList); - } - - /** Sets the epsilon range to use when learning the segments for the list of keys. */ - public LongBuilder setEpsilon(int epsilon) { - if (epsilon <= 0) { - throw new IllegalArgumentException("epsilon must be > 0"); - } - this.epsilon = epsilon; - return this; - } - - /** - * Sets the recursive epsilon range to use when learning the segments for the segment levels. - */ - public LongBuilder setEpsilonRecursive(int epsilonRecursive) { - if (epsilonRecursive <= 0) { - throw new IllegalArgumentException("epsilonRecursive must be > 0"); - } - this.epsilonRecursive = epsilonRecursive; - return this; - } - - /** Builds the {@link LongPgmIndex}; or {@link #EMPTY} if there are no keys in the list. */ - public LongPgmIndex build() { - if (keys == null || keys.size() == 0) { - return (LongPgmIndex) EMPTY; - } - plam = new PlaModel(epsilon); - - int segmentsInitialCapacity = - Math.min( - Math.max(keys.size() / (2 * epsilon * epsilon) * SEGMENT_DATA_SIZE, 16), 1 << 19); - segmentData = new IntArrayList(segmentsInitialCapacity); - IntArrayList levelOffsets = new IntArrayList(16); - - int levelOffset = 0; - levelOffsets.add(levelOffset); - int levelNumSegments = buildFirstLevel(); - while (levelNumSegments > 1) { - int nextLevelOffset = numSegments; - levelOffsets.add(nextLevelOffset); - levelNumSegments = buildUpperLevel(levelOffset, levelNumSegments); - levelOffset = nextLevelOffset; - } - - int[] segmentDataFinal = segmentData.toArray(); - int[] levelOffsetsFinal = levelOffsets.toArray(); - return new LongPgmIndex( - keys, size, epsilon, epsilonRecursive, levelOffsetsFinal, segmentDataFinal); - } - - private int buildFirstLevel() { - assert numSegments == 0; - int numKeys = keys.size(); - int size = 0; - long key = keys.get(0); - size++; - plam.addKey(key, 0, this); - for (int i = 1; i < numKeys; i++) { - long nextKey = keys.get(i); - if (!((nextKey) == (key))) { - key = nextKey; - plam.addKey(key, i, this); - size++; - } - } - plam.finish(this); - addSentinelSegment(numKeys); - this.size = size; - return numSegments - 1; - } - - private int buildUpperLevel(int levelOffset, int levelNumSegments) { - plam.setEpsilon(epsilonRecursive); - assert numSegments > 0; - int initialNumSegments = numSegments; - int segmentDataIndex = levelOffset * SEGMENT_DATA_SIZE; - long key = getKey(segmentDataIndex, segmentData.buffer); - plam.addKey(key, 0, this); - for (int i = 1; i < levelNumSegments; i++) { - segmentDataIndex += SEGMENT_DATA_SIZE; - long nextKey = getKey(segmentDataIndex, segmentData.buffer); - if (!((nextKey) == (key))) { - key = nextKey; - plam.addKey(key, i, this); - } - } - plam.finish(this); - addSentinelSegment(levelNumSegments); - return numSegments - initialNumSegments - 1; - } - - private long getKey(int segmentDataIndex, int[] segmentData) { - return PgmIndexUtil.getKey(segmentDataIndex + KEY_SIZE, segmentData, 0L); - } - - /** - * Adds a sentinel segment that is used to give a limit for the position approximation, but does - * not count in the number of segments per level. - */ - private void addSentinelSegment(int endIndex) { - // This sentinel segment is used in findSegment(). - accept(Double.MAX_VALUE, 0d, endIndex); - } - - @Override - public void accept(double firstKey, double slope, long intercept) { - PgmIndexUtil.addIntercept(intercept, segmentData, KEY_SIZE); - PgmIndexUtil.addKey((long) firstKey, segmentData); - PgmIndexUtil.addSlope(slope, segmentData, KEY_SIZE); - numSegments++; - assert segmentData.size() == numSegments * SEGMENT_DATA_SIZE; - } - - /** - * Estimates the allocated memory. It does not count the memory for the list of keys, only for - * the builder itself. - */ - @Override - public long ramBytesAllocated() { - // int: epsilon, epsilonRecursive, size, numSegments - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - // + keys.ramBytesAllocated() - + plam.ramBytesAllocated() - + segmentData.ramBytesAllocated(); - } - - /** - * Estimates the bytes that are actually used. It does not count the memory for the list of - * keys, only for the builder itself. - */ - @Override - public long ramBytesUsed() { - // int: epsilon, epsilonRecursive, size, numSegments - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - // + keys.ramBytesUsed() - + plam.ramBytesUsed() - + segmentData.ramBytesUsed(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/LongSet.java b/src/main/java/com/carrotsearch/hppc/LongSet.java deleted file mode 100755 index 69a3144a..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongSet.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.carrotsearch.hppc; - -/** A set of longs. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeSet.java") -public interface LongSet extends LongCollection { - /** - * Adds k to the set. - * - * @return Returns true if this element was not part of the set before. Returns - * false if an equal element is already part of the set, does not replace the - * existing element with the argument. - */ - public boolean add(long k); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); - - /** - * Adds all elements from the given {@link LongContainer} to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - * @since 0.9.1 - */ - public int addAll(LongContainer container); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongShortAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/LongShortAssociativeContainer.java deleted file mode 100755 index e9f9f677..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongShortAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see LongContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface LongShortAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(long key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(LongShortPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link LongShortProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link LongShortPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public LongCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public ShortContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongShortHashMap.java b/src/main/java/com/carrotsearch/hppc/LongShortHashMap.java deleted file mode 100755 index cf9a34c8..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongShortHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of long to short, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeHashMap.java") -public class LongShortHashMap implements LongShortMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public long[] keys; - - /** The array holding values. */ - public short[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public LongShortHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public LongShortHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public LongShortHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public LongShortHashMap(LongShortAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public short put(long key, short value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - short previousValue = hasEmptyKey ? values[mask + 1] : ((short) 0); - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final short previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return ((short) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(LongShortAssociativeContainer container) { - final int count = size(); - for (LongShortCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (LongShortCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public short putOrAdd(long key, short putValue, short incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((short) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public short addTo(long key, short incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public short remove(long key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return ((short) 0); - } - hasEmptyKey = false; - short previousValue = values[mask + 1]; - values[mask + 1] = ((short) 0); - return previousValue; - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final short previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return ((short) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof LongLookupContainer) { - if (hasEmptyKey && other.contains(0L)) { - hasEmptyKey = false; - values[mask + 1] = ((short) 0); - } - - final long[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - long existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (LongCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongShortPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(0L, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = ((short) 0); - } - } - - final long[] keys = this.keys; - final short[] values = this.values; - for (int slot = 0; slot <= mask; ) { - long existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(LongPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(0L)) { - hasEmptyKey = false; - values[mask + 1] = ((short) 0); - } - } - - final long[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - long existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public short get(long key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : ((short) 0); - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return ((short) 0); - } - } - - /** {@inheritDoc} */ - @Override - public short getOrDefault(long key, short defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(long key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final long[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(long key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final long[] keys = this.keys; - int slot = hashKey(key) & mask; - - long existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public short indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public short indexReplace(int index, short newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - short previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, long key, short value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public short indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - short previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = ((short) 0); - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, 0L); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (LongShortCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(LongShortHashMap other) { - if (other.size() != size()) { - return false; - } - - for (LongShortCursor c : other) { - long key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final long[] prevKeys = this.keys; - final short[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final LongShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new LongShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongShortCursor fetch() { - final int mask = LongShortHashMap.this.mask; - while (index <= mask) { - long existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = 0L; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final long[] keys = this.keys; - final short[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(0L, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final long[] keys = this.keys; - final short[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(0L, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractLongCollection implements LongLookupContainer { - private final LongShortHashMap owner = LongShortHashMap.this; - - @Override - public boolean contains(long e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((LongShortProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((LongShortPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(LongPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final long e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final LongCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new LongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongCursor fetch() { - final int mask = LongShortHashMap.this.mask; - while (index <= mask) { - long existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = 0L; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public ShortCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractShortCollection { - private final LongShortHashMap owner = LongShortHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(short value) { - for (LongShortCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (LongShortCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (LongShortCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final short e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final ShortPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new ShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortCursor fetch() { - final int mask = LongShortHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public LongShortHashMap clone() { - try { - - LongShortHashMap cloned = (LongShortHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (LongShortCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return LongBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static LongShortHashMap from(long[] keys, short[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - LongShortHashMap map = new LongShortHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(long key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(long[] fromKeys, short[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final long[] keys = this.keys; - final short[] values = this.values; - final int mask = this.mask; - long existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - long[] prevKeys = this.keys; - short[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new long[arraySize + emptyElementSlot]); - this.values = (new short[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, long pendingKey, short pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final long[] prevKeys = this.keys; - final short[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final long[] keys = this.keys; - final short[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final long existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = 0L; - values[gapSlot] = ((short) 0); - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/LongShortMap.java b/src/main/java/com/carrotsearch/hppc/LongShortMap.java deleted file mode 100755 index 3ef50522..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongShortMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.LongShortCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface LongShortMap extends LongShortAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public short get(long key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public short getOrDefault(long key, short defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public short put(long key, short value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(long key, short value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(LongShortAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public short putOrAdd(long key, short putValue, short incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public short addTo(long key, short additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public short remove(long key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link LongShortMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(long key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public short indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public short indexReplace(int index, short newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, long key, short value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public short indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/LongStack.java b/src/main/java/com/carrotsearch/hppc/LongStack.java deleted file mode 100755 index 6d5f15ff..00000000 --- a/src/main/java/com/carrotsearch/hppc/LongStack.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.LongCursor; - -/** - * A subclass of {@link LongArrayList} adding stack-related utility methods. The top of the stack is - * at the {@link #size()} - 1 element. - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeStack.java") -public class LongStack extends LongArrayList { - /** New instance with sane defaults. */ - public LongStack() { - super(); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public LongStack(int expectedElements) { - super(expectedElements); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public LongStack(int expectedElements, ArraySizingStrategy resizer) { - super(expectedElements, resizer); - } - - /** Create a stack by pushing all elements of another container to it. */ - public LongStack(LongContainer container) { - super(container); - } - - /** Adds one long to the stack. */ - public void push(long e1) { - ensureBufferSpace(1); - buffer[elementsCount++] = e1; - } - - /** Adds two longs to the stack. */ - public void push(long e1, long e2) { - ensureBufferSpace(2); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - } - - /** Adds three longs to the stack. */ - public void push(long e1, long e2, long e3) { - ensureBufferSpace(3); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - buffer[elementsCount++] = e3; - } - - /** Adds four longs to the stack. */ - public void push(long e1, long e2, long e3, long e4) { - ensureBufferSpace(4); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - buffer[elementsCount++] = e3; - buffer[elementsCount++] = e4; - } - - /** Add a range of array elements to the stack. */ - public void push(long[] elements, int start, int len) { - assert start >= 0 && len >= 0; - - ensureBufferSpace(len); - System.arraycopy(elements, start, buffer, elementsCount, len); - elementsCount += len; - } - - /** - * Vararg-signature method for pushing elements at the top of the stack. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - */ - public final void push(long... elements) { - push(elements, 0, elements.length); - } - - /** Pushes all elements from another container to the top of the stack. */ - public int pushAll(LongContainer container) { - return addAll(container); - } - - /** Pushes all elements from another iterable to the top of the stack. */ - public int pushAll(Iterable iterable) { - return addAll(iterable); - } - - /** Discard an arbitrary number of elements from the top of the stack. */ - public void discard(int count) { - assert elementsCount >= count; - - elementsCount -= count; - } - - /** Discard the top element from the stack. */ - public void discard() { - assert elementsCount > 0; - - elementsCount--; - } - - /** Remove the top element from the stack and return it. */ - public long pop() { - return removeLast(); - } - - /** Peek at the top element on the stack. */ - public long peek() { - assert elementsCount > 0; - return buffer[elementsCount - 1]; - } - - /** Create a stack by pushing a variable number of arguments to it. */ - public static LongStack from(long... elements) { - final LongStack stack = new LongStack(elements.length); - stack.push(elements); - return stack; - } - - /** {@inheritDoc} */ - @Override - public LongStack clone() { - return (LongStack) super.clone(); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectArrayDeque.java b/src/main/java/com/carrotsearch/hppc/ObjectArrayDeque.java deleted file mode 100755 index a25261f8..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectArrayDeque.java +++ /dev/null @@ -1,786 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; - -import com.carrotsearch.hppc.cursors.ObjectCursor; -import com.carrotsearch.hppc.predicates.ObjectPredicate; -import com.carrotsearch.hppc.procedures.ObjectProcedure; -import java.util.*; - -/** An array-backed {@link ObjectDeque}. */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeArrayDeque.java") -public class ObjectArrayDeque extends AbstractObjectCollection - implements ObjectDeque, Preallocable, Cloneable, Accountable { - - /** Reuse the same strategy instance. */ - private static final BoundedProportionalArraySizingStrategy DEFAULT_SIZING_STRATEGY = - BoundedProportionalArraySizingStrategy.DEFAULT_INSTANCE; - - /** Internal array for storing elements of the deque. */ - public Object[] buffer = ObjectArrayList.EMPTY_ARRAY; - - /** - * The index of the element at the head of the deque or an arbitrary number equal to tail if the - * deque is empty. - */ - public int head; - - /** The index at which the next element would be added to the tail of the deque. */ - public int tail; - - /** Buffer resizing strategy. */ - protected final ArraySizingStrategy resizer; - - /** New instance with sane defaults. */ - public ObjectArrayDeque() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectArrayDeque(int expectedElements) { - this(expectedElements, DEFAULT_SIZING_STRATEGY); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public ObjectArrayDeque(int expectedElements, ArraySizingStrategy resizer) { - assert resizer != null; - this.resizer = resizer; - ensureCapacity(expectedElements); - } - - /** - * Creates a new deque from elements of another container, appending elements at the end of the - * deque in the iteration order. - */ - public ObjectArrayDeque(ObjectContainer container) { - this(container.size()); - addLast(container); - } - - /** {@inheritDoc} */ - @Override - public void addFirst(KType e1) { - int h = oneLeft(head, buffer.length); - if (h == tail) { - ensureBufferSpace(1); - h = oneLeft(head, buffer.length); - } - buffer[head = h] = e1; - } - - /** - * Vararg-signature method for adding elements at the front of this deque. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - * - * @param elements The elements to add. - */ - @SafeVarargs - public final void addFirst(KType... elements) { - ensureBufferSpace(elements.length); - for (KType k : elements) { - addFirst(k); - } - } - - /** - * Inserts all elements from the given container to the front of this deque. - * - * @param container The container to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addFirst(ObjectContainer container) { - int size = container.size(); - ensureBufferSpace(size); - - for (ObjectCursor cursor : container) { - addFirst(cursor.value); - } - - return size; - } - - /** - * Inserts all elements from the given iterable to the front of this deque. - * - * @param iterable The iterable to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addFirst(Iterable> iterable) { - int size = 0; - for (ObjectCursor cursor : iterable) { - addFirst(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public void addLast(KType e1) { - int t = oneRight(tail, buffer.length); - if (head == t) { - ensureBufferSpace(1); - t = oneRight(tail, buffer.length); - } - buffer[tail] = e1; - tail = t; - } - - /** - * Vararg-signature method for adding elements at the end of this deque. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - * - * @param elements The elements to iterate over. - */ - @SafeVarargs - public final void addLast(KType... elements) { - ensureBufferSpace(1); - for (KType k : elements) { - addLast(k); - } - } - - /** - * Inserts all elements from the given container to the end of this deque. - * - * @param container The container to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addLast(ObjectContainer container) { - int size = container.size(); - ensureBufferSpace(size); - - for (ObjectCursor cursor : container) { - addLast(cursor.value); - } - - return size; - } - - /** - * Inserts all elements from the given iterable to the end of this deque. - * - * @param iterable The iterable to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addLast(Iterable> iterable) { - int size = 0; - for (ObjectCursor cursor : iterable) { - addLast(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public KType removeFirst() { - assert size() > 0 : "The deque is empty."; - - final KType result = (KType) buffer[head]; - buffer[head] = null; - head = oneRight(head, buffer.length); - return result; - } - - /** {@inheritDoc} */ - @Override - public KType removeLast() { - assert size() > 0 : "The deque is empty."; - - tail = oneLeft(tail, buffer.length); - final KType result = (KType) buffer[tail]; - buffer[tail] = null; - return result; - } - - /** {@inheritDoc} */ - @Override - public KType getFirst() { - assert size() > 0 : "The deque is empty."; - - return (KType) buffer[head]; - } - - /** {@inheritDoc} */ - @Override - public KType getLast() { - assert size() > 0 : "The deque is empty."; - - return (KType) buffer[oneLeft(tail, buffer.length)]; - } - - /** {@inheritDoc} */ - @Override - public int removeFirst(KType e1) { - final int index = bufferIndexOf(e1); - if (index >= 0) removeAtBufferIndex(index); - return index; - } - - /** - * Return the index of the first (counting from head) element equal to e1. The index - * points to the {@link #buffer} array. - * - * @param e1 The element to look for. - * @return Returns the index of the first element equal to e1 or -1 if - * not found. - */ - public int bufferIndexOf(KType e1) { - final int last = tail; - final int bufLen = buffer.length; - for (int i = head; i != last; i = oneRight(i, bufLen)) { - if (this.equals(e1, buffer[i])) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int removeLast(KType e1) { - final int index = lastBufferIndexOf(e1); - if (index >= 0) { - removeAtBufferIndex(index); - } - return index; - } - - /** - * Return the index of the last (counting from tail) element equal to e1. The index - * points to the {@link #buffer} array. - * - * @param e1 The element to look for. - * @return Returns the index of the first element equal to e1 or -1 if - * not found. - */ - public int lastBufferIndexOf(KType e1) { - final int bufLen = buffer.length; - final int last = oneLeft(head, bufLen); - for (int i = oneLeft(tail, bufLen); i != last; i = oneLeft(i, bufLen)) { - if (this.equals(e1, buffer[i])) return i; - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(KType e1) { - int removed = 0; - final int last = tail; - final int bufLen = buffer.length; - int from, to; - for (from = to = head; from != last; from = oneRight(from, bufLen)) { - if (this.equals(e1, buffer[from])) { - buffer[from] = null; - removed++; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = null; - } - - to = oneRight(to, bufLen); - } - - tail = to; - return removed; - } - - /** - * Removes the element at index in the internal {#link {@link #buffer} array, - * returning its value. - * - * @param index Index of the element to remove. The index must be located between {@link #head} - * and {@link #tail} in modulo {@link #buffer} arithmetic. - */ - public void removeAtBufferIndex(int index) { - assert (head <= tail ? index >= head && index < tail : index >= head || index < tail) - : "Index out of range (head=" + head + ", tail=" + tail + ", index=" + index + ")."; - - // Cache fields in locals (hopefully moved to registers). - final KType[] buffer = (KType[]) this.buffer; - final int bufLen = buffer.length; - final int lastIndex = bufLen - 1; - final int head = this.head; - final int tail = this.tail; - - final int leftChunk = Math.abs(index - head) % bufLen; - final int rightChunk = Math.abs(tail - index) % bufLen; - - if (leftChunk < rightChunk) { - if (index >= head) { - System.arraycopy(buffer, head, buffer, head + 1, leftChunk); - } else { - System.arraycopy(buffer, 0, buffer, 1, index); - buffer[0] = buffer[lastIndex]; - System.arraycopy(buffer, head, buffer, head + 1, lastIndex - head); - } - buffer[head] = null; - this.head = oneRight(head, bufLen); - } else { - if (index < tail) { - System.arraycopy(buffer, index + 1, buffer, index, rightChunk); - } else { - System.arraycopy(buffer, index + 1, buffer, index, lastIndex - index); - buffer[lastIndex] = buffer[0]; - System.arraycopy(buffer, 1, buffer, 0, tail); - } - buffer[tail] = null; - this.tail = oneLeft(tail, bufLen); - } - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int size() { - if (head <= tail) return tail - head; - else return (tail - head + buffer.length); - } - - /** - * {@inheritDoc} - * - *

The internal array buffers are not released as a result of this call. - * - * @see #release() - */ - @Override - public void clear() { - if (head < tail) { - Arrays.fill(buffer, head, tail, null); - } else { - Arrays.fill(buffer, 0, tail, null); - Arrays.fill(buffer, head, buffer.length, null); - } - this.head = tail = 0; - } - - /** Release internal buffers of this deque and reallocate with the default buffer. */ - public void release() { - this.head = tail = 0; - buffer = ObjectArrayList.EMPTY_ARRAY; - ensureBufferSpace(0); - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - ensureBufferSpace(expectedElements - size()); - } - - /** - * Ensures the internal buffer has enough free slots to store expectedAdditions. - * Increases internal buffer size if needed. - */ - protected void ensureBufferSpace(int expectedAdditions) { - final int bufferLen = buffer.length; - final int elementsCount = size(); - - if (elementsCount + expectedAdditions >= bufferLen) { - final int emptySlot = 1; // deque invariant: always an empty slot. - final int newSize = resizer.grow(bufferLen, elementsCount + emptySlot, expectedAdditions); - assert newSize >= (elementsCount + expectedAdditions + emptySlot) - : "Resizer failed to" - + " return sensible new size: " - + newSize - + " <= " - + (elementsCount + expectedAdditions); - - try { - final KType[] newBuffer = ((KType[]) new Object[newSize]); - if (bufferLen > 0) { - toArray(newBuffer); - tail = elementsCount; - head = 0; - } - this.buffer = newBuffer; - } catch (OutOfMemoryError e) { - throw new BufferAllocationException( - "Not enough memory to allocate new buffers: %,d -> %,d", e, bufferLen, newSize); - } - } - } - - /** {@inheritDoc} */ - @Override - public Object[] toArray() { - - final int size = size(); - return toArray(((KType[]) new Object[size])); - } - - /** - * Copies elements of this deque to an array. The content of the target array is - * filled from index 0 (head of the queue) to index size() - 1 (tail of the queue). - * - * @param target The target array must be large enough to hold all elements. - * @return Returns the target argument for chaining. - */ - public KType[] toArray(KType[] target) { - assert target.length >= size() : "Target array must be >= " + size(); - - if (head < tail) { - // The contents is not wrapped around. Just copy. - System.arraycopy(buffer, head, target, 0, size()); - } else if (head > tail) { - // The contents is split. Merge elements from the following indexes: - // [head...buffer.length - 1][0, tail - 1] - final int rightCount = buffer.length - head; - System.arraycopy(buffer, head, target, 0, rightCount); - System.arraycopy(buffer, 0, target, rightCount, tail); - } - - return target; - } - - /** - * Clone this object. The returned clone will reuse the same hash function and array resizing - * strategy. - */ - @Override - public ObjectArrayDeque clone() { - try { - - ObjectArrayDeque cloned = (ObjectArrayDeque) super.clone(); - cloned.buffer = buffer.clone(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Move one index to the left, wrapping around buffer. */ - protected static int oneLeft(int index, int modulus) { - if (index >= 1) { - return index - 1; - } - return modulus - 1; - } - - /** Move one index to the right, wrapping around buffer. */ - protected static int oneRight(int index, int modulus) { - if (index + 1 == modulus) { - return 0; - } - return index + 1; - } - - @Override - public long ramBytesAllocated() { - // int: head, tail - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES * 2 - + resizer.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(buffer); - } - - @Override - public long ramBytesUsed() { - // int: head, tail - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES * 2 - + resizer.ramBytesUsed() - + RamUsageEstimator.shallowUsedSizeOfArray(buffer, size()); - } - - /** An iterator implementation for {@link ObjectArrayDeque#iterator}. */ - private final class ValueIterator extends AbstractIterator> { - private final ObjectCursor cursor; - private int remaining; - - public ValueIterator() { - cursor = new ObjectCursor(); - cursor.index = oneLeft(head, buffer.length); - this.remaining = size(); - } - - @Override - protected ObjectCursor fetch() { - if (remaining == 0) { - return done(); - } - - remaining--; - cursor.value = (KType) buffer[cursor.index = oneRight(cursor.index, buffer.length)]; - return cursor; - } - } - - /** An iterator implementation for {@link ObjectArrayDeque#descendingIterator()}. */ - private final class DescendingValueIterator extends AbstractIterator> { - private final ObjectCursor cursor; - private int remaining; - - public DescendingValueIterator() { - cursor = new ObjectCursor(); - cursor.index = tail; - this.remaining = size(); - } - - @Override - protected ObjectCursor fetch() { - if (remaining == 0) return done(); - - remaining--; - cursor.value = (KType) buffer[cursor.index = oneLeft(cursor.index, buffer.length)]; - return cursor; - } - } - - /** - * Returns a cursor over the values of this deque (in head to tail order). The iterator is - * implemented as a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()} (to avoid boxing of primitive types). To read the current value (or index in - * the deque's buffer) use the cursor's public fields. An example is shown below. - * - *

-   * for (IntValueCursor c : intDeque) {
-   *   System.out.println("buffer index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator> iterator() { - return new ValueIterator(); - } - - /** - * Returns a cursor over the values of this deque (in tail to head order). The iterator is - * implemented as a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()} (to avoid boxing of primitive types). To read the current value (or index in - * the deque's buffer) use the cursor's public fields. An example is shown below. - * - *
-   * for (Iterator<IntCursor> i = intDeque.descendingIterator(); i.hasNext();) {
-   *   final IntCursor c = i.next();
-   *   System.out.println("buffer index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator> descendingIterator() { - return new DescendingValueIterator(); - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T procedure) { - forEach(procedure, head, tail); - return procedure; - } - - /** - * Applies procedure to a slice of the deque, fromIndex, inclusive, to - * toIndex, exclusive. - */ - private void forEach(ObjectProcedure procedure, int fromIndex, final int toIndex) { - final KType[] buffer = (KType[]) this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - procedure.apply(buffer[i]); - } - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T predicate) { - int fromIndex = head; - int toIndex = tail; - - final KType[] buffer = (KType[]) this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - if (!predicate.apply(buffer[i])) { - break; - } - } - - return predicate; - } - - /** Applies procedure to all elements of this deque, tail to head. */ - @Override - public > T descendingForEach(T procedure) { - descendingForEach(procedure, head, tail); - return procedure; - } - - /** - * Applies procedure to a slice of the deque, toIndex, exclusive, down - * to fromIndex, inclusive. - */ - private void descendingForEach( - ObjectProcedure procedure, int fromIndex, final int toIndex) { - if (fromIndex == toIndex) return; - - final KType[] buffer = (KType[]) this.buffer; - int i = toIndex; - do { - i = oneLeft(i, buffer.length); - procedure.apply(buffer[i]); - } while (i != fromIndex); - } - - /** {@inheritDoc} */ - @Override - public > T descendingForEach(T predicate) { - descendingForEach(predicate, head, tail); - return predicate; - } - - /** - * Applies predicate to a slice of the deque, toIndex, exclusive, down - * to fromIndex, inclusive or until the predicate returns false. - */ - private void descendingForEach( - ObjectPredicate predicate, int fromIndex, final int toIndex) { - if (fromIndex == toIndex) return; - - final KType[] buffer = (KType[]) this.buffer; - int i = toIndex; - do { - i = oneLeft(i, buffer.length); - if (!predicate.apply(buffer[i])) { - break; - } - } while (i != fromIndex); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectPredicate predicate) { - final KType[] buffer = (KType[]) this.buffer; - final int last = tail; - final int bufLen = buffer.length; - int removed = 0; - int from, to; - from = to = head; - try { - for (from = to = head; from != last; from = oneRight(from, bufLen)) { - if (predicate.apply(buffer[from])) { - buffer[from] = null; - removed++; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = null; - } - - to = oneRight(to, bufLen); - } - } finally { - // Keep the deque in consistent state even if the predicate throws an exception. - for (; from != last; from = oneRight(from, bufLen)) { - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = null; - } - - to = oneRight(to, bufLen); - } - tail = to; - } - - return removed; - } - - /** {@inheritDoc} */ - @Override - public boolean contains(KType e) { - int fromIndex = head; - int toIndex = tail; - - final KType[] buffer = (KType[]) this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - if (this.equals(e, buffer[i])) { - return true; - } - } - - return false; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = 1; - int fromIndex = head; - int toIndex = tail; - - final KType[] buffer = (KType[]) this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - h = 31 * h + BitMixer.mix(this.buffer[i]); - } - return h; - } - - /** - * Returns true only if the other object is an instance of the same class and with - * the same elements. Equality comparison is performed with this object's {@link #equals(Object, - * Object)} method. - */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** - * Compare order-aligned elements against another {@link ObjectDeque}. Equality comparison is - * performed with this object's {@link #equals(Object, Object)} method. - */ - protected boolean equalElements(ObjectArrayDeque other) { - int max = size(); - if (other.size() != max) { - return false; - } - - Iterator> i1 = this.iterator(); - Iterator> i2 = other.iterator(); - - while (i1.hasNext() && i2.hasNext()) { - if (!this.equals(i1.next().value, i2.next().value)) { - return false; - } - } - - return !i1.hasNext() && !i2.hasNext(); - } - - /** Create a new deque by pushing a variable number of arguments to the end of it. */ - @SafeVarargs - public static ObjectArrayDeque from(KType... elements) { - final ObjectArrayDeque coll = new ObjectArrayDeque(elements.length); - coll.addLast(elements); - return coll; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectArrayList.java b/src/main/java/com/carrotsearch/hppc/ObjectArrayList.java deleted file mode 100755 index e11da848..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectArrayList.java +++ /dev/null @@ -1,604 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.ObjectPredicate; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; -import java.util.stream.Stream; - -/** An array-backed list of Objects. */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeArrayList.java") -public class ObjectArrayList extends AbstractObjectCollection - implements ObjectIndexedContainer, Preallocable, Cloneable, Accountable { - /** An immutable empty buffer (array). */ - public static final Object[] EMPTY_ARRAY = new Object[0]; - - ; - - /** Reuse the same strategy instance. */ - private static final BoundedProportionalArraySizingStrategy DEFAULT_SIZING_STRATEGY = - BoundedProportionalArraySizingStrategy.DEFAULT_INSTANCE; - - /** - * Internal array for storing the list. The array may be larger than the current size ({@link - * #size()}). - */ - public Object[] buffer = EMPTY_ARRAY; - - /** Current number of elements stored in {@link #buffer}. */ - public int elementsCount; - - /** Buffer resizing strategy. */ - protected final ArraySizingStrategy resizer; - - /** New instance with sane defaults. */ - public ObjectArrayList() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectArrayList(int expectedElements) { - this(expectedElements, DEFAULT_SIZING_STRATEGY); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public ObjectArrayList(int expectedElements, ArraySizingStrategy resizer) { - assert resizer != null; - this.resizer = resizer; - buffer = Arrays.copyOf(buffer, expectedElements); - } - - /** Creates a new list from the elements of another container in its iteration order. */ - public ObjectArrayList(ObjectContainer container) { - this(container.size()); - addAll(container); - } - - /** {@inheritDoc} */ - @Override - public void add(KType e1) { - ensureBufferSpace(1); - buffer[elementsCount++] = e1; - } - - /** - * Appends two elements at the end of the list. To add more than two elements, use add - * (vararg-version) or access the buffer directly (tight loop). - */ - public void add(KType e1, KType e2) { - ensureBufferSpace(2); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - } - - /** Add all elements from a range of given array to the list. */ - public void add(KType[] elements, int start, int length) { - assert length >= 0 : "Length must be >= 0"; - - ensureBufferSpace(length); - System.arraycopy(elements, start, buffer, elementsCount, length); - elementsCount += length; - } - - /** - * Vararg-signature method for adding elements at the end of the list. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - */ - @SafeVarargs - public final void add(KType... elements) { - add(elements, 0, elements.length); - } - - /** Adds all elements from another container. */ - public int addAll(ObjectContainer container) { - final int size = container.size(); - ensureBufferSpace(size); - - for (ObjectCursor cursor : container) { - add(cursor.value); - } - - return size; - } - - /** Adds all elements from another iterable. */ - public int addAll(Iterable> iterable) { - int size = 0; - for (ObjectCursor cursor : iterable) { - add(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public void insert(int index, KType e1) { - assert (index >= 0 && index <= size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + "]."; - - ensureBufferSpace(1); - System.arraycopy(buffer, index, buffer, index + 1, elementsCount - index); - buffer[index] = e1; - elementsCount++; - } - - /** {@inheritDoc} */ - @Override - public KType get(int index) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - return (KType) buffer[index]; - } - - /** {@inheritDoc} */ - @Override - public KType set(int index, KType e1) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - final KType v = (KType) buffer[index]; - buffer[index] = e1; - return v; - } - - /** {@inheritDoc} */ - @Override - public KType removeAt(int index) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - final KType v = (KType) buffer[index]; - System.arraycopy(buffer, index + 1, buffer, index, --elementsCount - index); - - buffer[elementsCount] = null; - - return v; - } - - /** {@inheritDoc} */ - @Override - public KType removeLast() { - assert elementsCount > 0; - - final KType v = (KType) buffer[--elementsCount]; - - buffer[elementsCount] = null; - - return v; - } - - /** {@inheritDoc} */ - @Override - public void removeRange(int fromIndex, int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - System.arraycopy(buffer, toIndex, buffer, fromIndex, elementsCount - toIndex); - final int count = toIndex - fromIndex; - elementsCount -= count; - - Arrays.fill(buffer, elementsCount, elementsCount + count, null); - } - - /** {@inheritDoc} */ - @Override - public boolean removeElement(KType e1) { - return removeFirst(e1) != -1; - } - - /** {@inheritDoc} */ - @Override - public int removeFirst(KType e1) { - final int index = indexOf(e1); - if (index >= 0) removeAt(index); - return index; - } - - /** {@inheritDoc} */ - @Override - public int removeLast(KType e1) { - final int index = lastIndexOf(e1); - if (index >= 0) removeAt(index); - return index; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(KType e1) { - int to = 0; - for (int from = 0; from < elementsCount; from++) { - if (this.equals(e1, buffer[from])) { - continue; - } - if (to != from) { - buffer[to] = buffer[from]; - } - to++; - } - final int deleted = elementsCount - to; - this.elementsCount = to; - - Arrays.fill(buffer, elementsCount, elementsCount + deleted, null); - - return deleted; - } - - /** {@inheritDoc} */ - @Override - public boolean contains(KType e1) { - return indexOf(e1) >= 0; - } - - /** {@inheritDoc} */ - @Override - public int indexOf(KType e1) { - for (int i = 0; i < elementsCount; i++) { - if (this.equals(e1, buffer[i])) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int lastIndexOf(KType e1) { - for (int i = elementsCount - 1; i >= 0; i--) { - if (this.equals(e1, buffer[i])) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return elementsCount == 0; - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - final int bufferLen = (buffer == null ? 0 : buffer.length); - if (expectedElements > bufferLen) { - ensureBufferSpace(expectedElements - size()); - } - } - - /** - * Ensures the internal buffer has enough free slots to store expectedAdditions. - * Increases internal buffer size if needed. - */ - protected void ensureBufferSpace(int expectedAdditions) { - final int bufferLen = (buffer == null ? 0 : buffer.length); - if (elementsCount + expectedAdditions > bufferLen) { - final int newSize = resizer.grow(bufferLen, elementsCount, expectedAdditions); - assert newSize >= elementsCount + expectedAdditions - : "Resizer failed to" - + " return sensible new size: " - + newSize - + " <= " - + (elementsCount + expectedAdditions); - - this.buffer = Arrays.copyOf(buffer, newSize); - } - } - - /** - * Truncate or expand the list to the new size. If the list is truncated, the buffer will not be - * reallocated (use {@link #trimToSize()} if you need a truncated buffer), but the truncated - * values will be reset to the default value (zero). If the list is expanded, the elements beyond - * the current size are initialized with JVM-defaults (zero or null values). - */ - public void resize(int newSize) { - if (newSize <= buffer.length) { - if (newSize < elementsCount) { - Arrays.fill(buffer, newSize, elementsCount, null); - } else { - Arrays.fill(buffer, elementsCount, newSize, null); - } - } else { - ensureCapacity(newSize); - } - this.elementsCount = newSize; - } - - /** {@inheritDoc} */ - @Override - public int size() { - return elementsCount; - } - - /** Trim the internal buffer to the current size. */ - public void trimToSize() { - if (size() != this.buffer.length) { - this.buffer = (KType[]) toArray(); - } - } - - /** - * Sets the number of stored elements to zero. Releases and initializes the internal storage array - * to default values. To clear the list without cleaning the buffer, simply set the {@link - * #elementsCount} field to zero. - */ - @Override - public void clear() { - Arrays.fill(buffer, 0, elementsCount, null); - this.elementsCount = 0; - } - - /** Sets the number of stored elements to zero and releases the internal storage array. */ - @Override - public void release() { - this.buffer = (KType[]) EMPTY_ARRAY; - this.elementsCount = 0; - } - - /** - * {@inheritDoc} - * - *

The returned array is sized to match exactly the number of elements of the stack. - */ - @Override - public Object[] toArray() { - - return Arrays.copyOf(buffer, elementsCount); - } - - /** {@inheritDoc} */ - @Override - @SuppressWarnings("unchecked") - public Stream stream() { - return (Stream) Arrays.stream(buffer, 0, size()); - } - - /** {@inheritDoc} */ - @Override - public ObjectIndexedContainer sort() { - Arrays.sort(buffer, 0, elementsCount); - return this; - } - - /** {@inheritDoc} */ - @Override - public ObjectIndexedContainer reverse() { - for (int i = 0, mid = elementsCount >> 1, j = elementsCount - 1; i < mid; i++, j--) { - KType tmp = (KType) buffer[i]; - buffer[i] = buffer[j]; - buffer[j] = tmp; - } - return this; - } - - /** - * Clone this object. The returned clone will reuse the same hash function and array resizing - * strategy. - */ - @Override - public ObjectArrayList clone() { - try { - - final ObjectArrayList cloned = (ObjectArrayList) super.clone(); - cloned.buffer = buffer.clone(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = 1, max = elementsCount; - for (int i = 0; i < max; i++) { - h = 31 * h + BitMixer.mix(this.buffer[i]); - } - return h; - } - - /** - * Returns true only if the other object is an instance of the same class and with - * the same elements. Equality comparison is performed with this object's {@link #equals(Object, - * Object)} method. - */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** - * Compare index-aligned elements against another {@link ObjectIndexedContainer}. Equality - * comparison is performed with this object's {@link #equals(Object, Object)} method. - */ - protected boolean equalElements(ObjectArrayList other) { - int max = size(); - if (other.size() != max) { - return false; - } - - for (int i = 0; i < max; i++) { - if (!this.equals(get(i), other.get(i))) { - return false; - } - } - - return true; - } - - @Override - public long ramBytesAllocated() { - // int: elementsCount - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES - + resizer.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(buffer); - } - - @Override - public long ramBytesUsed() { - // int: elementsCount - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES - + resizer.ramBytesUsed() - + RamUsageEstimator.shallowUsedSizeOfArray(buffer, elementsCount); - } - - /** An iterator implementation for {@link ObjectArrayList#iterator}. */ - static final class ValueIterator extends AbstractIterator> { - private final ObjectCursor cursor; - - private final KType[] buffer; - private final int size; - - public ValueIterator(KType[] buffer, int size) { - this.cursor = new ObjectCursor(); - this.cursor.index = -1; - this.size = size; - this.buffer = buffer; - } - - @Override - protected ObjectCursor fetch() { - if (cursor.index + 1 == size) return done(); - - cursor.value = buffer[++cursor.index]; - return cursor; - } - } - - /** {@inheritDoc} */ - @Override - public Iterator> iterator() { - return new ValueIterator((KType[]) buffer, size()); - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T procedure) { - return forEach(procedure, 0, size()); - } - - /** - * Applies procedure to a slice of the list, fromIndex, inclusive, to - * toIndex, exclusive. - */ - public > T forEach( - T procedure, int fromIndex, final int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - final KType[] buffer = (KType[]) this.buffer; - for (int i = fromIndex; i < toIndex; i++) { - procedure.apply(buffer[i]); - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectPredicate predicate) { - final KType[] buffer = (KType[]) this.buffer; - final int elementsCount = this.elementsCount; - int to = 0; - int from = 0; - try { - for (; from < elementsCount; from++) { - if (predicate.apply(buffer[from])) { - buffer[from] = null; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = null; - } - to++; - } - } finally { - // Keep the list in a consistent state, even if the predicate throws an exception. - for (; from < elementsCount; from++) { - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = null; - } - to++; - } - - this.elementsCount = to; - } - - return elementsCount - to; - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T predicate) { - return forEach(predicate, 0, size()); - } - - /** - * Applies predicate to a slice of the list, fromIndex, inclusive, to - * toIndex, exclusive, or until predicate returns false. - */ - public > T forEach( - T predicate, int fromIndex, final int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - final KType[] buffer = (KType[]) this.buffer; - for (int i = fromIndex; i < toIndex; i++) { - if (!predicate.apply(buffer[i])) break; - } - - return predicate; - } - - /** - * Create a list from a variable number of arguments or an array of Object. The - * elements are copied from the argument to the internal buffer. - */ - @SafeVarargs - public static ObjectArrayList from(KType... elements) { - final ObjectArrayList list = new ObjectArrayList(elements.length); - list.add(elements); - return list; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectBufferVisualizer.java b/src/main/java/com/carrotsearch/hppc/ObjectBufferVisualizer.java deleted file mode 100755 index c7a988f7..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectBufferVisualizer.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.carrotsearch.hppc; - -/** - * Reused buffer visualization routines. - * - * @see ObjectSet#visualizeKeyDistribution(int) - * @see ObjectVTypeMap#visualizeKeyDistribution(int) - */ -class ObjectBufferVisualizer { - static String visualizeKeyDistribution(Object[] buffer, int max, int characters) { - final StringBuilder b = new StringBuilder(); - final char[] chars = ".123456789X".toCharArray(); - for (int i = 1, start = -1; i <= characters; i++) { - int end = (int) ((long) i * max / characters); - - if (start + 1 <= end) { - int taken = 0; - int slots = 0; - for (int slot = start + 1; slot <= end; slot++, slots++) { - if (!((buffer[slot]) == null)) { - taken++; - } - } - b.append(chars[Math.min(chars.length - 1, taken * chars.length / slots)]); - start = end; - } - } - while (b.length() < characters) { - b.append(' '); - } - return b.toString(); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectByteAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/ObjectByteAssociativeContainer.java deleted file mode 100755 index a5904161..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectByteAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see ObjectContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface ObjectByteAssociativeContainer extends Iterable> { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator> iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(KType key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectBytePredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ObjectByteProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public > T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ObjectBytePredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public > T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public ObjectCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public ByteContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectByteHashMap.java b/src/main/java/com/carrotsearch/hppc/ObjectByteHashMap.java deleted file mode 100755 index 4de468b8..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectByteHashMap.java +++ /dev/null @@ -1,1089 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of Object to byte, implemented using open addressing with - * linear probing for collision resolution. Supports null key. - * - * @see HPPC interfaces diagram - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class ObjectByteHashMap - implements ObjectByteMap, Preallocable, Cloneable, Accountable { - /** The array holding keys. */ - public Object[] keys; - - /** The array holding values. */ - public byte[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public ObjectByteHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectByteHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ObjectByteHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ObjectByteHashMap(ObjectByteAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public byte put(KType key, byte value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == null)) { - byte previousValue = hasEmptyKey ? values[mask + 1] : ((byte) 0); - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - final byte previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return ((byte) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(ObjectByteAssociativeContainer container) { - final int count = size(); - for (ObjectByteCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable> iterable) { - final int count = size(); - for (ObjectByteCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public byte putOrAdd(KType key, byte putValue, byte incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((byte) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public byte addTo(KType key, byte incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public byte remove(KType key) { - final int mask = this.mask; - if (((key) == null)) { - if (!hasEmptyKey) { - return ((byte) 0); - } - hasEmptyKey = false; - byte previousValue = values[mask + 1]; - values[mask + 1] = ((byte) 0); - return previousValue; - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - final byte previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return ((byte) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof ObjectLookupContainer) { - if (hasEmptyKey && other.contains(null)) { - hasEmptyKey = false; - values[mask + 1] = ((byte) 0); - } - - final KType[] keys = (KType[]) this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - KType existing; - if (!((existing = keys[slot]) == null) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (ObjectCursor c : other) { - remove((KType) c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectBytePredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(null, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = ((byte) 0); - } - } - - final KType[] keys = (KType[]) this.keys; - final byte[] values = this.values; - for (int slot = 0; slot <= mask; ) { - KType existing; - if (!((existing = keys[slot]) == null) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(null)) { - hasEmptyKey = false; - values[mask + 1] = ((byte) 0); - } - } - - final KType[] keys = (KType[]) this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - KType existing; - if (!((existing = keys[slot]) == null) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public byte get(KType key) { - if (((key) == null)) { - return hasEmptyKey ? values[mask + 1] : ((byte) 0); - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return ((byte) 0); - } - } - - /** {@inheritDoc} */ - @Override - public byte getOrDefault(KType key, byte defaultValue) { - if (((key) == null)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(KType key) { - if (((key) == null)) { - return hasEmptyKey; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(KType key) { - final int mask = this.mask; - if (((key) == null)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public byte indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public byte indexReplace(int index, byte newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - byte previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, KType key, byte value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == null)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == null); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public byte indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - byte previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = ((byte) 0); - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, null); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (ObjectByteCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** - * Return true if all keys of some other container exist in this container. Equality comparison is - * performed with this object's {@link #equals(Object, Object)} method. - */ - protected boolean equalElements(ObjectByteHashMap other) { - if (other.size() != size()) { - return false; - } - - for (ObjectByteCursor c : other) { - KType key = (KType) c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final KType[] prevKeys = (KType[]) this.keys; - final byte[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final ObjectByteCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new ObjectByteCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectByteCursor fetch() { - final int mask = ObjectByteHashMap.this.mask; - while (index <= mask) { - KType existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = (KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = null; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator> iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T procedure) { - final KType[] keys = (KType[]) this.keys; - final byte[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(null, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == null)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T predicate) { - final KType[] keys = (KType[]) this.keys; - final byte[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(null, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == null)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractObjectCollection - implements ObjectLookupContainer { - private final ObjectByteHashMap owner = ObjectByteHashMap.this; - - @Override - public boolean contains(KType e) { - return owner.containsKey(e); - } - - @Override - public > T forEach(final T procedure) { - owner.forEach((ObjectByteProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public > T forEach(final T predicate) { - owner.forEach((ObjectBytePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator> iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final KType e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator> { - private final ObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new ObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectCursor fetch() { - final int mask = ObjectByteHashMap.this.mask; - while (index <= mask) { - KType existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = (KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = null; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public ByteCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractByteCollection { - private final ObjectByteHashMap owner = ObjectByteHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(byte value) { - for (ObjectByteCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (ObjectByteCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (ObjectByteCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final byte e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final BytePredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ByteCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new ByteCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ByteCursor fetch() { - final int mask = ObjectByteHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!(((KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public ObjectByteHashMap clone() { - try { - - ObjectByteHashMap cloned = (ObjectByteHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (ObjectByteCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return ObjectBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ObjectByteHashMap from(KType[] keys, byte[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ObjectByteHashMap map = new ObjectByteHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(KType key) { - assert !((key) == null); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(KType[] fromKeys, byte[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final KType[] keys = (KType[]) this.keys; - final byte[] values = this.values; - final int mask = this.mask; - KType existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == null)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == null)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - KType[] prevKeys = (KType[]) this.keys; - byte[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = ((KType[]) new Object[arraySize + emptyElementSlot]); - this.values = (new byte[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, KType pendingKey, byte pendingValue) { - assert assigned == resizeAt && (((KType) keys[slot]) == null) && !((pendingKey) == null); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final KType[] prevKeys = (KType[]) this.keys; - final byte[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final KType[] keys = (KType[]) this.keys; - final byte[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final KType existing = keys[slot]; - if (((existing) == null)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = null; - values[gapSlot] = ((byte) 0); - assigned--; - } - - protected boolean equals(Object v1, Object v2) { - return (v1 == v2) || (v1 != null && v1.equals(v2)); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectByteIdentityHashMap.java b/src/main/java/com/carrotsearch/hppc/ObjectByteIdentityHashMap.java deleted file mode 100755 index f9567cdd..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectByteIdentityHashMap.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -/** An identity hash map of Object to byte. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeIdentityHashMap.java") -public class ObjectByteIdentityHashMap extends ObjectByteHashMap { - /** New instance with sane defaults. */ - public ObjectByteIdentityHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectByteIdentityHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ObjectByteIdentityHashMap(int expectedElements, double loadFactor) { - super(expectedElements, loadFactor); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ObjectByteIdentityHashMap(ObjectByteAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - @Override - public int hashKey(KType key) { - assert !((key) == null); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(System.identityHashCode(key)); - } - - @Override - public boolean equals(Object v1, Object v2) { - return v1 == v2; - } - - @SuppressWarnings("unchecked") - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ObjectByteIdentityHashMap from(KType[] keys, byte[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ObjectByteIdentityHashMap map = new ObjectByteIdentityHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectByteMap.java b/src/main/java/com/carrotsearch/hppc/ObjectByteMap.java deleted file mode 100755 index 9b4ac38a..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectByteMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ObjectByteCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface ObjectByteMap extends ObjectByteAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public byte get(KType key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public byte getOrDefault(KType key, byte defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public byte put(KType key, byte value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(KType key, byte value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(ObjectByteAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable> iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public byte putOrAdd(KType key, byte putValue, byte incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public byte addTo(KType key, byte additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public byte remove(KType key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link ObjectByteMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(KType key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public byte indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public byte indexReplace(int index, byte newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, KType key, byte value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public byte indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectCharAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/ObjectCharAssociativeContainer.java deleted file mode 100755 index fc06ba23..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectCharAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see ObjectContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface ObjectCharAssociativeContainer extends Iterable> { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator> iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(KType key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectCharPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ObjectCharProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public > T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ObjectCharPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public > T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public ObjectCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public CharContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectCharHashMap.java b/src/main/java/com/carrotsearch/hppc/ObjectCharHashMap.java deleted file mode 100755 index b71d892c..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectCharHashMap.java +++ /dev/null @@ -1,1089 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of Object to char, implemented using open addressing with - * linear probing for collision resolution. Supports null key. - * - * @see HPPC interfaces diagram - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class ObjectCharHashMap - implements ObjectCharMap, Preallocable, Cloneable, Accountable { - /** The array holding keys. */ - public Object[] keys; - - /** The array holding values. */ - public char[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public ObjectCharHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectCharHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ObjectCharHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ObjectCharHashMap(ObjectCharAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public char put(KType key, char value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == null)) { - char previousValue = hasEmptyKey ? values[mask + 1] : ((char) 0); - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - final char previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return ((char) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(ObjectCharAssociativeContainer container) { - final int count = size(); - for (ObjectCharCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable> iterable) { - final int count = size(); - for (ObjectCharCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public char putOrAdd(KType key, char putValue, char incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((char) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public char addTo(KType key, char incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public char remove(KType key) { - final int mask = this.mask; - if (((key) == null)) { - if (!hasEmptyKey) { - return ((char) 0); - } - hasEmptyKey = false; - char previousValue = values[mask + 1]; - values[mask + 1] = ((char) 0); - return previousValue; - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - final char previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return ((char) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof ObjectLookupContainer) { - if (hasEmptyKey && other.contains(null)) { - hasEmptyKey = false; - values[mask + 1] = ((char) 0); - } - - final KType[] keys = (KType[]) this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - KType existing; - if (!((existing = keys[slot]) == null) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (ObjectCursor c : other) { - remove((KType) c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectCharPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(null, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = ((char) 0); - } - } - - final KType[] keys = (KType[]) this.keys; - final char[] values = this.values; - for (int slot = 0; slot <= mask; ) { - KType existing; - if (!((existing = keys[slot]) == null) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(null)) { - hasEmptyKey = false; - values[mask + 1] = ((char) 0); - } - } - - final KType[] keys = (KType[]) this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - KType existing; - if (!((existing = keys[slot]) == null) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public char get(KType key) { - if (((key) == null)) { - return hasEmptyKey ? values[mask + 1] : ((char) 0); - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return ((char) 0); - } - } - - /** {@inheritDoc} */ - @Override - public char getOrDefault(KType key, char defaultValue) { - if (((key) == null)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(KType key) { - if (((key) == null)) { - return hasEmptyKey; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(KType key) { - final int mask = this.mask; - if (((key) == null)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public char indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public char indexReplace(int index, char newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - char previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, KType key, char value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == null)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == null); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public char indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - char previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = ((char) 0); - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, null); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (ObjectCharCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** - * Return true if all keys of some other container exist in this container. Equality comparison is - * performed with this object's {@link #equals(Object, Object)} method. - */ - protected boolean equalElements(ObjectCharHashMap other) { - if (other.size() != size()) { - return false; - } - - for (ObjectCharCursor c : other) { - KType key = (KType) c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final KType[] prevKeys = (KType[]) this.keys; - final char[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final ObjectCharCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new ObjectCharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectCharCursor fetch() { - final int mask = ObjectCharHashMap.this.mask; - while (index <= mask) { - KType existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = (KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = null; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator> iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T procedure) { - final KType[] keys = (KType[]) this.keys; - final char[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(null, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == null)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T predicate) { - final KType[] keys = (KType[]) this.keys; - final char[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(null, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == null)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractObjectCollection - implements ObjectLookupContainer { - private final ObjectCharHashMap owner = ObjectCharHashMap.this; - - @Override - public boolean contains(KType e) { - return owner.containsKey(e); - } - - @Override - public > T forEach(final T procedure) { - owner.forEach((ObjectCharProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public > T forEach(final T predicate) { - owner.forEach((ObjectCharPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator> iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final KType e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator> { - private final ObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new ObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectCursor fetch() { - final int mask = ObjectCharHashMap.this.mask; - while (index <= mask) { - KType existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = (KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = null; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public CharCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractCharCollection { - private final ObjectCharHashMap owner = ObjectCharHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(char value) { - for (ObjectCharCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (ObjectCharCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (ObjectCharCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final char e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final CharPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final CharCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new CharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharCursor fetch() { - final int mask = ObjectCharHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!(((KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public ObjectCharHashMap clone() { - try { - - ObjectCharHashMap cloned = (ObjectCharHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (ObjectCharCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return ObjectBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ObjectCharHashMap from(KType[] keys, char[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ObjectCharHashMap map = new ObjectCharHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(KType key) { - assert !((key) == null); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(KType[] fromKeys, char[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final KType[] keys = (KType[]) this.keys; - final char[] values = this.values; - final int mask = this.mask; - KType existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == null)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == null)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - KType[] prevKeys = (KType[]) this.keys; - char[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = ((KType[]) new Object[arraySize + emptyElementSlot]); - this.values = (new char[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, KType pendingKey, char pendingValue) { - assert assigned == resizeAt && (((KType) keys[slot]) == null) && !((pendingKey) == null); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final KType[] prevKeys = (KType[]) this.keys; - final char[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final KType[] keys = (KType[]) this.keys; - final char[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final KType existing = keys[slot]; - if (((existing) == null)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = null; - values[gapSlot] = ((char) 0); - assigned--; - } - - protected boolean equals(Object v1, Object v2) { - return (v1 == v2) || (v1 != null && v1.equals(v2)); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectCharIdentityHashMap.java b/src/main/java/com/carrotsearch/hppc/ObjectCharIdentityHashMap.java deleted file mode 100755 index d373e7f0..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectCharIdentityHashMap.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -/** An identity hash map of Object to char. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeIdentityHashMap.java") -public class ObjectCharIdentityHashMap extends ObjectCharHashMap { - /** New instance with sane defaults. */ - public ObjectCharIdentityHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectCharIdentityHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ObjectCharIdentityHashMap(int expectedElements, double loadFactor) { - super(expectedElements, loadFactor); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ObjectCharIdentityHashMap(ObjectCharAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - @Override - public int hashKey(KType key) { - assert !((key) == null); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(System.identityHashCode(key)); - } - - @Override - public boolean equals(Object v1, Object v2) { - return v1 == v2; - } - - @SuppressWarnings("unchecked") - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ObjectCharIdentityHashMap from(KType[] keys, char[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ObjectCharIdentityHashMap map = new ObjectCharIdentityHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectCharMap.java b/src/main/java/com/carrotsearch/hppc/ObjectCharMap.java deleted file mode 100755 index ab636b51..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectCharMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ObjectCharCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface ObjectCharMap extends ObjectCharAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public char get(KType key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public char getOrDefault(KType key, char defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public char put(KType key, char value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(KType key, char value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(ObjectCharAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable> iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public char putOrAdd(KType key, char putValue, char incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public char addTo(KType key, char additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public char remove(KType key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link ObjectCharMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(KType key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public char indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public char indexReplace(int index, char newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, KType key, char value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public char indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectCollection.java b/src/main/java/com/carrotsearch/hppc/ObjectCollection.java deleted file mode 100755 index 447e46de..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectCollection.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.predicates.ObjectPredicate; - -/** - * A collection allows basic, efficient operations on sets of elements (difference and - * intersection). - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeCollection.java") -public interface ObjectCollection extends ObjectContainer { - /** - * Removes all occurrences of e from this collection. - * - * @param e Element to be removed from this collection, if present. - * @return The number of removed elements as a result of this call. - */ - public int removeAll(KType e); - - /** - * Removes all elements in this collection that are present in c. - * - * @return Returns the number of removed elements. - */ - public int removeAll(ObjectLookupContainer c); - - /** - * Removes all elements in this collection for which the given predicate returns true - * . - * - * @return Returns the number of removed elements. - */ - public int removeAll(ObjectPredicate predicate); - - /** - * Keeps all elements in this collection that are present in c. Runs in time - * proportional to the number of elements in this collection. Equivalent of sets intersection. - * - * @return Returns the number of removed elements. - */ - public int retainAll(ObjectLookupContainer c); - - /** - * Keeps all elements in this collection for which the given predicate returns true. - * - * @return Returns the number of removed elements. - */ - public int retainAll(ObjectPredicate predicate); - - /** - * Removes all elements from this collection. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectContainer.java b/src/main/java/com/carrotsearch/hppc/ObjectContainer.java deleted file mode 100755 index 6e8cd488..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectContainer.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ObjectCursor; -import com.carrotsearch.hppc.predicates.ObjectPredicate; -import com.carrotsearch.hppc.procedures.ObjectProcedure; -import java.util.Iterator; - -/** A generic container holding Objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeContainer.java") -public interface ObjectContainer extends Iterable> { - /** - * Returns an iterator to a cursor traversing the collection. The order of traversal is not - * defined. More than one cursor may be active at a time. The behavior of iterators is undefined - * if structural changes are made to the underlying collection. - * - *

The iterator is implemented as a cursor and it returns the same cursor instance on - * every call to {@link Iterator#next()} (to avoid boxing of primitive types). To read the current - * list's value (or index in the list) use the cursor's public fields. An example is shown below. - * - *

-   * for (ObjectCursor<Object> c : container) {
-   *   System.out.println("index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator> iterator(); - - /** - * Lookup a given element in the container. This operation has no speed guarantees (may be linear - * with respect to the size of this container). - * - * @return Returns true if this container has an element equal to e. - */ - public boolean contains(KType e); - - /** - * Return the current number of elements in this container. The time for calculating the - * container's size may take O(n) time, although implementing classes should try to - * maintain the current size and return in constant time. - */ - public int size(); - - /** Shortcut for size() == 0. */ - public boolean isEmpty(); - - /** - * Copies all elements of this container to an array. - * - *

The returned array is always a copy, regardless of the storage used by the container. - */ - public Object[] toArray(); - - /** - * Copies all elements of this container to a dynamically created array of the given component - * type. - * - * @throws ArrayStoreException Thrown if this container's elements are not assignable to the - * array's component type. - */ - public T[] toArray(Class componentClass); - - /** - * Applies a procedure to all container elements. Returns the argument (any subclass - * of {@link ObjectProcedure}. This lets the caller to call methods of the argument by chaining - * the call (even if the argument is an anonymous type) to retrieve computed values, for example - * (IntContainer): - * - *

-   * int count = container.forEach(new IntProcedure() {
-   *   int count; // this is a field declaration in an anonymous class.
-   *
-   *   public void apply(int value) {
-   *     count++;
-   *   }
-   * }).count;
-   * 
- */ - public > T forEach(T procedure); - - /** - * Applies a predicate to container elements as long, as the predicate returns - * true. The iteration is interrupted otherwise. - */ - public > T forEach(T predicate); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectDeque.java b/src/main/java/com/carrotsearch/hppc/ObjectDeque.java deleted file mode 100755 index 2e6f62f3..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectDeque.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ObjectCursor; -import com.carrotsearch.hppc.predicates.ObjectPredicate; -import com.carrotsearch.hppc.procedures.ObjectProcedure; -import java.util.Deque; -import java.util.Iterator; - -/** - * A linear collection that supports element insertion and removal at both ends. - * - * @see Deque - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeDeque.java") -public interface ObjectDeque extends ObjectCollection { - /** - * Removes the first element that equals e. - * - * @return The deleted element's index or -1 if the element was not found. - */ - public int removeFirst(KType e); - - /** - * Removes the last element that equals e. - * - * @return The deleted element's index or -1 if the element was not found. - */ - public int removeLast(KType e); - - /** Inserts the specified element at the front of this deque. */ - public void addFirst(KType e); - - /** Inserts the specified element at the end of this deque. */ - public void addLast(KType e); - - /** - * Retrieves and removes the first element of this deque. - * - * @return the head (first) element of this deque. - */ - public KType removeFirst(); - - /** - * Retrieves and removes the last element of this deque. - * - * @return the tail of this deque. - */ - public KType removeLast(); - - /** - * Retrieves the first element of this deque but does not remove it. - * - * @return the head of this deque. - */ - public KType getFirst(); - - /** - * Retrieves the last element of this deque but does not remove it. - * - * @return the head of this deque. - */ - public KType getLast(); - - /** - * @return An iterator over elements in this deque in tail-to-head order. - */ - public Iterator> descendingIterator(); - - /** Applies a procedure to all elements in tail-to-head order. */ - public > T descendingForEach(T procedure); - - /** - * Applies a predicate to container elements as long, as the predicate returns - * true. The iteration is interrupted otherwise. - */ - public > T descendingForEach(T predicate); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectDoubleAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/ObjectDoubleAssociativeContainer.java deleted file mode 100755 index 0eb15e2a..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectDoubleAssociativeContainer.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see ObjectContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface ObjectDoubleAssociativeContainer - extends Iterable> { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *
-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator> iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(KType key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectDoublePredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ObjectDoubleProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public > T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ObjectDoublePredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public > T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public ObjectCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public DoubleContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectDoubleHashMap.java b/src/main/java/com/carrotsearch/hppc/ObjectDoubleHashMap.java deleted file mode 100755 index bf3240c7..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectDoubleHashMap.java +++ /dev/null @@ -1,1091 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of Object to double, implemented using open addressing with - * linear probing for collision resolution. Supports null key. - * - * @see HPPC interfaces diagram - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class ObjectDoubleHashMap - implements ObjectDoubleMap, Preallocable, Cloneable, Accountable { - /** The array holding keys. */ - public Object[] keys; - - /** The array holding values. */ - public double[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public ObjectDoubleHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectDoubleHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ObjectDoubleHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ObjectDoubleHashMap(ObjectDoubleAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public double put(KType key, double value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == null)) { - double previousValue = hasEmptyKey ? values[mask + 1] : 0d; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - final double previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0d; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(ObjectDoubleAssociativeContainer container) { - final int count = size(); - for (ObjectDoubleCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable> iterable) { - final int count = size(); - for (ObjectDoubleCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public double putOrAdd(KType key, double putValue, double incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((double) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public double addTo(KType key, double incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public double remove(KType key) { - final int mask = this.mask; - if (((key) == null)) { - if (!hasEmptyKey) { - return 0d; - } - hasEmptyKey = false; - double previousValue = values[mask + 1]; - values[mask + 1] = 0d; - return previousValue; - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - final double previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0d; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof ObjectLookupContainer) { - if (hasEmptyKey && other.contains(null)) { - hasEmptyKey = false; - values[mask + 1] = 0d; - } - - final KType[] keys = (KType[]) this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - KType existing; - if (!((existing = keys[slot]) == null) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (ObjectCursor c : other) { - remove((KType) c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectDoublePredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(null, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0d; - } - } - - final KType[] keys = (KType[]) this.keys; - final double[] values = this.values; - for (int slot = 0; slot <= mask; ) { - KType existing; - if (!((existing = keys[slot]) == null) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(null)) { - hasEmptyKey = false; - values[mask + 1] = 0d; - } - } - - final KType[] keys = (KType[]) this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - KType existing; - if (!((existing = keys[slot]) == null) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public double get(KType key) { - if (((key) == null)) { - return hasEmptyKey ? values[mask + 1] : 0d; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0d; - } - } - - /** {@inheritDoc} */ - @Override - public double getOrDefault(KType key, double defaultValue) { - if (((key) == null)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(KType key) { - if (((key) == null)) { - return hasEmptyKey; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(KType key) { - final int mask = this.mask; - if (((key) == null)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public double indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public double indexReplace(int index, double newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - double previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, KType key, double value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == null)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == null); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public double indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - double previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0d; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, null); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (ObjectDoubleCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** - * Return true if all keys of some other container exist in this container. Equality comparison is - * performed with this object's {@link #equals(Object, Object)} method. - */ - protected boolean equalElements(ObjectDoubleHashMap other) { - if (other.size() != size()) { - return false; - } - - for (ObjectDoubleCursor c : other) { - KType key = (KType) c.key; - if (!containsKey(key) - || !(Double.doubleToLongBits(c.value) == Double.doubleToLongBits(get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final KType[] prevKeys = (KType[]) this.keys; - final double[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final ObjectDoubleCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new ObjectDoubleCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectDoubleCursor fetch() { - final int mask = ObjectDoubleHashMap.this.mask; - while (index <= mask) { - KType existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = (KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = null; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator> iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T procedure) { - final KType[] keys = (KType[]) this.keys; - final double[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(null, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == null)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T predicate) { - final KType[] keys = (KType[]) this.keys; - final double[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(null, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == null)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractObjectCollection - implements ObjectLookupContainer { - private final ObjectDoubleHashMap owner = ObjectDoubleHashMap.this; - - @Override - public boolean contains(KType e) { - return owner.containsKey(e); - } - - @Override - public > T forEach(final T procedure) { - owner.forEach((ObjectDoubleProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public > T forEach(final T predicate) { - owner.forEach((ObjectDoublePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator> iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final KType e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator> { - private final ObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new ObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectCursor fetch() { - final int mask = ObjectDoubleHashMap.this.mask; - while (index <= mask) { - KType existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = (KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = null; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public DoubleCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractDoubleCollection { - private final ObjectDoubleHashMap owner = ObjectDoubleHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(double value) { - for (ObjectDoubleCursor c : owner) { - if ((Double.doubleToLongBits(value) == Double.doubleToLongBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (ObjectDoubleCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (ObjectDoubleCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final double e) { - return owner.removeAll( - (key, value) -> (Double.doubleToLongBits(e) == Double.doubleToLongBits(value))); - } - - @Override - public int removeAll(final DoublePredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final DoubleCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new DoubleCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected DoubleCursor fetch() { - final int mask = ObjectDoubleHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!(((KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public ObjectDoubleHashMap clone() { - try { - - ObjectDoubleHashMap cloned = (ObjectDoubleHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (ObjectDoubleCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return ObjectBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ObjectDoubleHashMap from(KType[] keys, double[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ObjectDoubleHashMap map = new ObjectDoubleHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(KType key) { - assert !((key) == null); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(KType[] fromKeys, double[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final KType[] keys = (KType[]) this.keys; - final double[] values = this.values; - final int mask = this.mask; - KType existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == null)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == null)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - KType[] prevKeys = (KType[]) this.keys; - double[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = ((KType[]) new Object[arraySize + emptyElementSlot]); - this.values = (new double[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, KType pendingKey, double pendingValue) { - assert assigned == resizeAt && (((KType) keys[slot]) == null) && !((pendingKey) == null); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final KType[] prevKeys = (KType[]) this.keys; - final double[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final KType[] keys = (KType[]) this.keys; - final double[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final KType existing = keys[slot]; - if (((existing) == null)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = null; - values[gapSlot] = 0d; - assigned--; - } - - protected boolean equals(Object v1, Object v2) { - return (v1 == v2) || (v1 != null && v1.equals(v2)); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectDoubleIdentityHashMap.java b/src/main/java/com/carrotsearch/hppc/ObjectDoubleIdentityHashMap.java deleted file mode 100755 index b034adf4..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectDoubleIdentityHashMap.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -/** An identity hash map of Object to double. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeIdentityHashMap.java") -public class ObjectDoubleIdentityHashMap extends ObjectDoubleHashMap { - /** New instance with sane defaults. */ - public ObjectDoubleIdentityHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectDoubleIdentityHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ObjectDoubleIdentityHashMap(int expectedElements, double loadFactor) { - super(expectedElements, loadFactor); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ObjectDoubleIdentityHashMap(ObjectDoubleAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - @Override - public int hashKey(KType key) { - assert !((key) == null); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(System.identityHashCode(key)); - } - - @Override - public boolean equals(Object v1, Object v2) { - return v1 == v2; - } - - @SuppressWarnings("unchecked") - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ObjectDoubleIdentityHashMap from(KType[] keys, double[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ObjectDoubleIdentityHashMap map = new ObjectDoubleIdentityHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectDoubleMap.java b/src/main/java/com/carrotsearch/hppc/ObjectDoubleMap.java deleted file mode 100755 index 595fc978..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectDoubleMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ObjectDoubleCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface ObjectDoubleMap extends ObjectDoubleAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public double get(KType key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public double getOrDefault(KType key, double defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public double put(KType key, double value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(KType key, double value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(ObjectDoubleAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable> iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public double putOrAdd(KType key, double putValue, double incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public double addTo(KType key, double additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public double remove(KType key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link ObjectDoubleMap} and both objects contains exactly the - * same key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(KType key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public double indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public double indexReplace(int index, double newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, KType key, double value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public double indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectFloatAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/ObjectFloatAssociativeContainer.java deleted file mode 100755 index a7448b25..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectFloatAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see ObjectContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface ObjectFloatAssociativeContainer extends Iterable> { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator> iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(KType key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectFloatPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ObjectFloatProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public > T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ObjectFloatPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public > T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public ObjectCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public FloatContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectFloatHashMap.java b/src/main/java/com/carrotsearch/hppc/ObjectFloatHashMap.java deleted file mode 100755 index 2dc4c98e..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectFloatHashMap.java +++ /dev/null @@ -1,1090 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of Object to float, implemented using open addressing with - * linear probing for collision resolution. Supports null key. - * - * @see HPPC interfaces diagram - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class ObjectFloatHashMap - implements ObjectFloatMap, Preallocable, Cloneable, Accountable { - /** The array holding keys. */ - public Object[] keys; - - /** The array holding values. */ - public float[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public ObjectFloatHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectFloatHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ObjectFloatHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ObjectFloatHashMap(ObjectFloatAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public float put(KType key, float value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == null)) { - float previousValue = hasEmptyKey ? values[mask + 1] : 0f; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - final float previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0f; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(ObjectFloatAssociativeContainer container) { - final int count = size(); - for (ObjectFloatCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable> iterable) { - final int count = size(); - for (ObjectFloatCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public float putOrAdd(KType key, float putValue, float incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((float) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public float addTo(KType key, float incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public float remove(KType key) { - final int mask = this.mask; - if (((key) == null)) { - if (!hasEmptyKey) { - return 0f; - } - hasEmptyKey = false; - float previousValue = values[mask + 1]; - values[mask + 1] = 0f; - return previousValue; - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - final float previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0f; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof ObjectLookupContainer) { - if (hasEmptyKey && other.contains(null)) { - hasEmptyKey = false; - values[mask + 1] = 0f; - } - - final KType[] keys = (KType[]) this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - KType existing; - if (!((existing = keys[slot]) == null) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (ObjectCursor c : other) { - remove((KType) c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectFloatPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(null, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0f; - } - } - - final KType[] keys = (KType[]) this.keys; - final float[] values = this.values; - for (int slot = 0; slot <= mask; ) { - KType existing; - if (!((existing = keys[slot]) == null) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(null)) { - hasEmptyKey = false; - values[mask + 1] = 0f; - } - } - - final KType[] keys = (KType[]) this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - KType existing; - if (!((existing = keys[slot]) == null) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public float get(KType key) { - if (((key) == null)) { - return hasEmptyKey ? values[mask + 1] : 0f; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0f; - } - } - - /** {@inheritDoc} */ - @Override - public float getOrDefault(KType key, float defaultValue) { - if (((key) == null)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(KType key) { - if (((key) == null)) { - return hasEmptyKey; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(KType key) { - final int mask = this.mask; - if (((key) == null)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public float indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public float indexReplace(int index, float newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - float previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, KType key, float value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == null)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == null); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public float indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - float previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0f; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, null); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (ObjectFloatCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** - * Return true if all keys of some other container exist in this container. Equality comparison is - * performed with this object's {@link #equals(Object, Object)} method. - */ - protected boolean equalElements(ObjectFloatHashMap other) { - if (other.size() != size()) { - return false; - } - - for (ObjectFloatCursor c : other) { - KType key = (KType) c.key; - if (!containsKey(key) || !(Float.floatToIntBits(c.value) == Float.floatToIntBits(get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final KType[] prevKeys = (KType[]) this.keys; - final float[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final ObjectFloatCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new ObjectFloatCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectFloatCursor fetch() { - final int mask = ObjectFloatHashMap.this.mask; - while (index <= mask) { - KType existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = (KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = null; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator> iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T procedure) { - final KType[] keys = (KType[]) this.keys; - final float[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(null, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == null)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T predicate) { - final KType[] keys = (KType[]) this.keys; - final float[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(null, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == null)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractObjectCollection - implements ObjectLookupContainer { - private final ObjectFloatHashMap owner = ObjectFloatHashMap.this; - - @Override - public boolean contains(KType e) { - return owner.containsKey(e); - } - - @Override - public > T forEach(final T procedure) { - owner.forEach((ObjectFloatProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public > T forEach(final T predicate) { - owner.forEach((ObjectFloatPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator> iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final KType e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator> { - private final ObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new ObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectCursor fetch() { - final int mask = ObjectFloatHashMap.this.mask; - while (index <= mask) { - KType existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = (KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = null; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public FloatCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractFloatCollection { - private final ObjectFloatHashMap owner = ObjectFloatHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(float value) { - for (ObjectFloatCursor c : owner) { - if ((Float.floatToIntBits(value) == Float.floatToIntBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (ObjectFloatCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (ObjectFloatCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final float e) { - return owner.removeAll( - (key, value) -> (Float.floatToIntBits(e) == Float.floatToIntBits(value))); - } - - @Override - public int removeAll(final FloatPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final FloatCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new FloatCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected FloatCursor fetch() { - final int mask = ObjectFloatHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!(((KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public ObjectFloatHashMap clone() { - try { - - ObjectFloatHashMap cloned = (ObjectFloatHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (ObjectFloatCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return ObjectBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ObjectFloatHashMap from(KType[] keys, float[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ObjectFloatHashMap map = new ObjectFloatHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(KType key) { - assert !((key) == null); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(KType[] fromKeys, float[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final KType[] keys = (KType[]) this.keys; - final float[] values = this.values; - final int mask = this.mask; - KType existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == null)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == null)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - KType[] prevKeys = (KType[]) this.keys; - float[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = ((KType[]) new Object[arraySize + emptyElementSlot]); - this.values = (new float[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, KType pendingKey, float pendingValue) { - assert assigned == resizeAt && (((KType) keys[slot]) == null) && !((pendingKey) == null); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final KType[] prevKeys = (KType[]) this.keys; - final float[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final KType[] keys = (KType[]) this.keys; - final float[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final KType existing = keys[slot]; - if (((existing) == null)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = null; - values[gapSlot] = 0f; - assigned--; - } - - protected boolean equals(Object v1, Object v2) { - return (v1 == v2) || (v1 != null && v1.equals(v2)); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectFloatIdentityHashMap.java b/src/main/java/com/carrotsearch/hppc/ObjectFloatIdentityHashMap.java deleted file mode 100755 index cfb63d0c..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectFloatIdentityHashMap.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -/** An identity hash map of Object to float. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeIdentityHashMap.java") -public class ObjectFloatIdentityHashMap extends ObjectFloatHashMap { - /** New instance with sane defaults. */ - public ObjectFloatIdentityHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectFloatIdentityHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ObjectFloatIdentityHashMap(int expectedElements, double loadFactor) { - super(expectedElements, loadFactor); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ObjectFloatIdentityHashMap(ObjectFloatAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - @Override - public int hashKey(KType key) { - assert !((key) == null); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(System.identityHashCode(key)); - } - - @Override - public boolean equals(Object v1, Object v2) { - return v1 == v2; - } - - @SuppressWarnings("unchecked") - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ObjectFloatIdentityHashMap from(KType[] keys, float[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ObjectFloatIdentityHashMap map = new ObjectFloatIdentityHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectFloatMap.java b/src/main/java/com/carrotsearch/hppc/ObjectFloatMap.java deleted file mode 100755 index 8feee948..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectFloatMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ObjectFloatCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface ObjectFloatMap extends ObjectFloatAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public float get(KType key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public float getOrDefault(KType key, float defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public float put(KType key, float value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(KType key, float value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(ObjectFloatAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable> iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public float putOrAdd(KType key, float putValue, float incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public float addTo(KType key, float additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public float remove(KType key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link ObjectFloatMap} and both objects contains exactly the - * same key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(KType key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public float indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public float indexReplace(int index, float newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, KType key, float value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public float indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectHashSet.java b/src/main/java/com/carrotsearch/hppc/ObjectHashSet.java deleted file mode 100755 index 1a37b23e..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectHashSet.java +++ /dev/null @@ -1,797 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash set of Objects, implemented using open addressing with linear probing for - * collision resolution. - * - * @see HPPC interfaces diagram - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypeHashSet.java") -public class ObjectHashSet extends AbstractObjectCollection - implements ObjectLookupContainer, - ObjectSet, - Preallocable, - Cloneable, - Accountable { - /** The hash array holding keys. */ - public Object[] keys; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any. - * - * @see #size() - * @see #hasEmptyKey - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** - * New instance with sane defaults. - * - * @see #ObjectHashSet(int, double) - */ - public ObjectHashSet() { - this(DEFAULT_EXPECTED_ELEMENTS, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with sane defaults. - * - * @see #ObjectHashSet(int, double) - */ - public ObjectHashSet(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ObjectHashSet(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** New instance copying elements from another {@link ObjectContainer}. */ - public ObjectHashSet(ObjectContainer container) { - this(container.size()); - addAll(container); - } - - /** {@inheritDoc} */ - @Override - public boolean add(KType key) { - if (((key) == null)) { - assert ((keys[mask + 1]) == null); - boolean added = !hasEmptyKey; - hasEmptyKey = true; - return added; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return false; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key); - } else { - keys[slot] = key; - } - - assigned++; - return true; - } - } - - /** - * Adds all elements from the given list (vararg) to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - */ - @SafeVarargs - public final int addAll(KType... elements) { - ensureCapacity(elements.length); - int count = 0; - for (KType e : elements) { - if (add(e)) { - count++; - } - } - return count; - } - - /** - * Adds all elements from the given {@link ObjectContainer} to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - */ - public int addAll(ObjectContainer container) { - ensureCapacity(container.size()); - return addAll((Iterable>) container); - } - - /** - * Adds all elements from the given iterable to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - */ - public int addAll(Iterable> iterable) { - int count = 0; - for (ObjectCursor cursor : iterable) { - if (add(cursor.value)) { - count++; - } - } - return count; - } - - /** {@inheritDoc} */ - @Override - public Object[] toArray() { - - final KType[] cloned = ((KType[]) new Object[size()]); - int j = 0; - if (hasEmptyKey) { - cloned[j++] = null; - } - - final KType[] keys = (KType[]) this.keys; - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - KType existing; - if (!((existing = keys[slot]) == null)) { - cloned[j++] = existing; - } - } - - return cloned; - } - - /** An alias for the (preferred) {@link #removeAll}. */ - public boolean remove(KType key) { - if (((key) == null)) { - boolean hadEmptyKey = hasEmptyKey; - hasEmptyKey = false; - return hadEmptyKey; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - shiftConflictingKeys(slot); - return true; - } - slot = (slot + 1) & mask; - } - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(KType key) { - return remove(key) ? 1 : 0; - } - - /** - * Removes all keys present in a given container. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectContainer other) { - final int before = size(); - - // Try to iterate over the smaller set or over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof ObjectLookupContainer) { - if (hasEmptyKey && other.contains(null)) { - hasEmptyKey = false; - } - - final KType[] keys = (KType[]) this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - KType existing; - if (!((existing = keys[slot]) == null) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (ObjectCursor c : other) { - remove((KType) c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectPredicate predicate) { - int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(null)) { - hasEmptyKey = false; - } - } - - final KType[] keys = (KType[]) this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - KType existing; - if (!((existing = keys[slot]) == null)) { - if (predicate.apply(existing)) { - shiftConflictingKeys(slot); - continue; // Repeat the check for the same slot i (shifted). - } - } - slot++; - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public boolean contains(KType key) { - if (((key) == null)) { - return hasEmptyKey; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return true; - } - slot = (slot + 1) & mask; - } - return false; - } - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - Arrays.fill(keys, null); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - keys = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return size() == 0; - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final KType[] prevKeys = (KType[]) this.keys; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys); - } - } - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - final KType[] keys = (KType[]) this.keys; - for (int slot = mask; slot >= 0; slot--) { - KType existing; - if (!((existing = keys[slot]) == null)) { - h += BitMixer.mix(existing); - } - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && sameKeys(getClass().cast(obj))); - } - - /** - * Return true if all keys of some other container exist in this container. Equality comparison is - * performed with this object's {@link #equals(Object, Object)} method. - */ - private boolean sameKeys(ObjectSet other) { - if (other.size() != size()) { - return false; - } - - for (ObjectCursor c : other) { - if (!contains((KType) c.value)) { - return false; - } - } - - return true; - } - - /** {@inheritDoc} */ - @Override - public ObjectHashSet clone() { - try { - - ObjectHashSet cloned = (ObjectHashSet) super.clone(); - cloned.keys = keys.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator> iterator() { - return new EntryIterator(); - } - - @Override - public long ramBytesAllocated() { - // int: assigned, mask, keyMixer, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys); - } - - @Override - public long ramBytesUsed() { - // int: assigned, mask, keyMixer, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - protected final class EntryIterator extends AbstractIterator> { - private final ObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new ObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectCursor fetch() { - final int mask = ObjectHashSet.this.mask; - while (index <= mask) { - KType existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = (KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = null; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T procedure) { - if (hasEmptyKey) { - procedure.apply(null); - } - - final KType[] keys = (KType[]) this.keys; - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - KType existing; - if (!((existing = keys[slot]) == null)) { - procedure.apply(existing); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T predicate) { - if (hasEmptyKey) { - if (!predicate.apply(null)) { - return predicate; - } - } - - final KType[] keys = (KType[]) this.keys; - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - KType existing; - if (!((existing = keys[slot]) == null)) { - if (!predicate.apply(existing)) { - break; - } - } - } - - return predicate; - } - - /** - * Create a set from a variable number of arguments or an array of Object. The - * elements are copied from the argument to the internal buffer. - */ - @SafeVarargs - public static ObjectHashSet from(KType... elements) { - final ObjectHashSet set = new ObjectHashSet(elements.length); - set.addAll(elements); - return set; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(KType key) { - assert !((key) == null); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up logic in - * certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between modifications (it will not be affected by read-only - * operations). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the set. - * @return A non-negative value of the logical "index" of the key in the set or a negative value - * if the key did not exist. - */ - public int indexOf(KType key) { - final int mask = this.mask; - if (((key) == null)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index) { - assert index < 0 || index <= mask || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** - * Returns the exact value of the existing key. This method makes sense for sets of objects which - * define custom key-equality relationship. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the equivalent key currently stored in the set. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public KType indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return (KType) keys[index]; - } - - /** - * Replaces the existing equivalent key with the given one and returns any previous value stored - * for that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @param equivalentKey The key to put in the set as a replacement. Must be equivalent to the key - * currently stored at the provided index. - * @return Returns the previous key stored in the set. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public KType indexReplace(int index, KType equivalentKey) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - assert this.equals(keys[index], equivalentKey); - - KType previousValue = (KType) keys[index]; - keys[index] = equivalentKey; - return previousValue; - } - - /** - * Inserts a key for an index that is not present in the set. This method may help in avoiding - * double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public void indexInsert(int index, KType key) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == null)) { - assert index == mask + 1; - assert ((keys[index]) == null); - hasEmptyKey = true; - } else { - assert ((keys[index]) == null); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key); - } else { - keys[index] = key; - } - - assigned++; - } - } - - /** - * Removes a key at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public void indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - if (index > mask) { - hasEmptyKey = false; - } else { - shiftConflictingKeys(index); - } - } - - @Override - public String visualizeKeyDistribution(int characters) { - return ObjectBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(KType[] fromKeys) { - assert HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored keys into the new buffers. - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - KType existing; - for (int i = fromKeys.length - 1; --i >= 0; ) { - if (!((existing = fromKeys[i]) == null)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == null)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - KType[] prevKeys = (KType[]) this.keys; - try { - int emptyElementSlot = 1; - this.keys = ((KType[]) new Object[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.keys == null ? 0 : size(), arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key to be inserted into the buffer but there is not - * enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, KType pendingKey) { - assert assigned == resizeAt && (((KType) keys[slot]) == null) && !((pendingKey) == null); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final KType[] prevKeys = (KType[]) this.keys; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - - // Rehash old keys, including the pending key. - rehash(prevKeys); - } - - /** Shift all the slot-conflicting keys allocated to (and including) slot. */ - protected void shiftConflictingKeys(int gapSlot) { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final KType existing = keys[slot]; - if (((existing) == null)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = null; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectIdentityHashSet.java b/src/main/java/com/carrotsearch/hppc/ObjectIdentityHashSet.java deleted file mode 100755 index 4efb528d..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectIdentityHashSet.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -/** A reference-equality (identity) hash set. */ -public class ObjectIdentityHashSet extends ObjectHashSet { - /** New instance with sane defaults. */ - public ObjectIdentityHashSet() { - this(DEFAULT_EXPECTED_ELEMENTS, DEFAULT_LOAD_FACTOR); - } - - /** New instance with sane defaults. */ - public ObjectIdentityHashSet(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ObjectIdentityHashSet(int expectedElements, double loadFactor) { - super(expectedElements, loadFactor); - } - - /** New instance copying elements from another {@link ObjectContainer}. */ - public ObjectIdentityHashSet(ObjectContainer container) { - this(container.size()); - addAll(container); - } - - @Override - protected int hashKey(KType key) { - assert key != null; // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(System.identityHashCode(key)); - } - - @Override - protected boolean equals(Object v1, Object v2) { - return v1 == v2; - } - - /** - * Create a set from a variable number of arguments or an array of KType. The - * elements are copied from the argument to the internal buffer. - */ - @SafeVarargs - public static ObjectIdentityHashSet from(KType... elements) { - final ObjectIdentityHashSet set = new ObjectIdentityHashSet(elements.length); - set.addAll(elements); - return set; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectIndexedContainer.java b/src/main/java/com/carrotsearch/hppc/ObjectIndexedContainer.java deleted file mode 100755 index 37ce1fc4..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectIndexedContainer.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.carrotsearch.hppc; - -import java.util.RandomAccess; -import java.util.stream.Stream; - -/** - * An indexed container provides random access to elements based on an index. Indexes - * are zero-based. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeIndexedContainer.java") -public interface ObjectIndexedContainer extends ObjectCollection, RandomAccess { - /** - * Removes the first element that equals e1, returning whether an element has been - * removed. - */ - public boolean removeElement(KType e1); - - /** - * Removes the first element that equals e1, returning its deleted position or - * -1 if the element was not found. - */ - public int removeFirst(KType e1); - - /** - * Removes the last element that equals e1, returning its deleted position or - * -1 if the element was not found. - */ - public int removeLast(KType e1); - - /** - * Returns the index of the first occurrence of the specified element in this list, or -1 if this - * list does not contain the element. - */ - public int indexOf(KType e1); - - /** - * Returns the index of the last occurrence of the specified element in this list, or -1 if this - * list does not contain the element. - */ - public int lastIndexOf(KType e1); - - /** Adds an element to the end of this container (the last index is incremented by one). */ - public void add(KType e1); - - /** - * Inserts the specified element at the specified position in this list. - * - * @param index The index at which the element should be inserted, shifting any existing and - * subsequent elements to the right. - */ - public void insert(int index, KType e1); - - /** - * Replaces the element at the specified position in this list with the specified element. - * - * @return Returns the previous value in the list. - */ - public KType set(int index, KType e1); - - /** - * @return Returns the element at index index from the list. - */ - public KType get(int index); - - /** - * Removes the element at the specified position in this container and returns it. - * - * @see #removeFirst - * @see #removeLast - * @see #removeAll - */ - public KType removeAt(int index); - - /** Removes and returns the last element of this container. This container must not be empty. */ - public KType removeLast(); - - /** - * Removes from this container all of the elements with indexes between fromIndex, - * inclusive, and toIndex, exclusive. - */ - public void removeRange(int fromIndex, int toIndex); - - /** Returns this container elements as a stream. */ - public Stream stream(); - - /** Sorts the elements in this container and returns this container. */ - public ObjectIndexedContainer sort(); - - /** Reverses the elements in this container and returns this container. */ - public ObjectIndexedContainer reverse(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectIntAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/ObjectIntAssociativeContainer.java deleted file mode 100755 index 13f68440..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectIntAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see ObjectContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface ObjectIntAssociativeContainer extends Iterable> { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator> iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(KType key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectIntPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ObjectIntProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public > T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ObjectIntPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public > T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public ObjectCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public IntContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectIntHashMap.java b/src/main/java/com/carrotsearch/hppc/ObjectIntHashMap.java deleted file mode 100755 index 47e49998..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectIntHashMap.java +++ /dev/null @@ -1,1089 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of Object to int, implemented using open addressing with - * linear probing for collision resolution. Supports null key. - * - * @see HPPC interfaces diagram - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class ObjectIntHashMap - implements ObjectIntMap, Preallocable, Cloneable, Accountable { - /** The array holding keys. */ - public Object[] keys; - - /** The array holding values. */ - public int[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public ObjectIntHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectIntHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ObjectIntHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ObjectIntHashMap(ObjectIntAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public int put(KType key, int value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == null)) { - int previousValue = hasEmptyKey ? values[mask + 1] : 0; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - final int previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(ObjectIntAssociativeContainer container) { - final int count = size(); - for (ObjectIntCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable> iterable) { - final int count = size(); - for (ObjectIntCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public int putOrAdd(KType key, int putValue, int incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((int) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public int addTo(KType key, int incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public int remove(KType key) { - final int mask = this.mask; - if (((key) == null)) { - if (!hasEmptyKey) { - return 0; - } - hasEmptyKey = false; - int previousValue = values[mask + 1]; - values[mask + 1] = 0; - return previousValue; - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - final int previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof ObjectLookupContainer) { - if (hasEmptyKey && other.contains(null)) { - hasEmptyKey = false; - values[mask + 1] = 0; - } - - final KType[] keys = (KType[]) this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - KType existing; - if (!((existing = keys[slot]) == null) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (ObjectCursor c : other) { - remove((KType) c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectIntPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(null, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0; - } - } - - final KType[] keys = (KType[]) this.keys; - final int[] values = this.values; - for (int slot = 0; slot <= mask; ) { - KType existing; - if (!((existing = keys[slot]) == null) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(null)) { - hasEmptyKey = false; - values[mask + 1] = 0; - } - } - - final KType[] keys = (KType[]) this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - KType existing; - if (!((existing = keys[slot]) == null) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int get(KType key) { - if (((key) == null)) { - return hasEmptyKey ? values[mask + 1] : 0; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0; - } - } - - /** {@inheritDoc} */ - @Override - public int getOrDefault(KType key, int defaultValue) { - if (((key) == null)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(KType key) { - if (((key) == null)) { - return hasEmptyKey; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(KType key) { - final int mask = this.mask; - if (((key) == null)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public int indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public int indexReplace(int index, int newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - int previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, KType key, int value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == null)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == null); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public int indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - int previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, null); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (ObjectIntCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** - * Return true if all keys of some other container exist in this container. Equality comparison is - * performed with this object's {@link #equals(Object, Object)} method. - */ - protected boolean equalElements(ObjectIntHashMap other) { - if (other.size() != size()) { - return false; - } - - for (ObjectIntCursor c : other) { - KType key = (KType) c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final KType[] prevKeys = (KType[]) this.keys; - final int[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final ObjectIntCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new ObjectIntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectIntCursor fetch() { - final int mask = ObjectIntHashMap.this.mask; - while (index <= mask) { - KType existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = (KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = null; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator> iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T procedure) { - final KType[] keys = (KType[]) this.keys; - final int[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(null, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == null)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T predicate) { - final KType[] keys = (KType[]) this.keys; - final int[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(null, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == null)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractObjectCollection - implements ObjectLookupContainer { - private final ObjectIntHashMap owner = ObjectIntHashMap.this; - - @Override - public boolean contains(KType e) { - return owner.containsKey(e); - } - - @Override - public > T forEach(final T procedure) { - owner.forEach((ObjectIntProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public > T forEach(final T predicate) { - owner.forEach((ObjectIntPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator> iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final KType e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator> { - private final ObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new ObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectCursor fetch() { - final int mask = ObjectIntHashMap.this.mask; - while (index <= mask) { - KType existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = (KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = null; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public IntCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractIntCollection { - private final ObjectIntHashMap owner = ObjectIntHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(int value) { - for (ObjectIntCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (ObjectIntCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (ObjectIntCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final int e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final IntPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final IntCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new IntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntCursor fetch() { - final int mask = ObjectIntHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!(((KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public ObjectIntHashMap clone() { - try { - - ObjectIntHashMap cloned = (ObjectIntHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (ObjectIntCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return ObjectBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ObjectIntHashMap from(KType[] keys, int[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ObjectIntHashMap map = new ObjectIntHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(KType key) { - assert !((key) == null); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(KType[] fromKeys, int[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final KType[] keys = (KType[]) this.keys; - final int[] values = this.values; - final int mask = this.mask; - KType existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == null)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == null)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - KType[] prevKeys = (KType[]) this.keys; - int[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = ((KType[]) new Object[arraySize + emptyElementSlot]); - this.values = (new int[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, KType pendingKey, int pendingValue) { - assert assigned == resizeAt && (((KType) keys[slot]) == null) && !((pendingKey) == null); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final KType[] prevKeys = (KType[]) this.keys; - final int[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final KType[] keys = (KType[]) this.keys; - final int[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final KType existing = keys[slot]; - if (((existing) == null)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = null; - values[gapSlot] = 0; - assigned--; - } - - protected boolean equals(Object v1, Object v2) { - return (v1 == v2) || (v1 != null && v1.equals(v2)); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectIntIdentityHashMap.java b/src/main/java/com/carrotsearch/hppc/ObjectIntIdentityHashMap.java deleted file mode 100755 index b471a1a7..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectIntIdentityHashMap.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -/** An identity hash map of Object to int. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeIdentityHashMap.java") -public class ObjectIntIdentityHashMap extends ObjectIntHashMap { - /** New instance with sane defaults. */ - public ObjectIntIdentityHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectIntIdentityHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ObjectIntIdentityHashMap(int expectedElements, double loadFactor) { - super(expectedElements, loadFactor); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ObjectIntIdentityHashMap(ObjectIntAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - @Override - public int hashKey(KType key) { - assert !((key) == null); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(System.identityHashCode(key)); - } - - @Override - public boolean equals(Object v1, Object v2) { - return v1 == v2; - } - - @SuppressWarnings("unchecked") - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ObjectIntIdentityHashMap from(KType[] keys, int[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ObjectIntIdentityHashMap map = new ObjectIntIdentityHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectIntMap.java b/src/main/java/com/carrotsearch/hppc/ObjectIntMap.java deleted file mode 100755 index 5f87f0f6..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectIntMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ObjectIntCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface ObjectIntMap extends ObjectIntAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public int get(KType key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public int getOrDefault(KType key, int defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public int put(KType key, int value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(KType key, int value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(ObjectIntAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable> iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public int putOrAdd(KType key, int putValue, int incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public int addTo(KType key, int additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public int remove(KType key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link ObjectIntMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(KType key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public int indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public int indexReplace(int index, int newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, KType key, int value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public int indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectLongAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/ObjectLongAssociativeContainer.java deleted file mode 100755 index 3d78e376..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectLongAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see ObjectContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface ObjectLongAssociativeContainer extends Iterable> { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator> iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(KType key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectLongPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ObjectLongProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public > T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ObjectLongPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public > T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public ObjectCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public LongContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectLongHashMap.java b/src/main/java/com/carrotsearch/hppc/ObjectLongHashMap.java deleted file mode 100755 index 1688a68d..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectLongHashMap.java +++ /dev/null @@ -1,1089 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of Object to long, implemented using open addressing with - * linear probing for collision resolution. Supports null key. - * - * @see HPPC interfaces diagram - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class ObjectLongHashMap - implements ObjectLongMap, Preallocable, Cloneable, Accountable { - /** The array holding keys. */ - public Object[] keys; - - /** The array holding values. */ - public long[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public ObjectLongHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectLongHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ObjectLongHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ObjectLongHashMap(ObjectLongAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public long put(KType key, long value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == null)) { - long previousValue = hasEmptyKey ? values[mask + 1] : 0L; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - final long previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0L; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(ObjectLongAssociativeContainer container) { - final int count = size(); - for (ObjectLongCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable> iterable) { - final int count = size(); - for (ObjectLongCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public long putOrAdd(KType key, long putValue, long incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((long) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public long addTo(KType key, long incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public long remove(KType key) { - final int mask = this.mask; - if (((key) == null)) { - if (!hasEmptyKey) { - return 0L; - } - hasEmptyKey = false; - long previousValue = values[mask + 1]; - values[mask + 1] = 0L; - return previousValue; - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - final long previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0L; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof ObjectLookupContainer) { - if (hasEmptyKey && other.contains(null)) { - hasEmptyKey = false; - values[mask + 1] = 0L; - } - - final KType[] keys = (KType[]) this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - KType existing; - if (!((existing = keys[slot]) == null) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (ObjectCursor c : other) { - remove((KType) c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectLongPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(null, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0L; - } - } - - final KType[] keys = (KType[]) this.keys; - final long[] values = this.values; - for (int slot = 0; slot <= mask; ) { - KType existing; - if (!((existing = keys[slot]) == null) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(null)) { - hasEmptyKey = false; - values[mask + 1] = 0L; - } - } - - final KType[] keys = (KType[]) this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - KType existing; - if (!((existing = keys[slot]) == null) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public long get(KType key) { - if (((key) == null)) { - return hasEmptyKey ? values[mask + 1] : 0L; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0L; - } - } - - /** {@inheritDoc} */ - @Override - public long getOrDefault(KType key, long defaultValue) { - if (((key) == null)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(KType key) { - if (((key) == null)) { - return hasEmptyKey; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(KType key) { - final int mask = this.mask; - if (((key) == null)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public long indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public long indexReplace(int index, long newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - long previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, KType key, long value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == null)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == null); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public long indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - long previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0L; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, null); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (ObjectLongCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** - * Return true if all keys of some other container exist in this container. Equality comparison is - * performed with this object's {@link #equals(Object, Object)} method. - */ - protected boolean equalElements(ObjectLongHashMap other) { - if (other.size() != size()) { - return false; - } - - for (ObjectLongCursor c : other) { - KType key = (KType) c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final KType[] prevKeys = (KType[]) this.keys; - final long[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final ObjectLongCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new ObjectLongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectLongCursor fetch() { - final int mask = ObjectLongHashMap.this.mask; - while (index <= mask) { - KType existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = (KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = null; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator> iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T procedure) { - final KType[] keys = (KType[]) this.keys; - final long[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(null, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == null)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T predicate) { - final KType[] keys = (KType[]) this.keys; - final long[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(null, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == null)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractObjectCollection - implements ObjectLookupContainer { - private final ObjectLongHashMap owner = ObjectLongHashMap.this; - - @Override - public boolean contains(KType e) { - return owner.containsKey(e); - } - - @Override - public > T forEach(final T procedure) { - owner.forEach((ObjectLongProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public > T forEach(final T predicate) { - owner.forEach((ObjectLongPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator> iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final KType e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator> { - private final ObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new ObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectCursor fetch() { - final int mask = ObjectLongHashMap.this.mask; - while (index <= mask) { - KType existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = (KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = null; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public LongCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractLongCollection { - private final ObjectLongHashMap owner = ObjectLongHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(long value) { - for (ObjectLongCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (ObjectLongCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (ObjectLongCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final long e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final LongPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final LongCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new LongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongCursor fetch() { - final int mask = ObjectLongHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!(((KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public ObjectLongHashMap clone() { - try { - - ObjectLongHashMap cloned = (ObjectLongHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (ObjectLongCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return ObjectBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ObjectLongHashMap from(KType[] keys, long[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ObjectLongHashMap map = new ObjectLongHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(KType key) { - assert !((key) == null); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(KType[] fromKeys, long[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final KType[] keys = (KType[]) this.keys; - final long[] values = this.values; - final int mask = this.mask; - KType existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == null)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == null)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - KType[] prevKeys = (KType[]) this.keys; - long[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = ((KType[]) new Object[arraySize + emptyElementSlot]); - this.values = (new long[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, KType pendingKey, long pendingValue) { - assert assigned == resizeAt && (((KType) keys[slot]) == null) && !((pendingKey) == null); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final KType[] prevKeys = (KType[]) this.keys; - final long[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final KType[] keys = (KType[]) this.keys; - final long[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final KType existing = keys[slot]; - if (((existing) == null)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = null; - values[gapSlot] = 0L; - assigned--; - } - - protected boolean equals(Object v1, Object v2) { - return (v1 == v2) || (v1 != null && v1.equals(v2)); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectLongIdentityHashMap.java b/src/main/java/com/carrotsearch/hppc/ObjectLongIdentityHashMap.java deleted file mode 100755 index 886d6742..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectLongIdentityHashMap.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -/** An identity hash map of Object to long. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeIdentityHashMap.java") -public class ObjectLongIdentityHashMap extends ObjectLongHashMap { - /** New instance with sane defaults. */ - public ObjectLongIdentityHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectLongIdentityHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ObjectLongIdentityHashMap(int expectedElements, double loadFactor) { - super(expectedElements, loadFactor); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ObjectLongIdentityHashMap(ObjectLongAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - @Override - public int hashKey(KType key) { - assert !((key) == null); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(System.identityHashCode(key)); - } - - @Override - public boolean equals(Object v1, Object v2) { - return v1 == v2; - } - - @SuppressWarnings("unchecked") - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ObjectLongIdentityHashMap from(KType[] keys, long[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ObjectLongIdentityHashMap map = new ObjectLongIdentityHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectLongMap.java b/src/main/java/com/carrotsearch/hppc/ObjectLongMap.java deleted file mode 100755 index 4cab9639..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectLongMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ObjectLongCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface ObjectLongMap extends ObjectLongAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public long get(KType key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public long getOrDefault(KType key, long defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public long put(KType key, long value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(KType key, long value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(ObjectLongAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable> iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public long putOrAdd(KType key, long putValue, long incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public long addTo(KType key, long additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public long remove(KType key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link ObjectLongMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(KType key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public long indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public long indexReplace(int index, long newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, KType key, long value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public long indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectLookupContainer.java b/src/main/java/com/carrotsearch/hppc/ObjectLookupContainer.java deleted file mode 100755 index ee0982c0..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectLookupContainer.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.carrotsearch.hppc; - -/** - * Marker interface for containers that can check if they contain a given object in at least time - * O(log n) and ideally in amortized constant time O(1). - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeLookupContainer.java") -public interface ObjectLookupContainer extends ObjectContainer { - public boolean contains(KType e); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectObjectAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/ObjectObjectAssociativeContainer.java deleted file mode 100755 index 551388ef..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectObjectAssociativeContainer.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see ObjectContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface ObjectObjectAssociativeContainer - extends Iterable> { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator> iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(KType key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectObjectPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ObjectObjectProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public > T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ObjectObjectPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public > T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public ObjectCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public ObjectContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectObjectHashMap.java b/src/main/java/com/carrotsearch/hppc/ObjectObjectHashMap.java deleted file mode 100755 index 855027cd..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectObjectHashMap.java +++ /dev/null @@ -1,1059 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of Object to Object, implemented using open addressing with - * linear probing for collision resolution. Supports null key. Supports null values. - * - * @see HPPC interfaces diagram - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class ObjectObjectHashMap - implements ObjectObjectMap, Preallocable, Cloneable, Accountable { - /** The array holding keys. */ - public Object[] keys; - - /** The array holding values. */ - public Object[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public ObjectObjectHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectObjectHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ObjectObjectHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ObjectObjectHashMap( - ObjectObjectAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public VType put(KType key, VType value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == null)) { - VType previousValue = hasEmptyKey ? (VType) values[mask + 1] : null; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - final VType previousValue = (VType) values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return null; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(ObjectObjectAssociativeContainer container) { - final int count = size(); - for (ObjectObjectCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll( - Iterable> iterable) { - final int count = size(); - for (ObjectObjectCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** {@inheritDoc} */ - @Override - public VType remove(KType key) { - final int mask = this.mask; - if (((key) == null)) { - if (!hasEmptyKey) { - return null; - } - hasEmptyKey = false; - VType previousValue = (VType) values[mask + 1]; - values[mask + 1] = null; - return previousValue; - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - final VType previousValue = (VType) values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return null; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof ObjectLookupContainer) { - if (hasEmptyKey && other.contains(null)) { - hasEmptyKey = false; - values[mask + 1] = null; - } - - final KType[] keys = (KType[]) this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - KType existing; - if (!((existing = keys[slot]) == null) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (ObjectCursor c : other) { - remove((KType) c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectObjectPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(null, (VType) values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = null; - } - } - - final KType[] keys = (KType[]) this.keys; - final VType[] values = (VType[]) this.values; - for (int slot = 0; slot <= mask; ) { - KType existing; - if (!((existing = keys[slot]) == null) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(null)) { - hasEmptyKey = false; - values[mask + 1] = null; - } - } - - final KType[] keys = (KType[]) this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - KType existing; - if (!((existing = keys[slot]) == null) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public VType get(KType key) { - if (((key) == null)) { - return hasEmptyKey ? (VType) values[mask + 1] : null; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return (VType) values[slot]; - } - slot = (slot + 1) & mask; - } - - return null; - } - } - - /** {@inheritDoc} */ - @Override - public VType getOrDefault(KType key, VType defaultValue) { - if (((key) == null)) { - return hasEmptyKey ? (VType) values[mask + 1] : defaultValue; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return (VType) values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(KType key) { - if (((key) == null)) { - return hasEmptyKey; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(KType key) { - final int mask = this.mask; - if (((key) == null)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public VType indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return (VType) values[index]; - } - - /** {@inheritDoc} */ - @Override - public VType indexReplace(int index, VType newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - VType previousValue = (VType) values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, KType key, VType value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == null)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == null); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public VType indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - VType previousValue = (VType) values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = null; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, null); - - Arrays.fill(values, null); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (ObjectObjectCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** - * Return true if all keys of some other container exist in this container. Equality comparison is - * performed with this object's {@link #equals(Object, Object)} method. Values are compared using - * {@link Objects#equals(Object)} method. - */ - protected boolean equalElements(ObjectObjectHashMap other) { - if (other.size() != size()) { - return false; - } - - for (ObjectObjectCursor c : other) { - KType key = (KType) c.key; - if (!containsKey(key) || !java.util.Objects.equals(c.value, get(key))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final KType[] prevKeys = (KType[]) this.keys; - final VType[] prevValues = (VType[]) this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final ObjectObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new ObjectObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectObjectCursor fetch() { - final int mask = ObjectObjectHashMap.this.mask; - while (index <= mask) { - KType existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = (KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = (VType) values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = null; - cursor.value = (VType) values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator> iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T procedure) { - final KType[] keys = (KType[]) this.keys; - final VType[] values = (VType[]) this.values; - - if (hasEmptyKey) { - procedure.apply(null, (VType) values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == null)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T predicate) { - final KType[] keys = (KType[]) this.keys; - final VType[] values = (VType[]) this.values; - - if (hasEmptyKey) { - if (!predicate.apply(null, (VType) values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == null)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractObjectCollection - implements ObjectLookupContainer { - private final ObjectObjectHashMap owner = ObjectObjectHashMap.this; - - @Override - public boolean contains(KType e) { - return owner.containsKey(e); - } - - @Override - public > T forEach(final T procedure) { - owner.forEach((ObjectObjectProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public > T forEach(final T predicate) { - owner.forEach((ObjectObjectPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator> iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final KType e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator> { - private final ObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new ObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectCursor fetch() { - final int mask = ObjectObjectHashMap.this.mask; - while (index <= mask) { - KType existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = (KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = null; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public ObjectCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractObjectCollection { - private final ObjectObjectHashMap owner = ObjectObjectHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(VType value) { - for (ObjectObjectCursor c : owner) { - if (java.util.Objects.equals(value, c.value)) { - return true; - } - } - return false; - } - - @Override - public > T forEach(T procedure) { - for (ObjectObjectCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public > T forEach(T predicate) { - for (ObjectObjectCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator> iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final VType e) { - return owner.removeAll((key, value) -> java.util.Objects.equals(e, value)); - } - - @Override - public int removeAll(final ObjectPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator> { - private final ObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new ObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectCursor fetch() { - final int mask = ObjectObjectHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!(((KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.value = (VType) values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = (VType) values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public ObjectObjectHashMap clone() { - try { - - ObjectObjectHashMap cloned = (ObjectObjectHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (ObjectObjectCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return ObjectBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ObjectObjectHashMap from( - KType[] keys, VType[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ObjectObjectHashMap map = new ObjectObjectHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(KType key) { - assert !((key) == null); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(KType[] fromKeys, VType[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final KType[] keys = (KType[]) this.keys; - final VType[] values = (VType[]) this.values; - final int mask = this.mask; - KType existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == null)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == null)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - KType[] prevKeys = (KType[]) this.keys; - VType[] prevValues = (VType[]) this.values; - try { - int emptyElementSlot = 1; - this.keys = ((KType[]) new Object[arraySize + emptyElementSlot]); - this.values = ((VType[]) new Object[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, KType pendingKey, VType pendingValue) { - assert assigned == resizeAt && (((KType) keys[slot]) == null) && !((pendingKey) == null); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final KType[] prevKeys = (KType[]) this.keys; - final VType[] prevValues = (VType[]) this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final KType[] keys = (KType[]) this.keys; - final VType[] values = (VType[]) this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final KType existing = keys[slot]; - if (((existing) == null)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = null; - values[gapSlot] = null; - assigned--; - } - - protected boolean equals(Object v1, Object v2) { - return (v1 == v2) || (v1 != null && v1.equals(v2)); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectObjectIdentityHashMap.java b/src/main/java/com/carrotsearch/hppc/ObjectObjectIdentityHashMap.java deleted file mode 100755 index c726faa1..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectObjectIdentityHashMap.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; - -/** An identity hash map of Object to Object. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeIdentityHashMap.java") -public class ObjectObjectIdentityHashMap extends ObjectObjectHashMap { - /** New instance with sane defaults. */ - public ObjectObjectIdentityHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectObjectIdentityHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ObjectObjectIdentityHashMap(int expectedElements, double loadFactor) { - super(expectedElements, loadFactor); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ObjectObjectIdentityHashMap( - ObjectObjectAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - @Override - public int hashKey(KType key) { - assert !((key) == null); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(System.identityHashCode(key)); - } - - @Override - public boolean equals(Object v1, Object v2) { - return v1 == v2; - } - - @SuppressWarnings("unchecked") - @Override - protected boolean equalElements(ObjectObjectHashMap other) { - if (other.size() != size()) { - return false; - } - - for (ObjectObjectCursor c : other) { - KType key = (KType) c.key; - if (!containsKey(key) - || !equals(c.value, get(key))) { // Compare values using the same function as keys. - return false; - } - } - - return true; - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ObjectObjectIdentityHashMap from( - KType[] keys, VType[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ObjectObjectIdentityHashMap map = new ObjectObjectIdentityHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectObjectMap.java b/src/main/java/com/carrotsearch/hppc/ObjectObjectMap.java deleted file mode 100755 index c1316b06..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectObjectMap.java +++ /dev/null @@ -1,183 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface ObjectObjectMap - extends ObjectObjectAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public VType get(KType key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public VType getOrDefault(KType key, VType defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public VType put(KType key, VType value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(KType key, VType value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(ObjectObjectAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll( - Iterable> iterable); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public VType remove(KType key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link ObjectObjectMap} and both objects contains exactly the - * same key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(KType key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public VType indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public VType indexReplace(int index, VType newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, KType key, VType value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public VType indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectSet.java b/src/main/java/com/carrotsearch/hppc/ObjectSet.java deleted file mode 100755 index 24355d4d..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectSet.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.carrotsearch.hppc; - -/** A set of Objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeSet.java") -public interface ObjectSet extends ObjectCollection { - /** - * Adds k to the set. - * - * @return Returns true if this element was not part of the set before. Returns - * false if an equal element is already part of the set, does not replace the - * existing element with the argument. - */ - public boolean add(KType k); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); - - /** - * Adds all elements from the given {@link ObjectContainer} to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - * @since 0.9.1 - */ - public int addAll(ObjectContainer container); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectShortAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/ObjectShortAssociativeContainer.java deleted file mode 100755 index a5c46c27..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectShortAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see ObjectContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface ObjectShortAssociativeContainer extends Iterable> { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator> iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(KType key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ObjectShortPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ObjectShortProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public > T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ObjectShortPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public > T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public ObjectCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public ShortContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectShortHashMap.java b/src/main/java/com/carrotsearch/hppc/ObjectShortHashMap.java deleted file mode 100755 index 9d492a11..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectShortHashMap.java +++ /dev/null @@ -1,1089 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of Object to short, implemented using open addressing with - * linear probing for collision resolution. Supports null key. - * - * @see HPPC interfaces diagram - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class ObjectShortHashMap - implements ObjectShortMap, Preallocable, Cloneable, Accountable { - /** The array holding keys. */ - public Object[] keys; - - /** The array holding values. */ - public short[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public ObjectShortHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectShortHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ObjectShortHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ObjectShortHashMap(ObjectShortAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public short put(KType key, short value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == null)) { - short previousValue = hasEmptyKey ? values[mask + 1] : ((short) 0); - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - final short previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return ((short) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(ObjectShortAssociativeContainer container) { - final int count = size(); - for (ObjectShortCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable> iterable) { - final int count = size(); - for (ObjectShortCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public short putOrAdd(KType key, short putValue, short incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((short) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public short addTo(KType key, short incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public short remove(KType key) { - final int mask = this.mask; - if (((key) == null)) { - if (!hasEmptyKey) { - return ((short) 0); - } - hasEmptyKey = false; - short previousValue = values[mask + 1]; - values[mask + 1] = ((short) 0); - return previousValue; - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - final short previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return ((short) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof ObjectLookupContainer) { - if (hasEmptyKey && other.contains(null)) { - hasEmptyKey = false; - values[mask + 1] = ((short) 0); - } - - final KType[] keys = (KType[]) this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - KType existing; - if (!((existing = keys[slot]) == null) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (ObjectCursor c : other) { - remove((KType) c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectShortPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(null, values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = ((short) 0); - } - } - - final KType[] keys = (KType[]) this.keys; - final short[] values = this.values; - for (int slot = 0; slot <= mask; ) { - KType existing; - if (!((existing = keys[slot]) == null) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ObjectPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(null)) { - hasEmptyKey = false; - values[mask + 1] = ((short) 0); - } - } - - final KType[] keys = (KType[]) this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - KType existing; - if (!((existing = keys[slot]) == null) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public short get(KType key) { - if (((key) == null)) { - return hasEmptyKey ? values[mask + 1] : ((short) 0); - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return ((short) 0); - } - } - - /** {@inheritDoc} */ - @Override - public short getOrDefault(KType key, short defaultValue) { - if (((key) == null)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(KType key) { - if (((key) == null)) { - return hasEmptyKey; - } else { - final KType[] keys = (KType[]) this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(KType key) { - final int mask = this.mask; - if (((key) == null)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final KType[] keys = (KType[]) this.keys; - int slot = hashKey(key) & mask; - - KType existing; - while (!((existing = keys[slot]) == null)) { - if (this.equals(key, existing)) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public short indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public short indexReplace(int index, short newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - short previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, KType key, short value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == null)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == null); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public short indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - short previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = ((short) 0); - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, null); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (ObjectShortCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** - * Return true if all keys of some other container exist in this container. Equality comparison is - * performed with this object's {@link #equals(Object, Object)} method. - */ - protected boolean equalElements(ObjectShortHashMap other) { - if (other.size() != size()) { - return false; - } - - for (ObjectShortCursor c : other) { - KType key = (KType) c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final KType[] prevKeys = (KType[]) this.keys; - final short[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final ObjectShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new ObjectShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectShortCursor fetch() { - final int mask = ObjectShortHashMap.this.mask; - while (index <= mask) { - KType existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = (KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = null; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator> iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T procedure) { - final KType[] keys = (KType[]) this.keys; - final short[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(null, values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == null)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T predicate) { - final KType[] keys = (KType[]) this.keys; - final short[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(null, values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == null)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractObjectCollection - implements ObjectLookupContainer { - private final ObjectShortHashMap owner = ObjectShortHashMap.this; - - @Override - public boolean contains(KType e) { - return owner.containsKey(e); - } - - @Override - public > T forEach(final T procedure) { - owner.forEach((ObjectShortProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public > T forEach(final T predicate) { - owner.forEach((ObjectShortPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator> iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final KType e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator> { - private final ObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new ObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectCursor fetch() { - final int mask = ObjectShortHashMap.this.mask; - while (index <= mask) { - KType existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = (KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = null; - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public ShortCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractShortCollection { - private final ObjectShortHashMap owner = ObjectShortHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(short value) { - for (ObjectShortCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (ObjectShortCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (ObjectShortCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final short e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final ShortPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new ShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortCursor fetch() { - final int mask = ObjectShortHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!(((KType) keys[slot]) == null)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public ObjectShortHashMap clone() { - try { - - ObjectShortHashMap cloned = (ObjectShortHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (ObjectShortCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return ObjectBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ObjectShortHashMap from(KType[] keys, short[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ObjectShortHashMap map = new ObjectShortHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(KType key) { - assert !((key) == null); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(KType[] fromKeys, short[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final KType[] keys = (KType[]) this.keys; - final short[] values = this.values; - final int mask = this.mask; - KType existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == null)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == null)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - KType[] prevKeys = (KType[]) this.keys; - short[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = ((KType[]) new Object[arraySize + emptyElementSlot]); - this.values = (new short[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, KType pendingKey, short pendingValue) { - assert assigned == resizeAt && (((KType) keys[slot]) == null) && !((pendingKey) == null); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final KType[] prevKeys = (KType[]) this.keys; - final short[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final KType[] keys = (KType[]) this.keys; - final short[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final KType existing = keys[slot]; - if (((existing) == null)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = null; - values[gapSlot] = ((short) 0); - assigned--; - } - - protected boolean equals(Object v1, Object v2) { - return (v1 == v2) || (v1 != null && v1.equals(v2)); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectShortIdentityHashMap.java b/src/main/java/com/carrotsearch/hppc/ObjectShortIdentityHashMap.java deleted file mode 100755 index c5c04c28..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectShortIdentityHashMap.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -/** An identity hash map of Object to short. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeIdentityHashMap.java") -public class ObjectShortIdentityHashMap extends ObjectShortHashMap { - /** New instance with sane defaults. */ - public ObjectShortIdentityHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectShortIdentityHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ObjectShortIdentityHashMap(int expectedElements, double loadFactor) { - super(expectedElements, loadFactor); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ObjectShortIdentityHashMap(ObjectShortAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - @Override - public int hashKey(KType key) { - assert !((key) == null); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(System.identityHashCode(key)); - } - - @Override - public boolean equals(Object v1, Object v2) { - return v1 == v2; - } - - @SuppressWarnings("unchecked") - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ObjectShortIdentityHashMap from(KType[] keys, short[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ObjectShortIdentityHashMap map = new ObjectShortIdentityHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectShortMap.java b/src/main/java/com/carrotsearch/hppc/ObjectShortMap.java deleted file mode 100755 index df88b9e2..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectShortMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ObjectShortCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface ObjectShortMap extends ObjectShortAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public short get(KType key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public short getOrDefault(KType key, short defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public short put(KType key, short value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(KType key, short value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(ObjectShortAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable> iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public short putOrAdd(KType key, short putValue, short incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public short addTo(KType key, short additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public short remove(KType key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link ObjectShortMap} and both objects contains exactly the - * same key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(KType key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public short indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public short indexReplace(int index, short newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, KType key, short value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public short indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/ObjectStack.java b/src/main/java/com/carrotsearch/hppc/ObjectStack.java deleted file mode 100755 index d768be98..00000000 --- a/src/main/java/com/carrotsearch/hppc/ObjectStack.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ObjectCursor; -import java.util.Arrays; - -/** - * A subclass of {@link ObjectArrayList} adding stack-related utility methods. The top of the stack - * is at the {@link #size()} - 1 element. - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeStack.java") -public class ObjectStack extends ObjectArrayList { - /** New instance with sane defaults. */ - public ObjectStack() { - super(); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ObjectStack(int expectedElements) { - super(expectedElements); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public ObjectStack(int expectedElements, ArraySizingStrategy resizer) { - super(expectedElements, resizer); - } - - /** Create a stack by pushing all elements of another container to it. */ - public ObjectStack(ObjectContainer container) { - super(container); - } - - /** Adds one Object to the stack. */ - public void push(KType e1) { - ensureBufferSpace(1); - buffer[elementsCount++] = e1; - } - - /** Adds two Objects to the stack. */ - public void push(KType e1, KType e2) { - ensureBufferSpace(2); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - } - - /** Adds three Objects to the stack. */ - public void push(KType e1, KType e2, KType e3) { - ensureBufferSpace(3); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - buffer[elementsCount++] = e3; - } - - /** Adds four Objects to the stack. */ - public void push(KType e1, KType e2, KType e3, KType e4) { - ensureBufferSpace(4); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - buffer[elementsCount++] = e3; - buffer[elementsCount++] = e4; - } - - /** Add a range of array elements to the stack. */ - public void push(KType[] elements, int start, int len) { - assert start >= 0 && len >= 0; - - ensureBufferSpace(len); - System.arraycopy(elements, start, buffer, elementsCount, len); - elementsCount += len; - } - - /** - * Vararg-signature method for pushing elements at the top of the stack. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - */ - @SafeVarargs - public final void push(KType... elements) { - push(elements, 0, elements.length); - } - - /** Pushes all elements from another container to the top of the stack. */ - public int pushAll(ObjectContainer container) { - return addAll(container); - } - - /** Pushes all elements from another iterable to the top of the stack. */ - public int pushAll(Iterable> iterable) { - return addAll(iterable); - } - - /** Discard an arbitrary number of elements from the top of the stack. */ - public void discard(int count) { - assert elementsCount >= count; - - elementsCount -= count; - - Arrays.fill(buffer, elementsCount, elementsCount + count, null); - } - - /** Discard the top element from the stack. */ - public void discard() { - assert elementsCount > 0; - - elementsCount--; - - buffer[elementsCount] = null; - } - - /** Remove the top element from the stack and return it. */ - public KType pop() { - return removeLast(); - } - - /** Peek at the top element on the stack. */ - public KType peek() { - assert elementsCount > 0; - return (KType) buffer[elementsCount - 1]; - } - - /** Create a stack by pushing a variable number of arguments to it. */ - @SafeVarargs - public static ObjectStack from(KType... elements) { - final ObjectStack stack = new ObjectStack(elements.length); - stack.push(elements); - return stack; - } - - /** {@inheritDoc} */ - @Override - public ObjectStack clone() { - return (ObjectStack) super.clone(); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/PgmIndexUtil.java b/src/main/java/com/carrotsearch/hppc/PgmIndexUtil.java deleted file mode 100755 index 9793ab2b..00000000 --- a/src/main/java/com/carrotsearch/hppc/PgmIndexUtil.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc; - -/** Utility methods for {@code KTypePgmIndex}. */ -final class PgmIndexUtil { - - /** Adds the first key of the current segment to the segment data bytes. */ - static void addKey(KType key, IntArrayList segmentData) { - throw new UnsupportedOperationException("Invalid for generic type: " + key); - } - - /** Adds the first key of the current segment to the segment data bytes. */ - static void addKey(int key, IntArrayList segmentData) { - segmentData.add(key); - } - - /** Adds the first key of the current segment to the segment data bytes. */ - static void addKey(float key, IntArrayList segmentData) { - addKey(Float.floatToIntBits(key), segmentData); - } - - /** Adds the first key of the current segment to the segment data bytes. */ - static void addKey(long key, IntArrayList segmentData) { - segmentData.add((int) key); - segmentData.add((int) (key >> 32)); - } - - /** Adds the first key of the current segment to the segment data bytes. */ - static void addKey(double key, IntArrayList segmentData) { - addKey(Double.doubleToRawLongBits(key), segmentData); - } - - /** Gets the first key of the segment at the given data index. */ - static KType getKey(int segmentDataIndex, int[] segmentData, KType keyType) { - throw new UnsupportedOperationException("Invalid for generic type: " + keyType); - } - - /** Gets the first key of the segment at the given data index. */ - static int getKey(int segmentDataIndex, int[] segmentData, int keyType) { - return segmentData[segmentDataIndex]; - } - - /** Gets the first key of the segment at the given data index. */ - static float getKey(int segmentDataIndex, int[] segmentData, float keyType) { - return Float.intBitsToFloat(getKey(segmentDataIndex, segmentData, 0)); - } - - /** Gets the first key of the segment at the given data index. */ - static long getKey(int segmentDataIndex, int[] segmentData, long keyType) { - return (segmentData[segmentDataIndex] & 0xFFFFFFFFL) - | (((long) segmentData[segmentDataIndex + 1]) << 32); - } - - /** Gets the first key of the segment at the given data index. */ - static double getKey(int segmentDataIndex, int[] segmentData, double keyType) { - return Double.longBitsToDouble(getKey(segmentDataIndex, segmentData, 0L)); - } - - /** - * Adds the intercept of the current segment to the segment data bytes. The intercept is stored as - * an int for a key size equal to 1, otherwise it is stored as a long. - * - * @param keySize The size of the key, measure in {@link Integer#BYTES}. - */ - static void addIntercept(long intercept, IntArrayList segmentData, int keySize) { - assert keySize >= 1 && keySize <= 2; - if (keySize == 1) { - addKey((int) intercept, segmentData); - } else { - addKey(intercept, segmentData); - } - } - - /** - * Gets the intercept of the segment at the given data index. - * - * @param keySize The size of the key, measure in {@link Integer#BYTES}. - */ - static long getIntercept(int segmentDataIndex, int[] segmentData, int keySize) { - assert keySize >= 1 && keySize <= 2; - if (keySize == 1) { - return getKey(segmentDataIndex, segmentData, 0); - } - return getKey(segmentDataIndex, segmentData, 0L); - } - - /** - * Adds the slope of the current segment to the segment data bytes. The intercept is stored as a - * float for a key size equal to 1, otherwise it is stored as a double. - * - * @param keySize The size of the key, measure in {@link Integer#BYTES}. - */ - static void addSlope(double slope, IntArrayList segmentData, int keySize) { - assert keySize >= 1 && keySize <= 2; - if (keySize == 1) { - addKey((float) slope, segmentData); - } else { - addKey(slope, segmentData); - } - } - - /** - * Gets the slope of the segment at the given data index. - * - * @param keySize The size of the key, measure in {@link Integer#BYTES}. - */ - static double getSlope(int segmentDataIndex, int[] segmentData, int keySize) { - assert keySize >= 1 && keySize <= 2; - if (keySize == 1) { - return getKey(segmentDataIndex, segmentData, 0f); - } - return getKey(segmentDataIndex, segmentData, 0d); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/PlaModel.java b/src/main/java/com/carrotsearch/hppc/PlaModel.java deleted file mode 100755 index 49317404..00000000 --- a/src/main/java/com/carrotsearch/hppc/PlaModel.java +++ /dev/null @@ -1,414 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc; - -import java.util.Arrays; - -/** - * Optimal Piecewise Linear Approximation Model for KType keys. - * - *

Learns a mapping that returns a position for a KType key which is at most epsilon - * away from the correct one in a sorted list of keys. It is optimal and piecewise because it learns - * the minimum number of epsilon-approximate segments. - * - *

The PLA-model consists of a sequence segments. A segment s is a triple (key,slope,intercept) - * that indexes a range of keys through the function fs(k) = k × slope + intercept, which provides - * an epsilon-approximation of the position of the key k. - */ -public class PlaModel implements Accountable { - - /** Initial capacity of the lower and upper point lists. */ - private static final int INITIAL_CAPACITY = 1 << 8; - - /** Epsilon precision of the PLA-model. */ - private int epsilon; - - /** First key of the current segment. */ - private double firstKey; - - /** Previous key used to check that keys are added in strictly increasing sequence. */ - private double previousKey; - - /** Number of points in the convex hull for the current segment. */ - private int numPointsInHull; - - /** Enclosing rectangle for the current segment. */ - private final Point[] rect = new Point[4]; - - /** - * Ordered list of lower points for the current segment. Inside the list, allocated points are - * re-used. - */ - private final PointList lower = new PointList(INITIAL_CAPACITY); - - /** - * Ordered list of upper points for the current segment. Inside the list, allocated points are - * re-used. - */ - private final PointList upper = new PointList(INITIAL_CAPACITY); - - /** Index of the first lower point to compare to. */ - private int lowerStart; - - /** Index of the first upper point to compare to. */ - private int upperStart; - - // Re-used mutable points and slopes. - private final Point point1 = new Point(); - private final Point point2 = new Point(); - private final Slope slope1 = new Slope(); - private final Slope slope2 = new Slope(); - private final Slope slopeTmp = new Slope(); - private final Slope slopeMin = new Slope(); - private final Slope slopeMax = new Slope(); - - /** - * Creates an optimal PLA-model with the provided epsilon precision. - * - * @param epsilon must be greater than or equal to 0. - */ - public PlaModel(int epsilon) { - setEpsilon(epsilon); - for (int i = 0; i < rect.length; i++) { - rect[i] = new Point(); - } - reset(); - } - - /** Sets epsilon precision which must be greater than or equal to 0. */ - public void setEpsilon(int epsilon) { - if (epsilon < 0) { - throw new IllegalArgumentException("epsilon must be >= 0"); - } - this.epsilon = epsilon; - } - - private void reset() { - previousKey = Double.NEGATIVE_INFINITY; - numPointsInHull = 0; - lower.clear(); - upper.clear(); - } - - /** - * Adds a key to this PLA-model. The keys must be provided in a strictly increasing sequence. That - * is, the key must be greater than the previous key. - * - * @param index The index of the key in the sorted key list. - * @param segmentConsumer The consumer to call when a new segment is built in the PLA-model. - */ - public void addKey(double key, int index, SegmentConsumer segmentConsumer) { - if (key <= previousKey) { - throw new IllegalArgumentException("Keys must be increasing"); - } - previousKey = key; - point1.set(key, addEpsilon(index)); - point2.set(key, subtractEpsilon(index)); - - if (numPointsInHull > 1) { - slope1.set(rect[0], rect[2]); - slope2.set(rect[1], rect[3]); - boolean outside_line1 = slopeTmp.set(rect[2], point1).isLessThan(slope1); - boolean outside_line2 = slopeTmp.set(rect[3], point2).isGreaterThan(slope2); - if (outside_line1 || outside_line2) { - produceSegment(segmentConsumer); - numPointsInHull = 0; - } - } - if (numPointsInHull == 0) { - firstKey = key; - rect[0].set(point1); - rect[1].set(point2); - upper.clear(); - lower.clear(); - upper.add(point1); - lower.add(point2); - upperStart = lowerStart = 0; - numPointsInHull++; - return; - } - if (numPointsInHull == 1) { - rect[2].set(point2); - rect[3].set(point1); - upper.add(point1); - lower.add(point2); - numPointsInHull++; - return; - } - - if (slopeTmp.set(rect[1], point1).isLessThan(slope2)) { - // Find extreme slope. - slopeMin.set(point1, lower.get(lowerStart)); - int min_i = lowerStart; - for (int i = lowerStart + 1; i < lower.size(); i++) { - slopeTmp.set(point1, lower.get(i)); - if (slopeTmp.isGreaterThan(slopeMin)) { - break; - } - slopeMin.set(slopeTmp); - min_i = i; - } - rect[1].set(lower.get(min_i)); - rect[3].set(point1); - lowerStart = min_i; - - // Hull update. - int end = upper.size(); - while (end >= upperStart + 2 && cross(upper.get(end - 2), upper.get(end - 1), point1) <= 0) { - end--; - } - upper.clearFrom(end); - upper.add(point1); - } - - if (slopeTmp.set(rect[0], point2).isGreaterThan(slope1)) { - // Find extreme slope. - slopeMax.set(point2, upper.get(upperStart)); - int max_i = upperStart; - for (int i = upperStart + 1; i < upper.size(); i++) { - slopeTmp.set(point2, upper.get(i)); - if (slopeTmp.isLessThan(slopeMax)) { - break; - } - slopeMax.set(slopeTmp); - max_i = i; - } - rect[0].set(upper.get(max_i)); - rect[2].set(point2); - upperStart = max_i; - - // Hull update. - int end = lower.size(); - while (end >= lowerStart + 2 && cross(lower.get(end - 2), lower.get(end - 1), point2) >= 0) { - end--; - } - lower.clearFrom(end); - lower.add(point2); - } - - numPointsInHull++; - } - - private void produceSegment(SegmentConsumer segmentConsumer) { - double slope; - long intercept; - - if (numPointsInHull == 1) { - slope = 0d; - intercept = ((long) rect[0].y + rect[1].y) >>> 1; - - } else { - Point p0 = rect[0]; - Point p1 = rect[1]; - Point p2 = rect[2]; - Point p3 = rect[3]; - - // Compute the slope intersection point. - double intersectX; - double intersectY; - slope1.set(p0, p2); - slope2.set(p1, p3); - if (slope1.isEqual(slope2)) { - intersectX = p0.x; - intersectY = p0.y; - } else { - slopeTmp.set(p0, p1); - double a = slope1.dx * slope2.dy - slope1.dy * slope2.dx; - double b = (slopeTmp.dx * slope2.dy - slopeTmp.dy * slope2.dx) / a; - intersectX = p0.x + b * slope1.dx; - intersectY = p0.y + b * slope1.dy; - } - - // Compute the slope range. - double minSlope = Slope.asDouble(p0, p2); - double maxSlope = Slope.asDouble(p1, p3); - - // Compute the segment slope and intercept. - slope = (minSlope + maxSlope) / 2d; - intercept = (long) (intersectY - (intersectX - firstKey) * slope); - } - - segmentConsumer.accept(firstKey, slope, intercept); - } - - /** - * Finishes the PLA-model construction. Declares that no additional keys will be added. Builds the - * last segment and calls the provided {@link SegmentConsumer}. - */ - public void finish(SegmentConsumer segmentConsumer) { - produceSegment(segmentConsumer); - reset(); - } - - @Override - public long ramBytesAllocated() { - // int: epsilon, numPointsInHull, lowerStart, upperStart - // double: firstKey, previousKey - // Point: rect[4], point1, point2 - // Slope: slope1, slope2, slopeTmp, slopeMin, slopeMax - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + 2 * Double.BYTES - + 6L * Point.RAM_BYTES_ALLOCATED - + lower.ramBytesAllocated() - + upper.ramBytesAllocated() - + 5L * Slope.RAM_BYTES_ALLOCATED; - } - - @Override - public long ramBytesUsed() { - return ramBytesAllocated(); - } - - private int addEpsilon(int index) { - try { - return Math.addExact(index, epsilon); - } catch (ArithmeticException e) { - return Integer.MAX_VALUE; - } - } - - private int subtractEpsilon(int index) { - try { - return Math.subtractExact(index, epsilon); - } catch (ArithmeticException e) { - return Integer.MIN_VALUE; - } - } - - private static double cross(Point o, Point a, Point b) { - return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x); - } - - /** Consumer notified when a new segment is built by the {@link PlaModel}. */ - public interface SegmentConsumer { - - /** - * Consumes a new segment. The segment is defined by the epsilon-approximation function fs(k) = - * k × slope + intercept. - * - * @param firstKey The first key of the segment. - * @param slope The segment slope. - * @param intercept The segment intercept. - */ - void accept(double firstKey, double slope, long intercept); - } - - /** Re-usable mutable (x,y) point. */ - private static class Point { - - static final int RAM_BYTES_ALLOCATED = - RamUsageEstimator.NUM_BYTES_OBJECT_HEADER + Double.BYTES + Long.BYTES; - - double x; - long y; - - Point set(double x, long y) { - this.x = x; - this.y = y; - return this; - } - - Point set(Point p) { - return set(p.x, p.y); - } - } - - /** List of mutable {@link Point}. Re-uses allocated points instead of creating new instances. */ - private static class PointList implements Accountable { - - Point[] points; - int size; - int numAllocated; - - PointList(int initialCapacity) { - points = new Point[initialCapacity]; - } - - void add(Point point) { - if (size == points.length) { - int newSize = - BoundedProportionalArraySizingStrategy.DEFAULT_INSTANCE.grow(points.length, size, 1); - points = Arrays.copyOf(points, newSize); - } - if (size == numAllocated) { - points[numAllocated++] = new Point(); - } - points[size++].set(point); - } - - Point get(int index) { - return points[index]; - } - - int size() { - return size; - } - - void clear() { - size = 0; - } - - void clearFrom(int end) { - size = end; - } - - @Override - public long ramBytesAllocated() { - // int: size, numAllocated - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 2 * Integer.BYTES - + RamUsageEstimator.shallowSizeOfArray(points) - + (long) numAllocated * Point.RAM_BYTES_ALLOCATED; - } - - @Override - public long ramBytesUsed() { - return ramBytesAllocated(); - } - } - - /** Re-usable mutable (dx,dy) slope. */ - private static class Slope { - - static final int RAM_BYTES_ALLOCATED = - RamUsageEstimator.NUM_BYTES_OBJECT_HEADER + Double.BYTES + Long.BYTES; - - double dx; - long dy; - - void set(Slope s) { - dx = s.dx; - dy = s.dy; - } - - Slope set(Point p1, Point p2) { - dx = p2.x - p1.x; - dy = p2.y - p1.y; - return this; - } - - boolean isLessThan(Slope s) { - return dy * s.dx < dx * s.dy; - } - - boolean isGreaterThan(Slope s) { - return dy * s.dx > dx * s.dy; - } - - boolean isEqual(Slope s) { - return Double.doubleToLongBits(dy * s.dx) == Double.doubleToLongBits(dx * s.dy); - } - - static double asDouble(Point p1, Point p2) { - return (double) (p2.y - p1.y) / (p2.x - p1.x); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/Preallocable.java b/src/main/java/com/carrotsearch/hppc/Preallocable.java deleted file mode 100755 index 1251bcb9..00000000 --- a/src/main/java/com/carrotsearch/hppc/Preallocable.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc; - -/** Anything that can preallocate buffers given prior knowledge of the number of stored elements. */ -public interface Preallocable { - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - public void ensureCapacity(int expectedElements); -} diff --git a/src/main/java/com/carrotsearch/hppc/RamUsageEstimator.java b/src/main/java/com/carrotsearch/hppc/RamUsageEstimator.java deleted file mode 100755 index 907ea2e9..00000000 --- a/src/main/java/com/carrotsearch/hppc/RamUsageEstimator.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc; - -import java.lang.reflect.Array; -import java.lang.reflect.Method; -import java.util.Collections; -import java.util.IdentityHashMap; -import java.util.Map; - -/** - * Helper class that helps estimate memory usage - * - *

Mostly forked from Lucene tag releases/lucene-solr/8.5.1 - */ -final class RamUsageEstimator { - /** No instantiation. */ - private RamUsageEstimator() {} - - /** True, iff compressed references (oops) are enabled by this JVM */ - static final boolean COMPRESSED_REFS_ENABLED; - - /** Number of bytes this JVM uses to represent an object reference. */ - static final int NUM_BYTES_OBJECT_REF; - - /** Number of bytes to represent an object header (no fields, no alignments). */ - static final int NUM_BYTES_OBJECT_HEADER; - - /** Number of bytes to represent an array header (no content, but with alignments). */ - static final int NUM_BYTES_ARRAY_HEADER; - - /** - * A constant specifying the object alignment boundary inside the JVM. Objects will always take a - * full multiple of this constant, possibly wasting some space. - */ - static final int NUM_BYTES_OBJECT_ALIGNMENT; - - /** Sizes of primitive classes. */ - static final Map, Integer> primitiveSizes; - - static { - Map, Integer> primitiveSizesMap = new IdentityHashMap<>(); - primitiveSizesMap.put(boolean.class, 1); - primitiveSizesMap.put(byte.class, 1); - primitiveSizesMap.put(char.class, Character.BYTES); - primitiveSizesMap.put(short.class, Short.BYTES); - primitiveSizesMap.put(int.class, Integer.BYTES); - primitiveSizesMap.put(float.class, Float.BYTES); - primitiveSizesMap.put(double.class, Double.BYTES); - primitiveSizesMap.put(long.class, Long.BYTES); - - primitiveSizes = Collections.unmodifiableMap(primitiveSizesMap); - } - - static final boolean JRE_IS_64BIT; - - static final String MANAGEMENT_FACTORY_CLASS = "java.lang.management.ManagementFactory"; - static final String HOTSPOT_BEAN_CLASS = "com.sun.management.HotSpotDiagnosticMXBean"; - - static final String OS_ARCH = System.getProperty("os.arch"); - - // Initialize constants and try to collect information about the JVM internals. - static { - boolean is64Bit = false; - String datamodel = null; - try { - datamodel = System.getProperty("sun.arch.data.model"); - if (datamodel != null) { - is64Bit = datamodel.contains("64"); - } - } catch (SecurityException ignored) { - } - if (datamodel == null) { - is64Bit = OS_ARCH != null && OS_ARCH.contains("64"); - } - JRE_IS_64BIT = is64Bit; - if (JRE_IS_64BIT) { - // Try to get compressed oops and object alignment (the default seems to be 8 on Hotspot); - // (this only works on 64 bit, on 32 bits the alignment and reference size is fixed): - boolean compressedOops = false; - int objectAlignment = 8; - try { - final Class beanClazz = Class.forName(HOTSPOT_BEAN_CLASS); - // we use reflection for this, because the management factory is not part - // of Java 8's compact profile: - final Object hotSpotBean = - Class.forName(MANAGEMENT_FACTORY_CLASS) - .getMethod("getPlatformMXBean", Class.class) - .invoke(null, beanClazz); - if (hotSpotBean != null) { - final Method getVMOptionMethod = beanClazz.getMethod("getVMOption", String.class); - try { - final Object vmOption = getVMOptionMethod.invoke(hotSpotBean, "UseCompressedOops"); - compressedOops = - Boolean.parseBoolean( - vmOption.getClass().getMethod("getValue").invoke(vmOption).toString()); - } catch (ReflectiveOperationException | RuntimeException ignored) { - } - try { - final Object vmOption = getVMOptionMethod.invoke(hotSpotBean, "ObjectAlignmentInBytes"); - objectAlignment = - Integer.parseInt( - vmOption.getClass().getMethod("getValue").invoke(vmOption).toString()); - } catch (ReflectiveOperationException | RuntimeException ignored) { - } - } - } catch (ReflectiveOperationException | RuntimeException ignored) { - } - COMPRESSED_REFS_ENABLED = compressedOops; - NUM_BYTES_OBJECT_ALIGNMENT = objectAlignment; - // reference size is 4, if we have compressed oops: - NUM_BYTES_OBJECT_REF = COMPRESSED_REFS_ENABLED ? 4 : 8; - // "best guess" based on reference size: - NUM_BYTES_OBJECT_HEADER = 8 + NUM_BYTES_OBJECT_REF; - // array header is NUM_BYTES_OBJECT_HEADER + NUM_BYTES_INT, but aligned (object alignment): - NUM_BYTES_ARRAY_HEADER = (int) alignObjectSize(NUM_BYTES_OBJECT_HEADER + Integer.BYTES); - } else { - COMPRESSED_REFS_ENABLED = false; - NUM_BYTES_OBJECT_ALIGNMENT = 8; - NUM_BYTES_OBJECT_REF = 4; - NUM_BYTES_OBJECT_HEADER = 8; - // For 32 bit JVMs, no extra alignment of array header: - NUM_BYTES_ARRAY_HEADER = NUM_BYTES_OBJECT_HEADER + Integer.BYTES; - } - } - - /** Aligns an object size to be the next multiple of {@link #NUM_BYTES_OBJECT_ALIGNMENT}. */ - static long alignObjectSize(long size) { - size += (long) NUM_BYTES_OBJECT_ALIGNMENT - 1L; - return size - (size % NUM_BYTES_OBJECT_ALIGNMENT); - } - - /** - * Return used part of shallow size of any array. - * - * @param usedSize Size that array is actually used - */ - static long shallowUsedSizeOfArray(Object array, int usedSize) { - long size = NUM_BYTES_ARRAY_HEADER; - if (usedSize > 0) { - Class arrayElementClazz = array.getClass().getComponentType(); - if (arrayElementClazz.isPrimitive()) { - size += (long) usedSize * primitiveSizes.get(arrayElementClazz); - } else { - size += (long) NUM_BYTES_OBJECT_REF * usedSize; - } - } - return alignObjectSize(size); - } - - /** Return shallow size of any array. */ - static long shallowSizeOfArray(Object array) { - return shallowUsedSizeOfArray(array, Array.getLength(array)); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortArrayDeque.java b/src/main/java/com/carrotsearch/hppc/ShortArrayDeque.java deleted file mode 100755 index 9c1cf824..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortArrayDeque.java +++ /dev/null @@ -1,776 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; - -import com.carrotsearch.hppc.cursors.ShortCursor; -import com.carrotsearch.hppc.predicates.ShortPredicate; -import com.carrotsearch.hppc.procedures.ShortProcedure; -import java.util.*; - -/** An array-backed {@link ShortDeque}. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeArrayDeque.java") -public class ShortArrayDeque extends AbstractShortCollection - implements ShortDeque, Preallocable, Cloneable, Accountable { - - /** Reuse the same strategy instance. */ - private static final BoundedProportionalArraySizingStrategy DEFAULT_SIZING_STRATEGY = - BoundedProportionalArraySizingStrategy.DEFAULT_INSTANCE; - - /** Internal array for storing elements of the deque. */ - public short[] buffer = ShortArrayList.EMPTY_ARRAY; - - /** - * The index of the element at the head of the deque or an arbitrary number equal to tail if the - * deque is empty. - */ - public int head; - - /** The index at which the next element would be added to the tail of the deque. */ - public int tail; - - /** Buffer resizing strategy. */ - protected final ArraySizingStrategy resizer; - - /** New instance with sane defaults. */ - public ShortArrayDeque() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ShortArrayDeque(int expectedElements) { - this(expectedElements, DEFAULT_SIZING_STRATEGY); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public ShortArrayDeque(int expectedElements, ArraySizingStrategy resizer) { - assert resizer != null; - this.resizer = resizer; - ensureCapacity(expectedElements); - } - - /** - * Creates a new deque from elements of another container, appending elements at the end of the - * deque in the iteration order. - */ - public ShortArrayDeque(ShortContainer container) { - this(container.size()); - addLast(container); - } - - /** {@inheritDoc} */ - @Override - public void addFirst(short e1) { - int h = oneLeft(head, buffer.length); - if (h == tail) { - ensureBufferSpace(1); - h = oneLeft(head, buffer.length); - } - buffer[head = h] = e1; - } - - /** - * Vararg-signature method for adding elements at the front of this deque. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - * - * @param elements The elements to add. - */ - public final void addFirst(short... elements) { - ensureBufferSpace(elements.length); - for (short k : elements) { - addFirst(k); - } - } - - /** - * Inserts all elements from the given container to the front of this deque. - * - * @param container The container to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addFirst(ShortContainer container) { - int size = container.size(); - ensureBufferSpace(size); - - for (ShortCursor cursor : container) { - addFirst(cursor.value); - } - - return size; - } - - /** - * Inserts all elements from the given iterable to the front of this deque. - * - * @param iterable The iterable to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addFirst(Iterable iterable) { - int size = 0; - for (ShortCursor cursor : iterable) { - addFirst(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public void addLast(short e1) { - int t = oneRight(tail, buffer.length); - if (head == t) { - ensureBufferSpace(1); - t = oneRight(tail, buffer.length); - } - buffer[tail] = e1; - tail = t; - } - - /** - * Vararg-signature method for adding elements at the end of this deque. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - * - * @param elements The elements to iterate over. - */ - public final void addLast(short... elements) { - ensureBufferSpace(1); - for (short k : elements) { - addLast(k); - } - } - - /** - * Inserts all elements from the given container to the end of this deque. - * - * @param container The container to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addLast(ShortContainer container) { - int size = container.size(); - ensureBufferSpace(size); - - for (ShortCursor cursor : container) { - addLast(cursor.value); - } - - return size; - } - - /** - * Inserts all elements from the given iterable to the end of this deque. - * - * @param iterable The iterable to iterate over. - * @return Returns the number of elements actually added as a result of this call. - */ - public int addLast(Iterable iterable) { - int size = 0; - for (ShortCursor cursor : iterable) { - addLast(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public short removeFirst() { - assert size() > 0 : "The deque is empty."; - - final short result = buffer[head]; - buffer[head] = ((short) 0); - head = oneRight(head, buffer.length); - return result; - } - - /** {@inheritDoc} */ - @Override - public short removeLast() { - assert size() > 0 : "The deque is empty."; - - tail = oneLeft(tail, buffer.length); - final short result = buffer[tail]; - buffer[tail] = ((short) 0); - return result; - } - - /** {@inheritDoc} */ - @Override - public short getFirst() { - assert size() > 0 : "The deque is empty."; - - return buffer[head]; - } - - /** {@inheritDoc} */ - @Override - public short getLast() { - assert size() > 0 : "The deque is empty."; - - return buffer[oneLeft(tail, buffer.length)]; - } - - /** {@inheritDoc} */ - @Override - public int removeFirst(short e1) { - final int index = bufferIndexOf(e1); - if (index >= 0) removeAtBufferIndex(index); - return index; - } - - /** - * Return the index of the first (counting from head) element equal to e1. The index - * points to the {@link #buffer} array. - * - * @param e1 The element to look for. - * @return Returns the index of the first element equal to e1 or -1 if - * not found. - */ - public int bufferIndexOf(short e1) { - final int last = tail; - final int bufLen = buffer.length; - for (int i = head; i != last; i = oneRight(i, bufLen)) { - if (((e1) == (buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int removeLast(short e1) { - final int index = lastBufferIndexOf(e1); - if (index >= 0) { - removeAtBufferIndex(index); - } - return index; - } - - /** - * Return the index of the last (counting from tail) element equal to e1. The index - * points to the {@link #buffer} array. - * - * @param e1 The element to look for. - * @return Returns the index of the first element equal to e1 or -1 if - * not found. - */ - public int lastBufferIndexOf(short e1) { - final int bufLen = buffer.length; - final int last = oneLeft(head, bufLen); - for (int i = oneLeft(tail, bufLen); i != last; i = oneLeft(i, bufLen)) { - if (((e1) == (buffer[i]))) return i; - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(short e1) { - int removed = 0; - final int last = tail; - final int bufLen = buffer.length; - int from, to; - for (from = to = head; from != last; from = oneRight(from, bufLen)) { - if (((e1) == (buffer[from]))) { - buffer[from] = ((short) 0); - removed++; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = ((short) 0); - } - - to = oneRight(to, bufLen); - } - - tail = to; - return removed; - } - - /** - * Removes the element at index in the internal {#link {@link #buffer} array, - * returning its value. - * - * @param index Index of the element to remove. The index must be located between {@link #head} - * and {@link #tail} in modulo {@link #buffer} arithmetic. - */ - public void removeAtBufferIndex(int index) { - assert (head <= tail ? index >= head && index < tail : index >= head || index < tail) - : "Index out of range (head=" + head + ", tail=" + tail + ", index=" + index + ")."; - - // Cache fields in locals (hopefully moved to registers). - final short[] buffer = this.buffer; - final int bufLen = buffer.length; - final int lastIndex = bufLen - 1; - final int head = this.head; - final int tail = this.tail; - - final int leftChunk = Math.abs(index - head) % bufLen; - final int rightChunk = Math.abs(tail - index) % bufLen; - - if (leftChunk < rightChunk) { - if (index >= head) { - System.arraycopy(buffer, head, buffer, head + 1, leftChunk); - } else { - System.arraycopy(buffer, 0, buffer, 1, index); - buffer[0] = buffer[lastIndex]; - System.arraycopy(buffer, head, buffer, head + 1, lastIndex - head); - } - buffer[head] = ((short) 0); - this.head = oneRight(head, bufLen); - } else { - if (index < tail) { - System.arraycopy(buffer, index + 1, buffer, index, rightChunk); - } else { - System.arraycopy(buffer, index + 1, buffer, index, lastIndex - index); - buffer[lastIndex] = buffer[0]; - System.arraycopy(buffer, 1, buffer, 0, tail); - } - buffer[tail] = ((short) 0); - this.tail = oneLeft(tail, bufLen); - } - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int size() { - if (head <= tail) return tail - head; - else return (tail - head + buffer.length); - } - - /** - * {@inheritDoc} - * - *

The internal array buffers are not released as a result of this call. - * - * @see #release() - */ - @Override - public void clear() { - if (head < tail) { - Arrays.fill(buffer, head, tail, ((short) 0)); - } else { - Arrays.fill(buffer, 0, tail, ((short) 0)); - Arrays.fill(buffer, head, buffer.length, ((short) 0)); - } - this.head = tail = 0; - } - - /** Release internal buffers of this deque and reallocate with the default buffer. */ - public void release() { - this.head = tail = 0; - buffer = ShortArrayList.EMPTY_ARRAY; - ensureBufferSpace(0); - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - ensureBufferSpace(expectedElements - size()); - } - - /** - * Ensures the internal buffer has enough free slots to store expectedAdditions. - * Increases internal buffer size if needed. - */ - protected void ensureBufferSpace(int expectedAdditions) { - final int bufferLen = buffer.length; - final int elementsCount = size(); - - if (elementsCount + expectedAdditions >= bufferLen) { - final int emptySlot = 1; // deque invariant: always an empty slot. - final int newSize = resizer.grow(bufferLen, elementsCount + emptySlot, expectedAdditions); - assert newSize >= (elementsCount + expectedAdditions + emptySlot) - : "Resizer failed to" - + " return sensible new size: " - + newSize - + " <= " - + (elementsCount + expectedAdditions); - - try { - final short[] newBuffer = (new short[newSize]); - if (bufferLen > 0) { - toArray(newBuffer); - tail = elementsCount; - head = 0; - } - this.buffer = newBuffer; - } catch (OutOfMemoryError e) { - throw new BufferAllocationException( - "Not enough memory to allocate new buffers: %,d -> %,d", e, bufferLen, newSize); - } - } - } - - /** {@inheritDoc} */ - @Override - public short[] toArray() { - - final int size = size(); - return toArray((new short[size])); - } - - /** - * Copies elements of this deque to an array. The content of the target array is - * filled from index 0 (head of the queue) to index size() - 1 (tail of the queue). - * - * @param target The target array must be large enough to hold all elements. - * @return Returns the target argument for chaining. - */ - public short[] toArray(short[] target) { - assert target.length >= size() : "Target array must be >= " + size(); - - if (head < tail) { - // The contents is not wrapped around. Just copy. - System.arraycopy(buffer, head, target, 0, size()); - } else if (head > tail) { - // The contents is split. Merge elements from the following indexes: - // [head...buffer.length - 1][0, tail - 1] - final int rightCount = buffer.length - head; - System.arraycopy(buffer, head, target, 0, rightCount); - System.arraycopy(buffer, 0, target, rightCount, tail); - } - - return target; - } - - /** - * Clone this object. The returned clone will reuse the same hash function and array resizing - * strategy. - */ - @Override - public ShortArrayDeque clone() { - try { - - ShortArrayDeque cloned = (ShortArrayDeque) super.clone(); - cloned.buffer = buffer.clone(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Move one index to the left, wrapping around buffer. */ - protected static int oneLeft(int index, int modulus) { - if (index >= 1) { - return index - 1; - } - return modulus - 1; - } - - /** Move one index to the right, wrapping around buffer. */ - protected static int oneRight(int index, int modulus) { - if (index + 1 == modulus) { - return 0; - } - return index + 1; - } - - @Override - public long ramBytesAllocated() { - // int: head, tail - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES * 2 - + resizer.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(buffer); - } - - @Override - public long ramBytesUsed() { - // int: head, tail - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES * 2 - + resizer.ramBytesUsed() - + RamUsageEstimator.shallowUsedSizeOfArray(buffer, size()); - } - - /** An iterator implementation for {@link ObjectArrayDeque#iterator}. */ - private final class ValueIterator extends AbstractIterator { - private final ShortCursor cursor; - private int remaining; - - public ValueIterator() { - cursor = new ShortCursor(); - cursor.index = oneLeft(head, buffer.length); - this.remaining = size(); - } - - @Override - protected ShortCursor fetch() { - if (remaining == 0) { - return done(); - } - - remaining--; - cursor.value = buffer[cursor.index = oneRight(cursor.index, buffer.length)]; - return cursor; - } - } - - /** An iterator implementation for {@link ObjectArrayDeque#descendingIterator()}. */ - private final class DescendingValueIterator extends AbstractIterator { - private final ShortCursor cursor; - private int remaining; - - public DescendingValueIterator() { - cursor = new ShortCursor(); - cursor.index = tail; - this.remaining = size(); - } - - @Override - protected ShortCursor fetch() { - if (remaining == 0) return done(); - - remaining--; - cursor.value = buffer[cursor.index = oneLeft(cursor.index, buffer.length)]; - return cursor; - } - } - - /** - * Returns a cursor over the values of this deque (in head to tail order). The iterator is - * implemented as a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()} (to avoid boxing of primitive types). To read the current value (or index in - * the deque's buffer) use the cursor's public fields. An example is shown below. - * - *

-   * for (IntValueCursor c : intDeque) {
-   *   System.out.println("buffer index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator iterator() { - return new ValueIterator(); - } - - /** - * Returns a cursor over the values of this deque (in tail to head order). The iterator is - * implemented as a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()} (to avoid boxing of primitive types). To read the current value (or index in - * the deque's buffer) use the cursor's public fields. An example is shown below. - * - *
-   * for (Iterator<IntCursor> i = intDeque.descendingIterator(); i.hasNext();) {
-   *   final IntCursor c = i.next();
-   *   System.out.println("buffer index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator descendingIterator() { - return new DescendingValueIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - forEach(procedure, head, tail); - return procedure; - } - - /** - * Applies procedure to a slice of the deque, fromIndex, inclusive, to - * toIndex, exclusive. - */ - private void forEach(ShortProcedure procedure, int fromIndex, final int toIndex) { - final short[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - procedure.apply(buffer[i]); - } - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - int fromIndex = head; - int toIndex = tail; - - final short[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - if (!predicate.apply(buffer[i])) { - break; - } - } - - return predicate; - } - - /** Applies procedure to all elements of this deque, tail to head. */ - @Override - public T descendingForEach(T procedure) { - descendingForEach(procedure, head, tail); - return procedure; - } - - /** - * Applies procedure to a slice of the deque, toIndex, exclusive, down - * to fromIndex, inclusive. - */ - private void descendingForEach(ShortProcedure procedure, int fromIndex, final int toIndex) { - if (fromIndex == toIndex) return; - - final short[] buffer = this.buffer; - int i = toIndex; - do { - i = oneLeft(i, buffer.length); - procedure.apply(buffer[i]); - } while (i != fromIndex); - } - - /** {@inheritDoc} */ - @Override - public T descendingForEach(T predicate) { - descendingForEach(predicate, head, tail); - return predicate; - } - - /** - * Applies predicate to a slice of the deque, toIndex, exclusive, down - * to fromIndex, inclusive or until the predicate returns false. - */ - private void descendingForEach(ShortPredicate predicate, int fromIndex, final int toIndex) { - if (fromIndex == toIndex) return; - - final short[] buffer = this.buffer; - int i = toIndex; - do { - i = oneLeft(i, buffer.length); - if (!predicate.apply(buffer[i])) { - break; - } - } while (i != fromIndex); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortPredicate predicate) { - final short[] buffer = this.buffer; - final int last = tail; - final int bufLen = buffer.length; - int removed = 0; - int from, to; - from = to = head; - try { - for (from = to = head; from != last; from = oneRight(from, bufLen)) { - if (predicate.apply(buffer[from])) { - buffer[from] = ((short) 0); - removed++; - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = ((short) 0); - } - - to = oneRight(to, bufLen); - } - } finally { - // Keep the deque in consistent state even if the predicate throws an exception. - for (; from != last; from = oneRight(from, bufLen)) { - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = ((short) 0); - } - - to = oneRight(to, bufLen); - } - tail = to; - } - - return removed; - } - - /** {@inheritDoc} */ - @Override - public boolean contains(short e) { - int fromIndex = head; - int toIndex = tail; - - final short[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - if (((e) == (buffer[i]))) { - return true; - } - } - - return false; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = 1; - int fromIndex = head; - int toIndex = tail; - - final short[] buffer = this.buffer; - for (int i = fromIndex; i != toIndex; i = oneRight(i, buffer.length)) { - h = 31 * h + BitMixer.mix(this.buffer[i]); - } - return h; - } - - /** - * Returns true only if the other object is an instance of the same class and with - * the same elements. - */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Compare order-aligned elements against another {@link ShortDeque}. */ - protected boolean equalElements(ShortArrayDeque other) { - int max = size(); - if (other.size() != max) { - return false; - } - - Iterator i1 = this.iterator(); - Iterator i2 = other.iterator(); - - while (i1.hasNext() && i2.hasNext()) { - if (!((i1.next().value) == (i2.next().value))) { - return false; - } - } - - return !i1.hasNext() && !i2.hasNext(); - } - - /** Create a new deque by pushing a variable number of arguments to the end of it. */ - public static ShortArrayDeque from(short... elements) { - final ShortArrayDeque coll = new ShortArrayDeque(elements.length); - coll.addLast(elements); - return coll; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortArrayList.java b/src/main/java/com/carrotsearch/hppc/ShortArrayList.java deleted file mode 100755 index cae87388..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortArrayList.java +++ /dev/null @@ -1,579 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.ShortPredicate; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** An array-backed list of shorts. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeArrayList.java") -public class ShortArrayList extends AbstractShortCollection - implements ShortIndexedContainer, Preallocable, Cloneable, Accountable { - /** An immutable empty buffer (array). */ - public static final short[] EMPTY_ARRAY = new short[0]; - - ; - - /** Reuse the same strategy instance. */ - private static final BoundedProportionalArraySizingStrategy DEFAULT_SIZING_STRATEGY = - BoundedProportionalArraySizingStrategy.DEFAULT_INSTANCE; - - /** - * Internal array for storing the list. The array may be larger than the current size ({@link - * #size()}). - */ - public short[] buffer = EMPTY_ARRAY; - - /** Current number of elements stored in {@link #buffer}. */ - public int elementsCount; - - /** Buffer resizing strategy. */ - protected final ArraySizingStrategy resizer; - - /** New instance with sane defaults. */ - public ShortArrayList() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ShortArrayList(int expectedElements) { - this(expectedElements, DEFAULT_SIZING_STRATEGY); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public ShortArrayList(int expectedElements, ArraySizingStrategy resizer) { - assert resizer != null; - this.resizer = resizer; - buffer = Arrays.copyOf(buffer, expectedElements); - } - - /** Creates a new list from the elements of another container in its iteration order. */ - public ShortArrayList(ShortContainer container) { - this(container.size()); - addAll(container); - } - - /** {@inheritDoc} */ - @Override - public void add(short e1) { - ensureBufferSpace(1); - buffer[elementsCount++] = e1; - } - - /** - * Appends two elements at the end of the list. To add more than two elements, use add - * (vararg-version) or access the buffer directly (tight loop). - */ - public void add(short e1, short e2) { - ensureBufferSpace(2); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - } - - /** Add all elements from a range of given array to the list. */ - public void add(short[] elements, int start, int length) { - assert length >= 0 : "Length must be >= 0"; - - ensureBufferSpace(length); - System.arraycopy(elements, start, buffer, elementsCount, length); - elementsCount += length; - } - - /** - * Vararg-signature method for adding elements at the end of the list. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - */ - public final void add(short... elements) { - add(elements, 0, elements.length); - } - - /** Adds all elements from another container. */ - public int addAll(ShortContainer container) { - final int size = container.size(); - ensureBufferSpace(size); - - for (ShortCursor cursor : container) { - add(cursor.value); - } - - return size; - } - - /** Adds all elements from another iterable. */ - public int addAll(Iterable iterable) { - int size = 0; - for (ShortCursor cursor : iterable) { - add(cursor.value); - size++; - } - return size; - } - - /** {@inheritDoc} */ - @Override - public void insert(int index, short e1) { - assert (index >= 0 && index <= size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + "]."; - - ensureBufferSpace(1); - System.arraycopy(buffer, index, buffer, index + 1, elementsCount - index); - buffer[index] = e1; - elementsCount++; - } - - /** {@inheritDoc} */ - @Override - public short get(int index) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - return buffer[index]; - } - - /** {@inheritDoc} */ - @Override - public short set(int index, short e1) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - final short v = buffer[index]; - buffer[index] = e1; - return v; - } - - /** {@inheritDoc} */ - @Override - public short removeAt(int index) { - assert (index >= 0 && index < size()) - : "Index " + index + " out of bounds [" + 0 + ", " + size() + ")."; - - final short v = buffer[index]; - System.arraycopy(buffer, index + 1, buffer, index, --elementsCount - index); - - return v; - } - - /** {@inheritDoc} */ - @Override - public short removeLast() { - assert elementsCount > 0; - - final short v = buffer[--elementsCount]; - - return v; - } - - /** {@inheritDoc} */ - @Override - public void removeRange(int fromIndex, int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - System.arraycopy(buffer, toIndex, buffer, fromIndex, elementsCount - toIndex); - final int count = toIndex - fromIndex; - elementsCount -= count; - } - - /** {@inheritDoc} */ - @Override - public boolean removeElement(short e1) { - return removeFirst(e1) != -1; - } - - /** {@inheritDoc} */ - @Override - public int removeFirst(short e1) { - final int index = indexOf(e1); - if (index >= 0) removeAt(index); - return index; - } - - /** {@inheritDoc} */ - @Override - public int removeLast(short e1) { - final int index = lastIndexOf(e1); - if (index >= 0) removeAt(index); - return index; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(short e1) { - int to = 0; - for (int from = 0; from < elementsCount; from++) { - if (((e1) == (buffer[from]))) { - continue; - } - if (to != from) { - buffer[to] = buffer[from]; - } - to++; - } - final int deleted = elementsCount - to; - this.elementsCount = to; - - return deleted; - } - - /** {@inheritDoc} */ - @Override - public boolean contains(short e1) { - return indexOf(e1) >= 0; - } - - /** {@inheritDoc} */ - @Override - public int indexOf(short e1) { - for (int i = 0; i < elementsCount; i++) { - if (((e1) == (buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public int lastIndexOf(short e1) { - for (int i = elementsCount - 1; i >= 0; i--) { - if (((e1) == (buffer[i]))) { - return i; - } - } - - return -1; - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return elementsCount == 0; - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - final int bufferLen = (buffer == null ? 0 : buffer.length); - if (expectedElements > bufferLen) { - ensureBufferSpace(expectedElements - size()); - } - } - - /** - * Ensures the internal buffer has enough free slots to store expectedAdditions. - * Increases internal buffer size if needed. - */ - protected void ensureBufferSpace(int expectedAdditions) { - final int bufferLen = (buffer == null ? 0 : buffer.length); - if (elementsCount + expectedAdditions > bufferLen) { - final int newSize = resizer.grow(bufferLen, elementsCount, expectedAdditions); - assert newSize >= elementsCount + expectedAdditions - : "Resizer failed to" - + " return sensible new size: " - + newSize - + " <= " - + (elementsCount + expectedAdditions); - - this.buffer = Arrays.copyOf(buffer, newSize); - } - } - - /** - * Truncate or expand the list to the new size. If the list is truncated, the buffer will not be - * reallocated (use {@link #trimToSize()} if you need a truncated buffer), but the truncated - * values will be reset to the default value (zero). If the list is expanded, the elements beyond - * the current size are initialized with JVM-defaults (zero or null values). - */ - public void resize(int newSize) { - if (newSize <= buffer.length) { - if (newSize < elementsCount) { - Arrays.fill(buffer, newSize, elementsCount, ((short) 0)); - } else { - Arrays.fill(buffer, elementsCount, newSize, ((short) 0)); - } - } else { - ensureCapacity(newSize); - } - this.elementsCount = newSize; - } - - /** {@inheritDoc} */ - @Override - public int size() { - return elementsCount; - } - - /** Trim the internal buffer to the current size. */ - public void trimToSize() { - if (size() != this.buffer.length) { - this.buffer = toArray(); - } - } - - /** - * Sets the number of stored elements to zero. Releases and initializes the internal storage array - * to default values. To clear the list without cleaning the buffer, simply set the {@link - * #elementsCount} field to zero. - */ - @Override - public void clear() { - Arrays.fill(buffer, 0, elementsCount, ((short) 0)); - this.elementsCount = 0; - } - - /** Sets the number of stored elements to zero and releases the internal storage array. */ - @Override - public void release() { - this.buffer = EMPTY_ARRAY; - this.elementsCount = 0; - } - - /** - * {@inheritDoc} - * - *

The returned array is sized to match exactly the number of elements of the stack. - */ - @Override - public short[] toArray() { - - return Arrays.copyOf(buffer, elementsCount); - } - - /** {@inheritDoc} */ - @Override - public ShortIndexedContainer sort() { - Arrays.sort(buffer, 0, elementsCount); - return this; - } - - /** {@inheritDoc} */ - @Override - public ShortIndexedContainer reverse() { - for (int i = 0, mid = elementsCount >> 1, j = elementsCount - 1; i < mid; i++, j--) { - short tmp = buffer[i]; - buffer[i] = buffer[j]; - buffer[j] = tmp; - } - return this; - } - - /** - * Clone this object. The returned clone will reuse the same hash function and array resizing - * strategy. - */ - @Override - public ShortArrayList clone() { - try { - - final ShortArrayList cloned = (ShortArrayList) super.clone(); - cloned.buffer = buffer.clone(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = 1, max = elementsCount; - for (int i = 0; i < max; i++) { - h = 31 * h + BitMixer.mix(this.buffer[i]); - } - return h; - } - - /** - * Returns true only if the other object is an instance of the same class and with - * the same elements. - */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Compare index-aligned elements against another {@link ShortIndexedContainer}. */ - protected boolean equalElements(ShortArrayList other) { - int max = size(); - if (other.size() != max) { - return false; - } - - for (int i = 0; i < max; i++) { - if (!((get(i)) == (other.get(i)))) { - return false; - } - } - - return true; - } - - @Override - public long ramBytesAllocated() { - // int: elementsCount - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES - + resizer.ramBytesAllocated() - + RamUsageEstimator.shallowSizeOfArray(buffer); - } - - @Override - public long ramBytesUsed() { - // int: elementsCount - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + Integer.BYTES - + resizer.ramBytesUsed() - + RamUsageEstimator.shallowUsedSizeOfArray(buffer, elementsCount); - } - - /** An iterator implementation for {@link ShortArrayList#iterator}. */ - static final class ValueIterator extends AbstractIterator { - private final ShortCursor cursor; - - private final short[] buffer; - private final int size; - - public ValueIterator(short[] buffer, int size) { - this.cursor = new ShortCursor(); - this.cursor.index = -1; - this.size = size; - this.buffer = buffer; - } - - @Override - protected ShortCursor fetch() { - if (cursor.index + 1 == size) return done(); - - cursor.value = buffer[++cursor.index]; - return cursor; - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new ValueIterator(buffer, size()); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - return forEach(procedure, 0, size()); - } - - /** - * Applies procedure to a slice of the list, fromIndex, inclusive, to - * toIndex, exclusive. - */ - public T forEach(T procedure, int fromIndex, final int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - final short[] buffer = this.buffer; - for (int i = fromIndex; i < toIndex; i++) { - procedure.apply(buffer[i]); - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortPredicate predicate) { - final short[] buffer = this.buffer; - final int elementsCount = this.elementsCount; - int to = 0; - int from = 0; - try { - for (; from < elementsCount; from++) { - if (predicate.apply(buffer[from])) { - buffer[from] = ((short) 0); - continue; - } - - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = ((short) 0); - } - to++; - } - } finally { - // Keep the list in a consistent state, even if the predicate throws an exception. - for (; from < elementsCount; from++) { - if (to != from) { - buffer[to] = buffer[from]; - buffer[from] = ((short) 0); - } - to++; - } - - this.elementsCount = to; - } - - return elementsCount - to; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - return forEach(predicate, 0, size()); - } - - /** - * Applies predicate to a slice of the list, fromIndex, inclusive, to - * toIndex, exclusive, or until predicate returns false. - */ - public T forEach(T predicate, int fromIndex, final int toIndex) { - assert (fromIndex >= 0 && fromIndex <= size()) - : "Index " + fromIndex + " out of bounds [" + 0 + ", " + size() + ")."; - assert (toIndex >= 0 && toIndex <= size()) - : "Index " + toIndex + " out of bounds [" + 0 + ", " + size() + "]."; - assert fromIndex <= toIndex : "fromIndex must be <= toIndex: " + fromIndex + ", " + toIndex; - - final short[] buffer = this.buffer; - for (int i = fromIndex; i < toIndex; i++) { - if (!predicate.apply(buffer[i])) break; - } - - return predicate; - } - - /** - * Create a list from a variable number of arguments or an array of short. The - * elements are copied from the argument to the internal buffer. - */ - public static ShortArrayList from(short... elements) { - final ShortArrayList list = new ShortArrayList(elements.length); - list.add(elements); - return list; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortBufferVisualizer.java b/src/main/java/com/carrotsearch/hppc/ShortBufferVisualizer.java deleted file mode 100755 index ce873652..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortBufferVisualizer.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.carrotsearch.hppc; - -/** - * Reused buffer visualization routines. - * - * @see ShortSet#visualizeKeyDistribution(int) - * @see ShortVTypeMap#visualizeKeyDistribution(int) - */ -class ShortBufferVisualizer { - static String visualizeKeyDistribution(short[] buffer, int max, int characters) { - final StringBuilder b = new StringBuilder(); - final char[] chars = ".123456789X".toCharArray(); - for (int i = 1, start = -1; i <= characters; i++) { - int end = (int) ((long) i * max / characters); - - if (start + 1 <= end) { - int taken = 0; - int slots = 0; - for (int slot = start + 1; slot <= end; slot++, slots++) { - if (!((buffer[slot]) == 0)) { - taken++; - } - } - b.append(chars[Math.min(chars.length - 1, taken * chars.length / slots)]); - start = end; - } - } - while (b.length() < characters) { - b.append(' '); - } - return b.toString(); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortByteAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/ShortByteAssociativeContainer.java deleted file mode 100755 index 27f4953d..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortByteAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see ShortContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface ShortByteAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(short key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortBytePredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ShortByteProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ShortBytePredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public ShortCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public ByteContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortByteHashMap.java b/src/main/java/com/carrotsearch/hppc/ShortByteHashMap.java deleted file mode 100755 index 049da077..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortByteHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of short to byte, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class ShortByteHashMap implements ShortByteMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public short[] keys; - - /** The array holding values. */ - public byte[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public ShortByteHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ShortByteHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ShortByteHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ShortByteHashMap(ShortByteAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public byte put(short key, byte value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - byte previousValue = hasEmptyKey ? values[mask + 1] : ((byte) 0); - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final byte previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return ((byte) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(ShortByteAssociativeContainer container) { - final int count = size(); - for (ShortByteCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (ShortByteCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public byte putOrAdd(short key, byte putValue, byte incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((byte) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public byte addTo(short key, byte incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public byte remove(short key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return ((byte) 0); - } - hasEmptyKey = false; - byte previousValue = values[mask + 1]; - values[mask + 1] = ((byte) 0); - return previousValue; - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final byte previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return ((byte) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof ShortLookupContainer) { - if (hasEmptyKey && other.contains(((short) 0))) { - hasEmptyKey = false; - values[mask + 1] = ((byte) 0); - } - - final short[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - short existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (ShortCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortBytePredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(((short) 0), values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = ((byte) 0); - } - } - - final short[] keys = this.keys; - final byte[] values = this.values; - for (int slot = 0; slot <= mask; ) { - short existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(((short) 0))) { - hasEmptyKey = false; - values[mask + 1] = ((byte) 0); - } - } - - final short[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - short existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public byte get(short key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : ((byte) 0); - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return ((byte) 0); - } - } - - /** {@inheritDoc} */ - @Override - public byte getOrDefault(short key, byte defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(short key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(short key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public byte indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public byte indexReplace(int index, byte newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - byte previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, short key, byte value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public byte indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - byte previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = ((byte) 0); - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, ((short) 0)); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (ShortByteCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(ShortByteHashMap other) { - if (other.size() != size()) { - return false; - } - - for (ShortByteCursor c : other) { - short key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final short[] prevKeys = this.keys; - final byte[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final ShortByteCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new ShortByteCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortByteCursor fetch() { - final int mask = ShortByteHashMap.this.mask; - while (index <= mask) { - short existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = ((short) 0); - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final short[] keys = this.keys; - final byte[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(((short) 0), values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final short[] keys = this.keys; - final byte[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(((short) 0), values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractShortCollection implements ShortLookupContainer { - private final ShortByteHashMap owner = ShortByteHashMap.this; - - @Override - public boolean contains(short e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((ShortByteProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((ShortBytePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final short e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final ShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new ShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortCursor fetch() { - final int mask = ShortByteHashMap.this.mask; - while (index <= mask) { - short existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = ((short) 0); - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public ByteCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractByteCollection { - private final ShortByteHashMap owner = ShortByteHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(byte value) { - for (ShortByteCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (ShortByteCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (ShortByteCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final byte e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final BytePredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ByteCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new ByteCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ByteCursor fetch() { - final int mask = ShortByteHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public ShortByteHashMap clone() { - try { - - ShortByteHashMap cloned = (ShortByteHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (ShortByteCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return ShortBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ShortByteHashMap from(short[] keys, byte[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ShortByteHashMap map = new ShortByteHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(short key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(short[] fromKeys, byte[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final short[] keys = this.keys; - final byte[] values = this.values; - final int mask = this.mask; - short existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - short[] prevKeys = this.keys; - byte[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new short[arraySize + emptyElementSlot]); - this.values = (new byte[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, short pendingKey, byte pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final short[] prevKeys = this.keys; - final byte[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final short[] keys = this.keys; - final byte[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final short existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = ((short) 0); - values[gapSlot] = ((byte) 0); - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortByteMap.java b/src/main/java/com/carrotsearch/hppc/ShortByteMap.java deleted file mode 100755 index dfe9a67a..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortByteMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ShortByteCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface ShortByteMap extends ShortByteAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public byte get(short key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public byte getOrDefault(short key, byte defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public byte put(short key, byte value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(short key, byte value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(ShortByteAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public byte putOrAdd(short key, byte putValue, byte incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public byte addTo(short key, byte additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public byte remove(short key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link ShortByteMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(short key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public byte indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public byte indexReplace(int index, byte newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, short key, byte value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public byte indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortCharAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/ShortCharAssociativeContainer.java deleted file mode 100755 index 3ab6c85d..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortCharAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see ShortContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface ShortCharAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(short key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortCharPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ShortCharProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ShortCharPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public ShortCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public CharContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortCharHashMap.java b/src/main/java/com/carrotsearch/hppc/ShortCharHashMap.java deleted file mode 100755 index cb29c12e..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortCharHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of short to char, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class ShortCharHashMap implements ShortCharMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public short[] keys; - - /** The array holding values. */ - public char[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public ShortCharHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ShortCharHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ShortCharHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ShortCharHashMap(ShortCharAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public char put(short key, char value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - char previousValue = hasEmptyKey ? values[mask + 1] : ((char) 0); - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final char previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return ((char) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(ShortCharAssociativeContainer container) { - final int count = size(); - for (ShortCharCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (ShortCharCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public char putOrAdd(short key, char putValue, char incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((char) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public char addTo(short key, char incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public char remove(short key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return ((char) 0); - } - hasEmptyKey = false; - char previousValue = values[mask + 1]; - values[mask + 1] = ((char) 0); - return previousValue; - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final char previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return ((char) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof ShortLookupContainer) { - if (hasEmptyKey && other.contains(((short) 0))) { - hasEmptyKey = false; - values[mask + 1] = ((char) 0); - } - - final short[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - short existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (ShortCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortCharPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(((short) 0), values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = ((char) 0); - } - } - - final short[] keys = this.keys; - final char[] values = this.values; - for (int slot = 0; slot <= mask; ) { - short existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(((short) 0))) { - hasEmptyKey = false; - values[mask + 1] = ((char) 0); - } - } - - final short[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - short existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public char get(short key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : ((char) 0); - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return ((char) 0); - } - } - - /** {@inheritDoc} */ - @Override - public char getOrDefault(short key, char defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(short key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(short key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public char indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public char indexReplace(int index, char newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - char previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, short key, char value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public char indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - char previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = ((char) 0); - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, ((short) 0)); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (ShortCharCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(ShortCharHashMap other) { - if (other.size() != size()) { - return false; - } - - for (ShortCharCursor c : other) { - short key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final short[] prevKeys = this.keys; - final char[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final ShortCharCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new ShortCharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortCharCursor fetch() { - final int mask = ShortCharHashMap.this.mask; - while (index <= mask) { - short existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = ((short) 0); - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final short[] keys = this.keys; - final char[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(((short) 0), values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final short[] keys = this.keys; - final char[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(((short) 0), values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractShortCollection implements ShortLookupContainer { - private final ShortCharHashMap owner = ShortCharHashMap.this; - - @Override - public boolean contains(short e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((ShortCharProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((ShortCharPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final short e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final ShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new ShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortCursor fetch() { - final int mask = ShortCharHashMap.this.mask; - while (index <= mask) { - short existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = ((short) 0); - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public CharCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractCharCollection { - private final ShortCharHashMap owner = ShortCharHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(char value) { - for (ShortCharCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (ShortCharCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (ShortCharCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final char e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final CharPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final CharCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new CharCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected CharCursor fetch() { - final int mask = ShortCharHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public ShortCharHashMap clone() { - try { - - ShortCharHashMap cloned = (ShortCharHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (ShortCharCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return ShortBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ShortCharHashMap from(short[] keys, char[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ShortCharHashMap map = new ShortCharHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(short key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(short[] fromKeys, char[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final short[] keys = this.keys; - final char[] values = this.values; - final int mask = this.mask; - short existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - short[] prevKeys = this.keys; - char[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new short[arraySize + emptyElementSlot]); - this.values = (new char[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, short pendingKey, char pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final short[] prevKeys = this.keys; - final char[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final short[] keys = this.keys; - final char[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final short existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = ((short) 0); - values[gapSlot] = ((char) 0); - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortCharMap.java b/src/main/java/com/carrotsearch/hppc/ShortCharMap.java deleted file mode 100755 index 17bd928b..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortCharMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ShortCharCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface ShortCharMap extends ShortCharAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public char get(short key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public char getOrDefault(short key, char defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public char put(short key, char value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(short key, char value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(ShortCharAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public char putOrAdd(short key, char putValue, char incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public char addTo(short key, char additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public char remove(short key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link ShortCharMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(short key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public char indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public char indexReplace(int index, char newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, short key, char value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public char indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortCollection.java b/src/main/java/com/carrotsearch/hppc/ShortCollection.java deleted file mode 100755 index 372dfca5..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortCollection.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.predicates.ShortPredicate; - -/** - * A collection allows basic, efficient operations on sets of elements (difference and - * intersection). - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeCollection.java") -public interface ShortCollection extends ShortContainer { - /** - * Removes all occurrences of e from this collection. - * - * @param e Element to be removed from this collection, if present. - * @return The number of removed elements as a result of this call. - */ - public int removeAll(short e); - - /** - * Removes all elements in this collection that are present in c. - * - * @return Returns the number of removed elements. - */ - public int removeAll(ShortLookupContainer c); - - /** - * Removes all elements in this collection for which the given predicate returns true - * . - * - * @return Returns the number of removed elements. - */ - public int removeAll(ShortPredicate predicate); - - /** - * Keeps all elements in this collection that are present in c. Runs in time - * proportional to the number of elements in this collection. Equivalent of sets intersection. - * - * @return Returns the number of removed elements. - */ - public int retainAll(ShortLookupContainer c); - - /** - * Keeps all elements in this collection for which the given predicate returns true. - * - * @return Returns the number of removed elements. - */ - public int retainAll(ShortPredicate predicate); - - /** - * Removes all elements from this collection. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortContainer.java b/src/main/java/com/carrotsearch/hppc/ShortContainer.java deleted file mode 100755 index f425a1fe..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortContainer.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ShortCursor; -import com.carrotsearch.hppc.predicates.ShortPredicate; -import com.carrotsearch.hppc.procedures.ShortProcedure; -import java.util.Iterator; - -/** A generic container holding shorts. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeContainer.java") -public interface ShortContainer extends Iterable { - /** - * Returns an iterator to a cursor traversing the collection. The order of traversal is not - * defined. More than one cursor may be active at a time. The behavior of iterators is undefined - * if structural changes are made to the underlying collection. - * - *

The iterator is implemented as a cursor and it returns the same cursor instance on - * every call to {@link Iterator#next()} (to avoid boxing of primitive types). To read the current - * list's value (or index in the list) use the cursor's public fields. An example is shown below. - * - *

-   * for (ShortCursor<short> c : container) {
-   *   System.out.println("index=" + c.index + " value=" + c.value);
-   * }
-   * 
- */ - public Iterator iterator(); - - /** - * Lookup a given element in the container. This operation has no speed guarantees (may be linear - * with respect to the size of this container). - * - * @return Returns true if this container has an element equal to e. - */ - public boolean contains(short e); - - /** - * Return the current number of elements in this container. The time for calculating the - * container's size may take O(n) time, although implementing classes should try to - * maintain the current size and return in constant time. - */ - public int size(); - - /** Shortcut for size() == 0. */ - public boolean isEmpty(); - - /** - * Copies all elements of this container to an array. - * - *

The returned array is always a copy, regardless of the storage used by the container. - */ - public short[] toArray(); - - /** - * Applies a procedure to all container elements. Returns the argument (any subclass - * of {@link ShortProcedure}. This lets the caller to call methods of the argument by chaining the - * call (even if the argument is an anonymous type) to retrieve computed values, for example - * (IntContainer): - * - *

-   * int count = container.forEach(new IntProcedure() {
-   *   int count; // this is a field declaration in an anonymous class.
-   *
-   *   public void apply(int value) {
-   *     count++;
-   *   }
-   * }).count;
-   * 
- */ - public T forEach(T procedure); - - /** - * Applies a predicate to container elements as long, as the predicate returns - * true. The iteration is interrupted otherwise. - */ - public T forEach(T predicate); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortDeque.java b/src/main/java/com/carrotsearch/hppc/ShortDeque.java deleted file mode 100755 index 244b6a6a..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortDeque.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ShortCursor; -import com.carrotsearch.hppc.predicates.ShortPredicate; -import com.carrotsearch.hppc.procedures.ShortProcedure; -import java.util.Deque; -import java.util.Iterator; - -/** - * A linear collection that supports element insertion and removal at both ends. - * - * @see Deque - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeDeque.java") -public interface ShortDeque extends ShortCollection { - /** - * Removes the first element that equals e. - * - * @return The deleted element's index or -1 if the element was not found. - */ - public int removeFirst(short e); - - /** - * Removes the last element that equals e. - * - * @return The deleted element's index or -1 if the element was not found. - */ - public int removeLast(short e); - - /** Inserts the specified element at the front of this deque. */ - public void addFirst(short e); - - /** Inserts the specified element at the end of this deque. */ - public void addLast(short e); - - /** - * Retrieves and removes the first element of this deque. - * - * @return the head (first) element of this deque. - */ - public short removeFirst(); - - /** - * Retrieves and removes the last element of this deque. - * - * @return the tail of this deque. - */ - public short removeLast(); - - /** - * Retrieves the first element of this deque but does not remove it. - * - * @return the head of this deque. - */ - public short getFirst(); - - /** - * Retrieves the last element of this deque but does not remove it. - * - * @return the head of this deque. - */ - public short getLast(); - - /** - * @return An iterator over elements in this deque in tail-to-head order. - */ - public Iterator descendingIterator(); - - /** Applies a procedure to all elements in tail-to-head order. */ - public T descendingForEach(T procedure); - - /** - * Applies a predicate to container elements as long, as the predicate returns - * true. The iteration is interrupted otherwise. - */ - public T descendingForEach(T predicate); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortDoubleAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/ShortDoubleAssociativeContainer.java deleted file mode 100755 index 0155b8a3..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortDoubleAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see ShortContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface ShortDoubleAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *
-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(short key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortDoublePredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ShortDoubleProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ShortDoublePredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public ShortCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public DoubleContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortDoubleHashMap.java b/src/main/java/com/carrotsearch/hppc/ShortDoubleHashMap.java deleted file mode 100755 index 4bf50582..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortDoubleHashMap.java +++ /dev/null @@ -1,1082 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of short to double, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class ShortDoubleHashMap implements ShortDoubleMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public short[] keys; - - /** The array holding values. */ - public double[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public ShortDoubleHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ShortDoubleHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ShortDoubleHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ShortDoubleHashMap(ShortDoubleAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public double put(short key, double value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - double previousValue = hasEmptyKey ? values[mask + 1] : 0d; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final double previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0d; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(ShortDoubleAssociativeContainer container) { - final int count = size(); - for (ShortDoubleCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (ShortDoubleCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public double putOrAdd(short key, double putValue, double incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((double) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public double addTo(short key, double incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public double remove(short key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return 0d; - } - hasEmptyKey = false; - double previousValue = values[mask + 1]; - values[mask + 1] = 0d; - return previousValue; - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final double previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0d; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof ShortLookupContainer) { - if (hasEmptyKey && other.contains(((short) 0))) { - hasEmptyKey = false; - values[mask + 1] = 0d; - } - - final short[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - short existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (ShortCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortDoublePredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(((short) 0), values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0d; - } - } - - final short[] keys = this.keys; - final double[] values = this.values; - for (int slot = 0; slot <= mask; ) { - short existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(((short) 0))) { - hasEmptyKey = false; - values[mask + 1] = 0d; - } - } - - final short[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - short existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public double get(short key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : 0d; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0d; - } - } - - /** {@inheritDoc} */ - @Override - public double getOrDefault(short key, double defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(short key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(short key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public double indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public double indexReplace(int index, double newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - double previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, short key, double value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public double indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - double previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0d; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, ((short) 0)); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (ShortDoubleCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(ShortDoubleHashMap other) { - if (other.size() != size()) { - return false; - } - - for (ShortDoubleCursor c : other) { - short key = c.key; - if (!containsKey(key) - || !(Double.doubleToLongBits(c.value) == Double.doubleToLongBits(get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final short[] prevKeys = this.keys; - final double[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final ShortDoubleCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new ShortDoubleCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortDoubleCursor fetch() { - final int mask = ShortDoubleHashMap.this.mask; - while (index <= mask) { - short existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = ((short) 0); - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final short[] keys = this.keys; - final double[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(((short) 0), values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final short[] keys = this.keys; - final double[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(((short) 0), values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractShortCollection implements ShortLookupContainer { - private final ShortDoubleHashMap owner = ShortDoubleHashMap.this; - - @Override - public boolean contains(short e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((ShortDoubleProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((ShortDoublePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final short e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final ShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new ShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortCursor fetch() { - final int mask = ShortDoubleHashMap.this.mask; - while (index <= mask) { - short existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = ((short) 0); - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public DoubleCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractDoubleCollection { - private final ShortDoubleHashMap owner = ShortDoubleHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(double value) { - for (ShortDoubleCursor c : owner) { - if ((Double.doubleToLongBits(value) == Double.doubleToLongBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (ShortDoubleCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (ShortDoubleCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final double e) { - return owner.removeAll( - (key, value) -> (Double.doubleToLongBits(e) == Double.doubleToLongBits(value))); - } - - @Override - public int removeAll(final DoublePredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final DoubleCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new DoubleCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected DoubleCursor fetch() { - final int mask = ShortDoubleHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public ShortDoubleHashMap clone() { - try { - - ShortDoubleHashMap cloned = (ShortDoubleHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (ShortDoubleCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return ShortBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ShortDoubleHashMap from(short[] keys, double[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ShortDoubleHashMap map = new ShortDoubleHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(short key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(short[] fromKeys, double[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final short[] keys = this.keys; - final double[] values = this.values; - final int mask = this.mask; - short existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - short[] prevKeys = this.keys; - double[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new short[arraySize + emptyElementSlot]); - this.values = (new double[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, short pendingKey, double pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final short[] prevKeys = this.keys; - final double[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final short[] keys = this.keys; - final double[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final short existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = ((short) 0); - values[gapSlot] = 0d; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortDoubleMap.java b/src/main/java/com/carrotsearch/hppc/ShortDoubleMap.java deleted file mode 100755 index 8d3ac5f8..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortDoubleMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ShortDoubleCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface ShortDoubleMap extends ShortDoubleAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public double get(short key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public double getOrDefault(short key, double defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public double put(short key, double value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(short key, double value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(ShortDoubleAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public double putOrAdd(short key, double putValue, double incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public double addTo(short key, double additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public double remove(short key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link ShortDoubleMap} and both objects contains exactly the - * same key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(short key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public double indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public double indexReplace(int index, double newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, short key, double value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public double indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortFloatAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/ShortFloatAssociativeContainer.java deleted file mode 100755 index f6a2bf72..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortFloatAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see ShortContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface ShortFloatAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(short key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortFloatPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ShortFloatProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ShortFloatPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public ShortCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public FloatContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortFloatHashMap.java b/src/main/java/com/carrotsearch/hppc/ShortFloatHashMap.java deleted file mode 100755 index 9383ebab..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortFloatHashMap.java +++ /dev/null @@ -1,1081 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of short to float, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class ShortFloatHashMap implements ShortFloatMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public short[] keys; - - /** The array holding values. */ - public float[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public ShortFloatHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ShortFloatHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ShortFloatHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ShortFloatHashMap(ShortFloatAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public float put(short key, float value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - float previousValue = hasEmptyKey ? values[mask + 1] : 0f; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final float previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0f; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(ShortFloatAssociativeContainer container) { - final int count = size(); - for (ShortFloatCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (ShortFloatCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public float putOrAdd(short key, float putValue, float incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((float) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public float addTo(short key, float incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public float remove(short key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return 0f; - } - hasEmptyKey = false; - float previousValue = values[mask + 1]; - values[mask + 1] = 0f; - return previousValue; - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final float previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0f; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof ShortLookupContainer) { - if (hasEmptyKey && other.contains(((short) 0))) { - hasEmptyKey = false; - values[mask + 1] = 0f; - } - - final short[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - short existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (ShortCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortFloatPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(((short) 0), values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0f; - } - } - - final short[] keys = this.keys; - final float[] values = this.values; - for (int slot = 0; slot <= mask; ) { - short existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(((short) 0))) { - hasEmptyKey = false; - values[mask + 1] = 0f; - } - } - - final short[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - short existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public float get(short key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : 0f; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0f; - } - } - - /** {@inheritDoc} */ - @Override - public float getOrDefault(short key, float defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(short key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(short key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public float indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public float indexReplace(int index, float newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - float previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, short key, float value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public float indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - float previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0f; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, ((short) 0)); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (ShortFloatCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(ShortFloatHashMap other) { - if (other.size() != size()) { - return false; - } - - for (ShortFloatCursor c : other) { - short key = c.key; - if (!containsKey(key) || !(Float.floatToIntBits(c.value) == Float.floatToIntBits(get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final short[] prevKeys = this.keys; - final float[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final ShortFloatCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new ShortFloatCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortFloatCursor fetch() { - final int mask = ShortFloatHashMap.this.mask; - while (index <= mask) { - short existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = ((short) 0); - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final short[] keys = this.keys; - final float[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(((short) 0), values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final short[] keys = this.keys; - final float[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(((short) 0), values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractShortCollection implements ShortLookupContainer { - private final ShortFloatHashMap owner = ShortFloatHashMap.this; - - @Override - public boolean contains(short e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((ShortFloatProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((ShortFloatPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final short e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final ShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new ShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortCursor fetch() { - final int mask = ShortFloatHashMap.this.mask; - while (index <= mask) { - short existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = ((short) 0); - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public FloatCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractFloatCollection { - private final ShortFloatHashMap owner = ShortFloatHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(float value) { - for (ShortFloatCursor c : owner) { - if ((Float.floatToIntBits(value) == Float.floatToIntBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (ShortFloatCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (ShortFloatCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final float e) { - return owner.removeAll( - (key, value) -> (Float.floatToIntBits(e) == Float.floatToIntBits(value))); - } - - @Override - public int removeAll(final FloatPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final FloatCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new FloatCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected FloatCursor fetch() { - final int mask = ShortFloatHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public ShortFloatHashMap clone() { - try { - - ShortFloatHashMap cloned = (ShortFloatHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (ShortFloatCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return ShortBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ShortFloatHashMap from(short[] keys, float[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ShortFloatHashMap map = new ShortFloatHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(short key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(short[] fromKeys, float[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final short[] keys = this.keys; - final float[] values = this.values; - final int mask = this.mask; - short existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - short[] prevKeys = this.keys; - float[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new short[arraySize + emptyElementSlot]); - this.values = (new float[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, short pendingKey, float pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final short[] prevKeys = this.keys; - final float[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final short[] keys = this.keys; - final float[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final short existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = ((short) 0); - values[gapSlot] = 0f; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortFloatMap.java b/src/main/java/com/carrotsearch/hppc/ShortFloatMap.java deleted file mode 100755 index d09dac18..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortFloatMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ShortFloatCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface ShortFloatMap extends ShortFloatAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public float get(short key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public float getOrDefault(short key, float defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public float put(short key, float value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(short key, float value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(ShortFloatAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public float putOrAdd(short key, float putValue, float incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public float addTo(short key, float additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public float remove(short key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link ShortFloatMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(short key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public float indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public float indexReplace(int index, float newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, short key, float value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public float indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortHashSet.java b/src/main/java/com/carrotsearch/hppc/ShortHashSet.java deleted file mode 100755 index f2f35006..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortHashSet.java +++ /dev/null @@ -1,787 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash set of shorts, implemented using open addressing with linear probing for - * collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypeHashSet.java") -public class ShortHashSet extends AbstractShortCollection - implements ShortLookupContainer, ShortSet, Preallocable, Cloneable, Accountable { - /** The hash array holding keys. */ - public short[] keys; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any. - * - * @see #size() - * @see #hasEmptyKey - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** - * New instance with sane defaults. - * - * @see #ShortHashSet(int, double) - */ - public ShortHashSet() { - this(DEFAULT_EXPECTED_ELEMENTS, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with sane defaults. - * - * @see #ShortHashSet(int, double) - */ - public ShortHashSet(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ShortHashSet(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** New instance copying elements from another {@link ShortContainer}. */ - public ShortHashSet(ShortContainer container) { - this(container.size()); - addAll(container); - } - - /** {@inheritDoc} */ - @Override - public boolean add(short key) { - if (((key) == 0)) { - assert ((keys[mask + 1]) == 0); - boolean added = !hasEmptyKey; - hasEmptyKey = true; - return added; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return false; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key); - } else { - keys[slot] = key; - } - - assigned++; - return true; - } - } - - /** - * Adds all elements from the given list (vararg) to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - */ - public final int addAll(short... elements) { - ensureCapacity(elements.length); - int count = 0; - for (short e : elements) { - if (add(e)) { - count++; - } - } - return count; - } - - /** - * Adds all elements from the given {@link ShortContainer} to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - */ - public int addAll(ShortContainer container) { - ensureCapacity(container.size()); - return addAll((Iterable) container); - } - - /** - * Adds all elements from the given iterable to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - */ - public int addAll(Iterable iterable) { - int count = 0; - for (ShortCursor cursor : iterable) { - if (add(cursor.value)) { - count++; - } - } - return count; - } - - /** {@inheritDoc} */ - @Override - public short[] toArray() { - - final short[] cloned = (new short[size()]); - int j = 0; - if (hasEmptyKey) { - cloned[j++] = ((short) 0); - } - - final short[] keys = this.keys; - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - short existing; - if (!((existing = keys[slot]) == 0)) { - cloned[j++] = existing; - } - } - - return cloned; - } - - /** An alias for the (preferred) {@link #removeAll}. */ - public boolean remove(short key) { - if (((key) == 0)) { - boolean hadEmptyKey = hasEmptyKey; - hasEmptyKey = false; - return hadEmptyKey; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - shiftConflictingKeys(slot); - return true; - } - slot = (slot + 1) & mask; - } - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(short key) { - return remove(key) ? 1 : 0; - } - - /** - * Removes all keys present in a given container. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortContainer other) { - final int before = size(); - - // Try to iterate over the smaller set or over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof ShortLookupContainer) { - if (hasEmptyKey && other.contains(((short) 0))) { - hasEmptyKey = false; - } - - final short[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - short existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (ShortCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortPredicate predicate) { - int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(((short) 0))) { - hasEmptyKey = false; - } - } - - final short[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - short existing; - if (!((existing = keys[slot]) == 0)) { - if (predicate.apply(existing)) { - shiftConflictingKeys(slot); - continue; // Repeat the check for the same slot i (shifted). - } - } - slot++; - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public boolean contains(short key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - return false; - } - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - Arrays.fill(keys, ((short) 0)); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - keys = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public boolean isEmpty() { - return size() == 0; - } - - /** - * Ensure this container can hold at least the given number of elements without resizing its - * buffers. - * - * @param expectedElements The total number of elements, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final short[] prevKeys = this.keys; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys); - } - } - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - final short[] keys = this.keys; - for (int slot = mask; slot >= 0; slot--) { - short existing; - if (!((existing = keys[slot]) == 0)) { - h += BitMixer.mix(existing); - } - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && sameKeys(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - private boolean sameKeys(ShortSet other) { - if (other.size() != size()) { - return false; - } - - for (ShortCursor c : other) { - if (!contains(c.value)) { - return false; - } - } - - return true; - } - - /** {@inheritDoc} */ - @Override - public ShortHashSet clone() { - try { - - ShortHashSet cloned = (ShortHashSet) super.clone(); - cloned.keys = keys.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - @Override - public long ramBytesAllocated() { - // int: assigned, mask, keyMixer, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys); - } - - @Override - public long ramBytesUsed() { - // int: assigned, mask, keyMixer, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - protected final class EntryIterator extends AbstractIterator { - private final ShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new ShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortCursor fetch() { - final int mask = ShortHashSet.this.mask; - while (index <= mask) { - short existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = ((short) 0); - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - if (hasEmptyKey) { - procedure.apply(((short) 0)); - } - - final short[] keys = this.keys; - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - short existing; - if (!((existing = keys[slot]) == 0)) { - procedure.apply(existing); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - if (hasEmptyKey) { - if (!predicate.apply(((short) 0))) { - return predicate; - } - } - - final short[] keys = this.keys; - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - short existing; - if (!((existing = keys[slot]) == 0)) { - if (!predicate.apply(existing)) { - break; - } - } - } - - return predicate; - } - - /** - * Create a set from a variable number of arguments or an array of short. The - * elements are copied from the argument to the internal buffer. - */ - public static ShortHashSet from(short... elements) { - final ShortHashSet set = new ShortHashSet(elements.length); - set.addAll(elements); - return set; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(short key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up logic in - * certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between modifications (it will not be affected by read-only - * operations). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the set. - * @return A non-negative value of the logical "index" of the key in the set or a negative value - * if the key did not exist. - */ - public int indexOf(short key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index) { - assert index < 0 || index <= mask || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** - * Returns the exact value of the existing key. This method makes sense for sets of objects which - * define custom key-equality relationship. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the equivalent key currently stored in the set. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public short indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return keys[index]; - } - - /** - * Replaces the existing equivalent key with the given one and returns any previous value stored - * for that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @param equivalentKey The key to put in the set as a replacement. Must be equivalent to the key - * currently stored at the provided index. - * @return Returns the previous key stored in the set. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public short indexReplace(int index, short equivalentKey) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - assert ((keys[index]) == (equivalentKey)); - - short previousValue = keys[index]; - keys[index] = equivalentKey; - return previousValue; - } - - /** - * Inserts a key for an index that is not present in the set. This method may help in avoiding - * double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public void indexInsert(int index, short key) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - assert ((keys[index]) == 0); - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key); - } else { - keys[index] = key; - } - - assigned++; - } - } - - /** - * Removes a key at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public void indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - if (index > mask) { - hasEmptyKey = false; - } else { - shiftConflictingKeys(index); - } - } - - @Override - public String visualizeKeyDistribution(int characters) { - return ShortBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(short[] fromKeys) { - assert HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored keys into the new buffers. - final short[] keys = this.keys; - final int mask = this.mask; - short existing; - for (int i = fromKeys.length - 1; --i >= 0; ) { - if (!((existing = fromKeys[i]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - short[] prevKeys = this.keys; - try { - int emptyElementSlot = 1; - this.keys = (new short[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.keys == null ? 0 : size(), arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key to be inserted into the buffer but there is not - * enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, short pendingKey) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final short[] prevKeys = this.keys; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - - // Rehash old keys, including the pending key. - rehash(prevKeys); - } - - /** Shift all the slot-conflicting keys allocated to (and including) slot. */ - protected void shiftConflictingKeys(int gapSlot) { - final short[] keys = this.keys; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final short existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = ((short) 0); - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortIndexedContainer.java b/src/main/java/com/carrotsearch/hppc/ShortIndexedContainer.java deleted file mode 100755 index fc7a41cf..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortIndexedContainer.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.carrotsearch.hppc; - -import java.util.RandomAccess; - -/** - * An indexed container provides random access to elements based on an index. Indexes - * are zero-based. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeIndexedContainer.java") -public interface ShortIndexedContainer extends ShortCollection, RandomAccess { - /** - * Removes the first element that equals e1, returning whether an element has been - * removed. - */ - public boolean removeElement(short e1); - - /** - * Removes the first element that equals e1, returning its deleted position or - * -1 if the element was not found. - */ - public int removeFirst(short e1); - - /** - * Removes the last element that equals e1, returning its deleted position or - * -1 if the element was not found. - */ - public int removeLast(short e1); - - /** - * Returns the index of the first occurrence of the specified element in this list, or -1 if this - * list does not contain the element. - */ - public int indexOf(short e1); - - /** - * Returns the index of the last occurrence of the specified element in this list, or -1 if this - * list does not contain the element. - */ - public int lastIndexOf(short e1); - - /** Adds an element to the end of this container (the last index is incremented by one). */ - public void add(short e1); - - /** - * Inserts the specified element at the specified position in this list. - * - * @param index The index at which the element should be inserted, shifting any existing and - * subsequent elements to the right. - */ - public void insert(int index, short e1); - - /** - * Replaces the element at the specified position in this list with the specified element. - * - * @return Returns the previous value in the list. - */ - public short set(int index, short e1); - - /** - * @return Returns the element at index index from the list. - */ - public short get(int index); - - /** - * Removes the element at the specified position in this container and returns it. - * - * @see #removeFirst - * @see #removeLast - * @see #removeAll - */ - public short removeAt(int index); - - /** Removes and returns the last element of this container. This container must not be empty. */ - public short removeLast(); - - /** - * Removes from this container all of the elements with indexes between fromIndex, - * inclusive, and toIndex, exclusive. - */ - public void removeRange(int fromIndex, int toIndex); - - /** Returns this container elements as a stream. */ - - /** Sorts the elements in this container and returns this container. */ - public ShortIndexedContainer sort(); - - /** Reverses the elements in this container and returns this container. */ - public ShortIndexedContainer reverse(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortIntAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/ShortIntAssociativeContainer.java deleted file mode 100755 index 07b4a19b..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortIntAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see ShortContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface ShortIntAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(short key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortIntPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ShortIntProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ShortIntPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public ShortCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public IntContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortIntHashMap.java b/src/main/java/com/carrotsearch/hppc/ShortIntHashMap.java deleted file mode 100755 index 07bea9e2..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortIntHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of short to int, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class ShortIntHashMap implements ShortIntMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public short[] keys; - - /** The array holding values. */ - public int[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public ShortIntHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ShortIntHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ShortIntHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ShortIntHashMap(ShortIntAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public int put(short key, int value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - int previousValue = hasEmptyKey ? values[mask + 1] : 0; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final int previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(ShortIntAssociativeContainer container) { - final int count = size(); - for (ShortIntCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (ShortIntCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public int putOrAdd(short key, int putValue, int incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((int) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public int addTo(short key, int incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public int remove(short key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return 0; - } - hasEmptyKey = false; - int previousValue = values[mask + 1]; - values[mask + 1] = 0; - return previousValue; - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final int previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof ShortLookupContainer) { - if (hasEmptyKey && other.contains(((short) 0))) { - hasEmptyKey = false; - values[mask + 1] = 0; - } - - final short[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - short existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (ShortCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortIntPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(((short) 0), values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0; - } - } - - final short[] keys = this.keys; - final int[] values = this.values; - for (int slot = 0; slot <= mask; ) { - short existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(((short) 0))) { - hasEmptyKey = false; - values[mask + 1] = 0; - } - } - - final short[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - short existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int get(short key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : 0; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0; - } - } - - /** {@inheritDoc} */ - @Override - public int getOrDefault(short key, int defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(short key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(short key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public int indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public int indexReplace(int index, int newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - int previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, short key, int value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public int indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - int previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, ((short) 0)); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (ShortIntCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(ShortIntHashMap other) { - if (other.size() != size()) { - return false; - } - - for (ShortIntCursor c : other) { - short key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final short[] prevKeys = this.keys; - final int[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final ShortIntCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new ShortIntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortIntCursor fetch() { - final int mask = ShortIntHashMap.this.mask; - while (index <= mask) { - short existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = ((short) 0); - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final short[] keys = this.keys; - final int[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(((short) 0), values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final short[] keys = this.keys; - final int[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(((short) 0), values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractShortCollection implements ShortLookupContainer { - private final ShortIntHashMap owner = ShortIntHashMap.this; - - @Override - public boolean contains(short e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((ShortIntProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((ShortIntPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final short e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final ShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new ShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortCursor fetch() { - final int mask = ShortIntHashMap.this.mask; - while (index <= mask) { - short existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = ((short) 0); - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public IntCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractIntCollection { - private final ShortIntHashMap owner = ShortIntHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(int value) { - for (ShortIntCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (ShortIntCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (ShortIntCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final int e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final IntPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final IntCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new IntCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected IntCursor fetch() { - final int mask = ShortIntHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public ShortIntHashMap clone() { - try { - - ShortIntHashMap cloned = (ShortIntHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (ShortIntCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return ShortBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ShortIntHashMap from(short[] keys, int[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ShortIntHashMap map = new ShortIntHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(short key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(short[] fromKeys, int[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final short[] keys = this.keys; - final int[] values = this.values; - final int mask = this.mask; - short existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - short[] prevKeys = this.keys; - int[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new short[arraySize + emptyElementSlot]); - this.values = (new int[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, short pendingKey, int pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final short[] prevKeys = this.keys; - final int[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final short[] keys = this.keys; - final int[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final short existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = ((short) 0); - values[gapSlot] = 0; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortIntMap.java b/src/main/java/com/carrotsearch/hppc/ShortIntMap.java deleted file mode 100755 index 78f19f42..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortIntMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ShortIntCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface ShortIntMap extends ShortIntAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public int get(short key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public int getOrDefault(short key, int defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public int put(short key, int value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(short key, int value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(ShortIntAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public int putOrAdd(short key, int putValue, int incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public int addTo(short key, int additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public int remove(short key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link ShortIntMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(short key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public int indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public int indexReplace(int index, int newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, short key, int value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public int indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortLongAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/ShortLongAssociativeContainer.java deleted file mode 100755 index 32375150..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortLongAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see ShortContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface ShortLongAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(short key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortLongPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ShortLongProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ShortLongPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public ShortCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public LongContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortLongHashMap.java b/src/main/java/com/carrotsearch/hppc/ShortLongHashMap.java deleted file mode 100755 index 2fe676e5..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortLongHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of short to long, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class ShortLongHashMap implements ShortLongMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public short[] keys; - - /** The array holding values. */ - public long[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public ShortLongHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ShortLongHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ShortLongHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ShortLongHashMap(ShortLongAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public long put(short key, long value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - long previousValue = hasEmptyKey ? values[mask + 1] : 0L; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final long previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return 0L; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(ShortLongAssociativeContainer container) { - final int count = size(); - for (ShortLongCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (ShortLongCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public long putOrAdd(short key, long putValue, long incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((long) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public long addTo(short key, long incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public long remove(short key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return 0L; - } - hasEmptyKey = false; - long previousValue = values[mask + 1]; - values[mask + 1] = 0L; - return previousValue; - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final long previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return 0L; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof ShortLookupContainer) { - if (hasEmptyKey && other.contains(((short) 0))) { - hasEmptyKey = false; - values[mask + 1] = 0L; - } - - final short[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - short existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (ShortCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortLongPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(((short) 0), values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = 0L; - } - } - - final short[] keys = this.keys; - final long[] values = this.values; - for (int slot = 0; slot <= mask; ) { - short existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(((short) 0))) { - hasEmptyKey = false; - values[mask + 1] = 0L; - } - } - - final short[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - short existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public long get(short key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : 0L; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return 0L; - } - } - - /** {@inheritDoc} */ - @Override - public long getOrDefault(short key, long defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(short key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(short key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public long indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public long indexReplace(int index, long newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - long previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, short key, long value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public long indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - long previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = 0L; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, ((short) 0)); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (ShortLongCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(ShortLongHashMap other) { - if (other.size() != size()) { - return false; - } - - for (ShortLongCursor c : other) { - short key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final short[] prevKeys = this.keys; - final long[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final ShortLongCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new ShortLongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortLongCursor fetch() { - final int mask = ShortLongHashMap.this.mask; - while (index <= mask) { - short existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = ((short) 0); - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final short[] keys = this.keys; - final long[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(((short) 0), values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final short[] keys = this.keys; - final long[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(((short) 0), values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractShortCollection implements ShortLookupContainer { - private final ShortLongHashMap owner = ShortLongHashMap.this; - - @Override - public boolean contains(short e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((ShortLongProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((ShortLongPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final short e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final ShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new ShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortCursor fetch() { - final int mask = ShortLongHashMap.this.mask; - while (index <= mask) { - short existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = ((short) 0); - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public LongCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractLongCollection { - private final ShortLongHashMap owner = ShortLongHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(long value) { - for (ShortLongCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (ShortLongCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (ShortLongCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final long e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final LongPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final LongCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new LongCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected LongCursor fetch() { - final int mask = ShortLongHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public ShortLongHashMap clone() { - try { - - ShortLongHashMap cloned = (ShortLongHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (ShortLongCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return ShortBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ShortLongHashMap from(short[] keys, long[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ShortLongHashMap map = new ShortLongHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(short key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(short[] fromKeys, long[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final short[] keys = this.keys; - final long[] values = this.values; - final int mask = this.mask; - short existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - short[] prevKeys = this.keys; - long[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new short[arraySize + emptyElementSlot]); - this.values = (new long[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, short pendingKey, long pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final short[] prevKeys = this.keys; - final long[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final short[] keys = this.keys; - final long[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final short existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = ((short) 0); - values[gapSlot] = 0L; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortLongMap.java b/src/main/java/com/carrotsearch/hppc/ShortLongMap.java deleted file mode 100755 index 682a924f..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortLongMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ShortLongCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface ShortLongMap extends ShortLongAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public long get(short key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public long getOrDefault(short key, long defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public long put(short key, long value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(short key, long value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(ShortLongAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public long putOrAdd(short key, long putValue, long incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public long addTo(short key, long additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public long remove(short key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link ShortLongMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(short key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public long indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public long indexReplace(int index, long newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, short key, long value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public long indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortLookupContainer.java b/src/main/java/com/carrotsearch/hppc/ShortLookupContainer.java deleted file mode 100755 index c049ef8f..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortLookupContainer.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.carrotsearch.hppc; - -/** - * Marker interface for containers that can check if they contain a given object in at least time - * O(log n) and ideally in amortized constant time O(1). - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeLookupContainer.java") -public interface ShortLookupContainer extends ShortContainer { - public boolean contains(short e); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortObjectAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/ShortObjectAssociativeContainer.java deleted file mode 100755 index 0c3b670b..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortObjectAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see ShortContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface ShortObjectAssociativeContainer extends Iterable> { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator> iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(short key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortObjectPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ShortObjectProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public > T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ShortObjectPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public > T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public ShortCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public ObjectContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortObjectHashMap.java b/src/main/java/com/carrotsearch/hppc/ShortObjectHashMap.java deleted file mode 100755 index 6b8a20b0..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortObjectHashMap.java +++ /dev/null @@ -1,1050 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of short to Object, implemented using open addressing with - * linear probing for collision resolution. Supports null values. - * - * @see HPPC interfaces diagram - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class ShortObjectHashMap - implements ShortObjectMap, Preallocable, Cloneable, Accountable { - /** The array holding keys. */ - public short[] keys; - - /** The array holding values. */ - public Object[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public ShortObjectHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ShortObjectHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ShortObjectHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ShortObjectHashMap(ShortObjectAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public VType put(short key, VType value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - VType previousValue = hasEmptyKey ? (VType) values[mask + 1] : null; - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final VType previousValue = (VType) values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return null; - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(ShortObjectAssociativeContainer container) { - final int count = size(); - for (ShortObjectCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable> iterable) { - final int count = size(); - for (ShortObjectCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** {@inheritDoc} */ - @Override - public VType remove(short key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return null; - } - hasEmptyKey = false; - VType previousValue = (VType) values[mask + 1]; - values[mask + 1] = null; - return previousValue; - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final VType previousValue = (VType) values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return null; - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof ShortLookupContainer) { - if (hasEmptyKey && other.contains(((short) 0))) { - hasEmptyKey = false; - values[mask + 1] = null; - } - - final short[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - short existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (ShortCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortObjectPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(((short) 0), (VType) values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = null; - } - } - - final short[] keys = this.keys; - final VType[] values = (VType[]) this.values; - for (int slot = 0; slot <= mask; ) { - short existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(((short) 0))) { - hasEmptyKey = false; - values[mask + 1] = null; - } - } - - final short[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - short existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public VType get(short key) { - if (((key) == 0)) { - return hasEmptyKey ? (VType) values[mask + 1] : null; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return (VType) values[slot]; - } - slot = (slot + 1) & mask; - } - - return null; - } - } - - /** {@inheritDoc} */ - @Override - public VType getOrDefault(short key, VType defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? (VType) values[mask + 1] : defaultValue; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return (VType) values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(short key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(short key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public VType indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return (VType) values[index]; - } - - /** {@inheritDoc} */ - @Override - public VType indexReplace(int index, VType newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - VType previousValue = (VType) values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, short key, VType value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public VType indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - VType previousValue = (VType) values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = null; - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, ((short) 0)); - - Arrays.fill(values, null); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (ShortObjectCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** - * Return true if all keys of some other container exist in this container. Values are compared - * using {@link Objects#equals(Object)} method. - */ - protected boolean equalElements(ShortObjectHashMap other) { - if (other.size() != size()) { - return false; - } - - for (ShortObjectCursor c : other) { - short key = c.key; - if (!containsKey(key) || !java.util.Objects.equals(c.value, get(key))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final short[] prevKeys = this.keys; - final VType[] prevValues = (VType[]) this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final ShortObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new ShortObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortObjectCursor fetch() { - final int mask = ShortObjectHashMap.this.mask; - while (index <= mask) { - short existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = (VType) values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = ((short) 0); - cursor.value = (VType) values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator> iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T procedure) { - final short[] keys = this.keys; - final VType[] values = (VType[]) this.values; - - if (hasEmptyKey) { - procedure.apply(((short) 0), (VType) values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public > T forEach(T predicate) { - final short[] keys = this.keys; - final VType[] values = (VType[]) this.values; - - if (hasEmptyKey) { - if (!predicate.apply(((short) 0), (VType) values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractShortCollection implements ShortLookupContainer { - private final ShortObjectHashMap owner = ShortObjectHashMap.this; - - @Override - public boolean contains(short e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((ShortObjectProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((ShortObjectPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final short e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final ShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new ShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortCursor fetch() { - final int mask = ShortObjectHashMap.this.mask; - while (index <= mask) { - short existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = ((short) 0); - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public ObjectCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractObjectCollection { - private final ShortObjectHashMap owner = ShortObjectHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(VType value) { - for (ShortObjectCursor c : owner) { - if (java.util.Objects.equals(value, c.value)) { - return true; - } - } - return false; - } - - @Override - public > T forEach(T procedure) { - for (ShortObjectCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public > T forEach(T predicate) { - for (ShortObjectCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator> iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final VType e) { - return owner.removeAll((key, value) -> java.util.Objects.equals(e, value)); - } - - @Override - public int removeAll(final ObjectPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator> { - private final ObjectCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new ObjectCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ObjectCursor fetch() { - final int mask = ShortObjectHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = (VType) values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = (VType) values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public ShortObjectHashMap clone() { - try { - - ShortObjectHashMap cloned = (ShortObjectHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (ShortObjectCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return ShortBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ShortObjectHashMap from(short[] keys, VType[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ShortObjectHashMap map = new ShortObjectHashMap<>(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(short key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(short[] fromKeys, VType[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final short[] keys = this.keys; - final VType[] values = (VType[]) this.values; - final int mask = this.mask; - short existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - short[] prevKeys = this.keys; - VType[] prevValues = (VType[]) this.values; - try { - int emptyElementSlot = 1; - this.keys = (new short[arraySize + emptyElementSlot]); - this.values = ((VType[]) new Object[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, short pendingKey, VType pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final short[] prevKeys = this.keys; - final VType[] prevValues = (VType[]) this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final short[] keys = this.keys; - final VType[] values = (VType[]) this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final short existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = ((short) 0); - values[gapSlot] = null; - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortObjectMap.java b/src/main/java/com/carrotsearch/hppc/ShortObjectMap.java deleted file mode 100755 index 732c57e7..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortObjectMap.java +++ /dev/null @@ -1,181 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ShortObjectCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface ShortObjectMap extends ShortObjectAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public VType get(short key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public VType getOrDefault(short key, VType defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public VType put(short key, VType value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(short key, VType value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(ShortObjectAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable> iterable); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public VType remove(short key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link ShortObjectMap} and both objects contains exactly the - * same key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(short key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public VType indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public VType indexReplace(int index, VType newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, short key, VType value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public VType indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortSet.java b/src/main/java/com/carrotsearch/hppc/ShortSet.java deleted file mode 100755 index f87250b4..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortSet.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.carrotsearch.hppc; - -/** A set of shorts. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeSet.java") -public interface ShortSet extends ShortCollection { - /** - * Adds k to the set. - * - * @return Returns true if this element was not part of the set before. Returns - * false if an equal element is already part of the set, does not replace the - * existing element with the argument. - */ - public boolean add(short k); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); - - /** - * Adds all elements from the given {@link ShortContainer} to this set. - * - * @return Returns the number of elements actually added as a result of this call (not previously - * present in the set). - * @since 0.9.1 - */ - public int addAll(ShortContainer container); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortShortAssociativeContainer.java b/src/main/java/com/carrotsearch/hppc/ShortShortAssociativeContainer.java deleted file mode 100755 index e1adb957..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortShortAssociativeContainer.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.Iterator; - -/** - * An associative container from keys to (one or possibly more) values. - * - * @see ShortContainer - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeAssociativeContainer.java") -public interface ShortShortAssociativeContainer extends Iterable { - /** - * Returns a cursor over the entries (key-value pairs) in this map. The iterator is implemented as - * a cursor and it returns the same cursor instance on every call to {@link - * Iterator#next()}. To read the current key and value use the cursor's public fields. An example - * is shown below. - * - *

-   * for (IntShortCursor c : intShortMap) {
-   *   System.out.println("index=" + c.index + " key=" + c.key + " value=" + c.value);
-   * }
- * - *

The index field inside the cursor gives the internal index inside the - * container's implementation. The interpretation of this index depends on to the container. - */ - @Override - public Iterator iterator(); - - /** - * Returns true if this container has an association to a value for the given key. - */ - public boolean containsKey(short key); - - /** - * @return Returns the current size (number of assigned keys) in the container. - */ - public int size(); - - /** - * @return Return true if this hash map contains no assigned keys. - */ - public boolean isEmpty(); - - /** - * Removes all keys (and associated values) present in a given container. An alias to: - * - *

-   * keys().removeAll(container)
-   * 
- * - * but with no additional overhead. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortContainer container); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortPredicate predicate); - - /** - * Removes all keys (and associated values) for which the predicate returns true. - * - * @return Returns the number of elements actually removed as a result of this call. - */ - public int removeAll(ShortShortPredicate predicate); - - /** - * Applies a given procedure to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ShortShortProcedure}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - */ - public T forEach(T procedure); - - /** - * Applies a given predicate to all keys-value pairs in this container. Returns the argument (any - * subclass of {@link ShortShortPredicate}. This lets the caller call methods of the argument by - * chaining the call (even if the argument is an anonymous type) to retrieve computed values. - * - *

The iteration is continued as long as the predicate returns true. - */ - public T forEach(T predicate); - - /** - * Returns a collection of keys of this container. The returned collection is a view over the key - * set and any modifications (if allowed) introduced to the collection will propagate to the - * associative container immediately. - */ - public ShortCollection keys(); - - /** - * Returns a container view of all values present in this container. The returned collection is a - * view over the key set and any modifications (if allowed) introduced to the collection will - * propagate to the associative container immediately. - */ - public ShortContainer values(); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortShortHashMap.java b/src/main/java/com/carrotsearch/hppc/ShortShortHashMap.java deleted file mode 100755 index 589b23c3..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortShortHashMap.java +++ /dev/null @@ -1,1080 +0,0 @@ -package com.carrotsearch.hppc; - -import static com.carrotsearch.hppc.Containers.*; -import static com.carrotsearch.hppc.HashContainers.*; - -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import java.util.*; - -/** - * A hash map of short to short, implemented using open addressing with - * linear probing for collision resolution. - * - * @see HPPC interfaces diagram - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeHashMap.java") -public class ShortShortHashMap implements ShortShortMap, Preallocable, Cloneable, Accountable { - - /** The array holding keys. */ - public short[] keys; - - /** The array holding values. */ - public short[] values; - - /** - * The number of stored keys (assigned key slots), excluding the special "empty" key, if any (use - * {@link #size()} instead). - * - * @see #size() - */ - protected int assigned; - - /** Mask for slot scans in {@link #keys}. */ - protected int mask; - - /** Expand (rehash) {@link #keys} when {@link #assigned} hits this value. */ - protected int resizeAt; - - /** Special treatment for the "empty slot" key marker. */ - protected boolean hasEmptyKey; - - /** The load factor for {@link #keys}. */ - protected double loadFactor; - - /** Seed used to ensure the hash iteration order is different from an iteration to another. */ - protected int iterationSeed; - - /** New instance with sane defaults. */ - public ShortShortHashMap() { - this(DEFAULT_EXPECTED_ELEMENTS); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ShortShortHashMap(int expectedElements) { - this(expectedElements, DEFAULT_LOAD_FACTOR); - } - - /** - * New instance with the provided defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause a rehash - * (inclusive). - * @param loadFactor The load factor for internal buffers. Insane load factors (zero, full - * capacity) are rejected by {@link #verifyLoadFactor(double)}. - */ - public ShortShortHashMap(int expectedElements, double loadFactor) { - this.loadFactor = verifyLoadFactor(loadFactor); - iterationSeed = HashContainers.nextIterationSeed(); - ensureCapacity(expectedElements); - } - - /** Create a hash map from all key-value pairs of another container. */ - public ShortShortHashMap(ShortShortAssociativeContainer container) { - this(container.size()); - putAll(container); - } - - /** {@inheritDoc} */ - @Override - public short put(short key, short value) { - assert assigned < mask + 1; - - final int mask = this.mask; - if (((key) == 0)) { - short previousValue = hasEmptyKey ? values[mask + 1] : ((short) 0); - hasEmptyKey = true; - values[mask + 1] = value; - return previousValue; - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final short previousValue = values[slot]; - values[slot] = value; - return previousValue; - } - slot = (slot + 1) & mask; - } - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(slot, key, value); - } else { - keys[slot] = key; - values[slot] = value; - } - - assigned++; - return ((short) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int putAll(ShortShortAssociativeContainer container) { - final int count = size(); - for (ShortShortCursor c : container) { - put(c.key, c.value); - } - return size() - count; - } - - /** Puts all key/value pairs from a given iterable into this map. */ - @Override - public int putAll(Iterable iterable) { - final int count = size(); - for (ShortShortCursor c : iterable) { - put(c.key, c.value); - } - return size() - count; - } - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public short putOrAdd(short key, short putValue, short incrementValue) { - assert assigned < mask + 1; - - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - putValue = ((short) ((values[keyIndex]) + (incrementValue))); - indexReplace(keyIndex, putValue); - } else { - indexInsert(keyIndex, key, putValue); - } - return putValue; - } - - /** - * Adds incrementValue to any existing value for the given key or - * inserts incrementValue if key did not previously exist. - * - * @param key The key of the value to adjust. - * @param incrementValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - @Override - public short addTo(short key, short incrementValue) { - return putOrAdd(key, incrementValue, incrementValue); - } - - /** {@inheritDoc} */ - @Override - public short remove(short key) { - final int mask = this.mask; - if (((key) == 0)) { - if (!hasEmptyKey) { - return ((short) 0); - } - hasEmptyKey = false; - short previousValue = values[mask + 1]; - values[mask + 1] = ((short) 0); - return previousValue; - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - final short previousValue = values[slot]; - shiftConflictingKeys(slot); - return previousValue; - } - slot = (slot + 1) & mask; - } - - return ((short) 0); - } - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortContainer other) { - final int before = size(); - - // Try to iterate over the smaller set of values or - // over the container that isn't implementing - // efficient contains() lookup. - - if (other.size() >= size() && other instanceof ShortLookupContainer) { - if (hasEmptyKey && other.contains(((short) 0))) { - hasEmptyKey = false; - values[mask + 1] = ((short) 0); - } - - final short[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - short existing; - if (!((existing = keys[slot]) == 0) && other.contains(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - } else { - for (ShortCursor c : other) { - remove(c.value); - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortShortPredicate predicate) { - final int before = size(); - - final int mask = this.mask; - - if (hasEmptyKey) { - if (predicate.apply(((short) 0), values[mask + 1])) { - hasEmptyKey = false; - values[mask + 1] = ((short) 0); - } - } - - final short[] keys = this.keys; - final short[] values = this.values; - for (int slot = 0; slot <= mask; ) { - short existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing, values[slot])) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public int removeAll(ShortPredicate predicate) { - final int before = size(); - - if (hasEmptyKey) { - if (predicate.apply(((short) 0))) { - hasEmptyKey = false; - values[mask + 1] = ((short) 0); - } - } - - final short[] keys = this.keys; - for (int slot = 0, max = this.mask; slot <= max; ) { - short existing; - if (!((existing = keys[slot]) == 0) && predicate.apply(existing)) { - // Shift, do not increment slot. - shiftConflictingKeys(slot); - } else { - slot++; - } - } - - return before - size(); - } - - /** {@inheritDoc} */ - @Override - public short get(short key) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : ((short) 0); - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return ((short) 0); - } - } - - /** {@inheritDoc} */ - @Override - public short getOrDefault(short key, short defaultValue) { - if (((key) == 0)) { - return hasEmptyKey ? values[mask + 1] : defaultValue; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return values[slot]; - } - slot = (slot + 1) & mask; - } - - return defaultValue; - } - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(short key) { - if (((key) == 0)) { - return hasEmptyKey; - } else { - final short[] keys = this.keys; - final int mask = this.mask; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return true; - } - slot = (slot + 1) & mask; - } - - return false; - } - } - - /** {@inheritDoc} */ - @Override - public int indexOf(short key) { - final int mask = this.mask; - if (((key) == 0)) { - return hasEmptyKey ? mask + 1 : ~(mask + 1); - } else { - final short[] keys = this.keys; - int slot = hashKey(key) & mask; - - short existing; - while (!((existing = keys[slot]) == 0)) { - if (((key) == (existing))) { - return slot; - } - slot = (slot + 1) & mask; - } - - return ~slot; - } - } - - /** {@inheritDoc} */ - @Override - public boolean indexExists(int index) { - assert index < 0 || (index >= 0 && index <= mask) || (index == mask + 1 && hasEmptyKey); - - return index >= 0; - } - - /** {@inheritDoc} */ - @Override - public short indexGet(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - return values[index]; - } - - /** {@inheritDoc} */ - @Override - public short indexReplace(int index, short newValue) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - short previousValue = values[index]; - values[index] = newValue; - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void indexInsert(int index, short key, short value) { - assert index < 0 : "The index must not point at an existing key."; - - index = ~index; - if (((key) == 0)) { - assert index == mask + 1; - values[index] = value; - hasEmptyKey = true; - } else { - assert ((keys[index]) == 0); - - if (assigned == resizeAt) { - allocateThenInsertThenRehash(index, key, value); - } else { - keys[index] = key; - values[index] = value; - } - - assigned++; - } - } - - /** {@inheritDoc} */ - @Override - public short indexRemove(int index) { - assert index >= 0 : "The index must point at an existing key."; - assert index <= mask || (index == mask + 1 && hasEmptyKey); - - short previousValue = values[index]; - if (index > mask) { - assert index == mask + 1; - hasEmptyKey = false; - values[index] = ((short) 0); - } else { - shiftConflictingKeys(index); - } - return previousValue; - } - - /** {@inheritDoc} */ - @Override - public void clear() { - assigned = 0; - hasEmptyKey = false; - - Arrays.fill(keys, ((short) 0)); - } - - /** {@inheritDoc} */ - @Override - public void release() { - assigned = 0; - hasEmptyKey = false; - - keys = null; - values = null; - ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS); - } - - /** {@inheritDoc} */ - @Override - public int size() { - return assigned + (hasEmptyKey ? 1 : 0); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return size() == 0; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = hasEmptyKey ? 0xDEADBEEF : 0; - for (ShortShortCursor c : this) { - h += BitMixer.mix(c.key) + BitMixer.mix(c.value); - } - return h; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - return (this == obj) - || (obj != null && getClass() == obj.getClass() && equalElements(getClass().cast(obj))); - } - - /** Return true if all keys of some other container exist in this container. */ - protected boolean equalElements(ShortShortHashMap other) { - if (other.size() != size()) { - return false; - } - - for (ShortShortCursor c : other) { - short key = c.key; - if (!containsKey(key) || !((c.value) == (get(key)))) { - return false; - } - } - - return true; - } - - /** - * Ensure this container can hold at least the given number of keys (entries) without resizing its - * buffers. - * - * @param expectedElements The total number of keys, inclusive. - */ - @Override - public void ensureCapacity(int expectedElements) { - if (expectedElements > resizeAt || keys == null) { - final short[] prevKeys = this.keys; - final short[] prevValues = this.values; - allocateBuffers(minBufferSize(expectedElements, loadFactor)); - if (prevKeys != null && !isEmpty()) { - rehash(prevKeys, prevValues); - } - } - } - - @Override - public long ramBytesAllocated() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowSizeOfArray(keys) - + RamUsageEstimator.shallowSizeOfArray(values); - } - - @Override - public long ramBytesUsed() { - // int: iterationSeed, assigned, mask, resizeAt - // double: loadFactor - // boolean: hasEmptyKey - return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER - + 4 * Integer.BYTES - + Double.BYTES - + 1 - + RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) - + RamUsageEstimator.shallowUsedSizeOfArray(values, size()); - } - - /** - * Provides the next iteration seed used to build the iteration starting slot and offset - * increment. This method does not need to be synchronized, what matters is that each thread gets - * a sequence of varying seeds. - */ - protected int nextIterationSeed() { - return iterationSeed = BitMixer.mixPhi(iterationSeed); - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final ShortShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public EntryIterator() { - cursor = new ShortShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortShortCursor fetch() { - final int mask = ShortShortHashMap.this.mask; - while (index <= mask) { - short existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.key = existing; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.key = ((short) 0); - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public Iterator iterator() { - return new EntryIterator(); - } - - /** {@inheritDoc} */ - @Override - public T forEach(T procedure) { - final short[] keys = this.keys; - final short[] values = this.values; - - if (hasEmptyKey) { - procedure.apply(((short) 0), values[mask + 1]); - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - procedure.apply(keys[slot], values[slot]); - } - } - - return procedure; - } - - /** {@inheritDoc} */ - @Override - public T forEach(T predicate) { - final short[] keys = this.keys; - final short[] values = this.values; - - if (hasEmptyKey) { - if (!predicate.apply(((short) 0), values[mask + 1])) { - return predicate; - } - } - - int seed = nextIterationSeed(); - int inc = iterationIncrement(seed); - for (int i = 0, mask = this.mask, slot = seed & mask; - i <= mask; - i++, slot = (slot + inc) & mask) { - if (!((keys[slot]) == 0)) { - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - } - - return predicate; - } - - /** - * Returns a specialized view of the keys of this associated container. The view additionally - * implements {@link ObjectLookupContainer}. - */ - public KeysContainer keys() { - return new KeysContainer(); - } - - /** A view of the keys inside this hash map. */ - public final class KeysContainer extends AbstractShortCollection implements ShortLookupContainer { - private final ShortShortHashMap owner = ShortShortHashMap.this; - - @Override - public boolean contains(short e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((ShortShortProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((ShortShortPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - return owner.removeAll(predicate); - } - - @Override - public int removeAll(final short e) { - if (owner.containsKey(e)) { - owner.remove(e); - return 1; - } else { - return 0; - } - } - } - ; - - /** An iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final ShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public KeysIterator() { - cursor = new ShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortCursor fetch() { - final int mask = ShortShortHashMap.this.mask; - while (index <= mask) { - short existing; - index++; - slot = (slot + increment) & mask; - if (!((existing = keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = existing; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index++; - cursor.value = ((short) 0); - return cursor; - } - - return done(); - } - } - - /** - * @return Returns a container with all values stored in this map. - */ - @Override - public ShortCollection values() { - return new ValuesContainer(); - } - - /** A view over the set of values of this map. */ - private final class ValuesContainer extends AbstractShortCollection { - private final ShortShortHashMap owner = ShortShortHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(short value) { - for (ShortShortCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - for (ShortShortCursor c : owner) { - procedure.apply(c.value); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - for (ShortShortCursor c : owner) { - if (!predicate.apply(c.value)) { - break; - } - } - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final short e) { - return owner.removeAll((key, value) -> ((e) == (value))); - } - - @Override - public int removeAll(final ShortPredicate predicate) { - return owner.removeAll((key, value) -> predicate.apply(value)); - } - - @Override - public void clear() { - owner.clear(); - } - - @Override - public void release() { - owner.release(); - } - } - - /** An iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ShortCursor cursor; - private final int increment; - private int index; - private int slot; - - public ValuesIterator() { - cursor = new ShortCursor(); - int seed = nextIterationSeed(); - increment = iterationIncrement(seed); - slot = seed & mask; - } - - @Override - protected ShortCursor fetch() { - final int mask = ShortShortHashMap.this.mask; - while (index <= mask) { - index++; - slot = (slot + increment) & mask; - if (!((keys[slot]) == 0)) { - cursor.index = slot; - cursor.value = values[slot]; - return cursor; - } - } - - if (index == mask + 1 && hasEmptyKey) { - cursor.index = index; - cursor.value = values[index++]; - return cursor; - } - - return done(); - } - } - - /** {@inheritDoc} */ - @Override - public ShortShortHashMap clone() { - try { - - ShortShortHashMap cloned = (ShortShortHashMap) super.clone(); - cloned.keys = keys.clone(); - cloned.values = values.clone(); - cloned.hasEmptyKey = hasEmptyKey; - cloned.iterationSeed = HashContainers.nextIterationSeed(); - return cloned; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - /** Convert the contents of this map to a human-friendly string. */ - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(); - buffer.append("["); - - boolean first = true; - for (ShortShortCursor cursor : this) { - if (!first) { - buffer.append(", "); - } - buffer.append(cursor.key); - buffer.append("=>"); - buffer.append(cursor.value); - first = false; - } - buffer.append("]"); - return buffer.toString(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return ShortBufferVisualizer.visualizeKeyDistribution(keys, mask, characters); - } - - /** Creates a hash map from two index-aligned arrays of key-value pairs. */ - public static ShortShortHashMap from(short[] keys, short[] values) { - if (keys.length != values.length) { - throw new IllegalArgumentException( - "Arrays of keys and values must have an identical length."); - } - - ShortShortHashMap map = new ShortShortHashMap(keys.length); - for (int i = 0; i < keys.length; i++) { - map.put(keys[i], values[i]); - } - - return map; - } - - /** - * Returns a hash code for the given key. - * - *

The output from this function should evenly distribute keys across the entire integer range. - */ - protected int hashKey(short key) { - assert !((key) == 0); // Handled as a special case (empty slot marker). - return BitMixer.mixPhi(key); - } - - /** - * Validate load factor range and return it. Override and suppress if you need insane load - * factors. - */ - protected double verifyLoadFactor(double loadFactor) { - checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR); - return loadFactor; - } - - /** Rehash from old buffers to new buffers. */ - protected void rehash(short[] fromKeys, short[] fromValues) { - assert fromKeys.length == fromValues.length - && HashContainers.checkPowerOfTwo(fromKeys.length - 1); - - // Rehash all stored key/value pairs into the new buffers. - final short[] keys = this.keys; - final short[] values = this.values; - final int mask = this.mask; - short existing; - - // Copy the zero element's slot, then rehash everything else. - int from = fromKeys.length - 1; - keys[keys.length - 1] = fromKeys[from]; - values[values.length - 1] = fromValues[from]; - while (--from >= 0) { - if (!((existing = fromKeys[from]) == 0)) { - int slot = hashKey(existing) & mask; - while (!((keys[slot]) == 0)) { - slot = (slot + 1) & mask; - } - keys[slot] = existing; - values[slot] = fromValues[from]; - } - } - } - - /** - * Allocate new internal buffers. This method attempts to allocate and assign internal buffers - * atomically (either allocations succeed or not). - */ - protected void allocateBuffers(int arraySize) { - assert Integer.bitCount(arraySize) == 1; - - // Ensure no change is done if we hit an OOM. - short[] prevKeys = this.keys; - short[] prevValues = this.values; - try { - int emptyElementSlot = 1; - this.keys = (new short[arraySize + emptyElementSlot]); - this.values = (new short[arraySize + emptyElementSlot]); - } catch (OutOfMemoryError e) { - this.keys = prevKeys; - this.values = prevValues; - throw new BufferAllocationException( - "Not enough memory to allocate buffers for rehashing: %,d -> %,d", - e, this.mask + 1, arraySize); - } - - this.resizeAt = expandAtCount(arraySize, loadFactor); - this.mask = arraySize - 1; - } - - /** - * This method is invoked when there is a new key/ value pair to be inserted into the buffers but - * there is not enough empty slots to do so. - * - *

New buffers are allocated. If this succeeds, we know we can proceed with rehashing so we - * assign the pending element to the previous buffer (possibly violating the invariant of having - * at least one empty slot) and rehash all keys, substituting new buffers at the end. - */ - protected void allocateThenInsertThenRehash(int slot, short pendingKey, short pendingValue) { - assert assigned == resizeAt && ((keys[slot]) == 0) && !((pendingKey) == 0); - - // Try to allocate new buffers first. If we OOM, we leave in a consistent state. - final short[] prevKeys = this.keys; - final short[] prevValues = this.values; - allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor)); - assert this.keys.length > prevKeys.length; - - // We have succeeded at allocating new data so insert the pending key/value at - // the free slot in the old arrays before rehashing. - prevKeys[slot] = pendingKey; - prevValues[slot] = pendingValue; - - // Rehash old keys, including the pending key. - rehash(prevKeys, prevValues); - } - - /** - * Shift all the slot-conflicting keys and values allocated to (and including) slot. - */ - protected void shiftConflictingKeys(int gapSlot) { - final short[] keys = this.keys; - final short[] values = this.values; - final int mask = this.mask; - - // Perform shifts of conflicting keys to fill in the gap. - int distance = 0; - while (true) { - final int slot = (gapSlot + (++distance)) & mask; - final short existing = keys[slot]; - if (((existing) == 0)) { - break; - } - - final int idealSlot = hashKey(existing); - final int shift = (slot - idealSlot) & mask; - if (shift >= distance) { - // Entry at this position was originally at or before the gap slot. - // Move the conflict-shifted entry to the gap's position and repeat the procedure - // for any entries to the right of the current position, treating it - // as the new gap. - keys[gapSlot] = existing; - values[gapSlot] = values[slot]; - gapSlot = slot; - distance = 0; - } - } - - // Mark the last found gap slot without a conflict as empty. - keys[gapSlot] = ((short) 0); - values[gapSlot] = ((short) 0); - assigned--; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortShortMap.java b/src/main/java/com/carrotsearch/hppc/ShortShortMap.java deleted file mode 100755 index d90c602e..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortShortMap.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ShortShortCursor; - -/** An associative container with unique binding from keys to a single value. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeMap.java") -public interface ShortShortMap extends ShortShortAssociativeContainer { - /** - * @return Returns the value associated with the given key or the default value for the value - * type, if the key is not associated with any value. For numeric value types, this default - * value is 0, for object types it is {@code null}. - */ - public short get(short key); - - /** - * @return Returns the value associated with the given key or the provided default value if the - * key is not associated with any value. - */ - public short getOrDefault(short key, short defaultValue); - - /** - * Place a given key and value in the container. - * - * @return The value previously stored under the given key in the map is returned. - */ - public short put(short key, short value); - - /** - * If the specified key is not already associated with a value, associates it with the given - * value. - * - * @return {@code true} if {@code key} did not exist and {@code value} was placed in the map, - * {@code false} otherwise. - */ - public default boolean putIfAbsent(short key, short value) { - int keyIndex = indexOf(key); - if (indexExists(keyIndex)) { - return false; - } else { - indexInsert(keyIndex, key, value); - return true; - } - } - - /** - * Puts all keys from another container to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(ShortShortAssociativeContainer container); - - /** - * Puts all keys from an iterable cursor to this map, replacing the values of existing keys, if - * such keys are present. - * - * @return Returns the number of keys added to the map as a result of this call (not previously - * present in the map). Values of existing keys are overwritten. - */ - public int putAll(Iterable iterable); - - /** - * If key exists, putValue is inserted into the map, otherwise any - * existing value is incremented by additionValue. - * - * @param key The key of the value to adjust. - * @param putValue The value to put if key does not exist. - * @param incrementValue The value to add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public short putOrAdd(short key, short putValue, short incrementValue); - - /** - * An equivalent of calling - * - *

-   * putOrAdd(key, additionValue, additionValue);
-   * 
- * - * @param key The key of the value to adjust. - * @param additionValue The value to put or add to the existing value if key exists. - * @return Returns the current value associated with key (after changes). - */ - public short addTo(short key, short additionValue); - - /** - * Remove all values at the given key. The default value for the key type is returned if the value - * does not exist in the map. - */ - public short remove(short key); - - /** - * Compares the specified object with this set for equality. Returns {@code true} if and only if - * the specified object is also a {@link ShortShortMap} and both objects contains exactly the same - * key-value pairs. - */ - public boolean equals(Object obj); - - /** - * @return A hash code of elements stored in the map. The hash code is defined as a sum of hash - * codes of keys and values stored within the set). Because sum is commutative, this ensures - * that different order of elements in a set does not affect the hash code. - */ - public int hashCode(); - - /** - * Returns a logical "index" of a given key that can be used to speed up follow-up value setters - * or getters in certain scenarios (conditional logic). - * - *

The semantics of "indexes" are not strictly defined. Indexes may (and typically won't be) - * contiguous. - * - *

The index is valid only between map modifications (it will not be affected by read-only - * operations like iteration or value retrievals). - * - * @see #indexExists - * @see #indexGet - * @see #indexInsert - * @see #indexReplace - * @param key The key to locate in the map. - * @return A non-negative value of the logical "index" of the key in the map or a negative value - * if the key did not exist. - */ - public int indexOf(short key); - - /** - * @see #indexOf - * @param index The index of a given key, as returned from {@link #indexOf}. - * @return Returns true if the index corresponds to an existing key or false - * otherwise. This is equivalent to checking whether the index is a positive value (existing - * keys) or a negative value (non-existing keys). - */ - public boolean indexExists(int index); - - /** - * Returns the value associated with an existing key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the value currently associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public short indexGet(int index); - - /** - * Replaces the value associated with an existing key and returns any previous value stored for - * that key. - * - * @see #indexOf - * @param index The index of an existing key. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public short indexReplace(int index, short newValue); - - /** - * Inserts a key-value pair for a key that is not present in the map. This method may help in - * avoiding double recalculation of the key's hash. - * - * @see #indexOf - * @param index The index of a previously non-existing key, as returned from {@link #indexOf}. - * @throws AssertionError If assertions are enabled and the index corresponds to an existing key. - */ - public void indexInsert(int index, short key, short value); - - /** - * Removes a key-value pair at an index previously acquired from {@link #indexOf}. - * - * @see #indexOf - * @param index The index of the key to remove, as returned from {@link #indexOf}. - * @return Returns the previous value associated with the key. - * @throws AssertionError If assertions are enabled and the index does not correspond to an - * existing key. - */ - public short indexRemove(int index); - - /** - * Clear all keys and values in the container. - * - * @see #release() - */ - public void clear(); - - /** - * Removes all elements from the collection and additionally releases any internal buffers. - * Typically, if the object is to be reused, a simple {@link #clear()} should be a better - * alternative since it'll avoid reallocation. - * - * @see #clear() - */ - public void release(); - - /** - * Visually depict the distribution of keys. - * - * @param characters The number of characters to "squeeze" the entire buffer into. - * @return Returns a sequence of characters where '.' depicts an empty fragment of the internal - * buffer and 'X' depicts full or nearly full capacity within the buffer's range and anything - * between 1 and 9 is between. - */ - public String visualizeKeyDistribution(int characters); -} diff --git a/src/main/java/com/carrotsearch/hppc/ShortStack.java b/src/main/java/com/carrotsearch/hppc/ShortStack.java deleted file mode 100755 index 2c972c32..00000000 --- a/src/main/java/com/carrotsearch/hppc/ShortStack.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.cursors.ShortCursor; - -/** - * A subclass of {@link ShortArrayList} adding stack-related utility methods. The top of the stack - * is at the {@link #size()} - 1 element. - */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeStack.java") -public class ShortStack extends ShortArrayList { - /** New instance with sane defaults. */ - public ShortStack() { - super(); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - */ - public ShortStack(int expectedElements) { - super(expectedElements); - } - - /** - * New instance with sane defaults. - * - * @param expectedElements The expected number of elements guaranteed not to cause buffer - * expansion (inclusive). - * @param resizer Underlying buffer sizing strategy. - */ - public ShortStack(int expectedElements, ArraySizingStrategy resizer) { - super(expectedElements, resizer); - } - - /** Create a stack by pushing all elements of another container to it. */ - public ShortStack(ShortContainer container) { - super(container); - } - - /** Adds one short to the stack. */ - public void push(short e1) { - ensureBufferSpace(1); - buffer[elementsCount++] = e1; - } - - /** Adds two shorts to the stack. */ - public void push(short e1, short e2) { - ensureBufferSpace(2); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - } - - /** Adds three shorts to the stack. */ - public void push(short e1, short e2, short e3) { - ensureBufferSpace(3); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - buffer[elementsCount++] = e3; - } - - /** Adds four shorts to the stack. */ - public void push(short e1, short e2, short e3, short e4) { - ensureBufferSpace(4); - buffer[elementsCount++] = e1; - buffer[elementsCount++] = e2; - buffer[elementsCount++] = e3; - buffer[elementsCount++] = e4; - } - - /** Add a range of array elements to the stack. */ - public void push(short[] elements, int start, int len) { - assert start >= 0 && len >= 0; - - ensureBufferSpace(len); - System.arraycopy(elements, start, buffer, elementsCount, len); - elementsCount += len; - } - - /** - * Vararg-signature method for pushing elements at the top of the stack. - * - *

This method is handy, but costly if used in tight loops (anonymous array passing) - */ - public final void push(short... elements) { - push(elements, 0, elements.length); - } - - /** Pushes all elements from another container to the top of the stack. */ - public int pushAll(ShortContainer container) { - return addAll(container); - } - - /** Pushes all elements from another iterable to the top of the stack. */ - public int pushAll(Iterable iterable) { - return addAll(iterable); - } - - /** Discard an arbitrary number of elements from the top of the stack. */ - public void discard(int count) { - assert elementsCount >= count; - - elementsCount -= count; - } - - /** Discard the top element from the stack. */ - public void discard() { - assert elementsCount > 0; - - elementsCount--; - } - - /** Remove the top element from the stack and return it. */ - public short pop() { - return removeLast(); - } - - /** Peek at the top element on the stack. */ - public short peek() { - assert elementsCount > 0; - return buffer[elementsCount - 1]; - } - - /** Create a stack by pushing a variable number of arguments to it. */ - public static ShortStack from(short... elements) { - final ShortStack stack = new ShortStack(elements.length); - stack.push(elements); - return stack; - } - - /** {@inheritDoc} */ - @Override - public ShortStack clone() { - return (ShortStack) super.clone(); - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationCharByteHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationCharByteHashMap.java deleted file mode 100755 index 4e0da79c..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationCharByteHashMap.java +++ /dev/null @@ -1,440 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link CharByteHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link CharByteHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationCharByteHashMap implements CharByteMap { - public final CharByteHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationCharByteHashMap(CharByteHashMap delegate, CharComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationCharByteHashMap(CharByteHashMap delegate, CharByteComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final char[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, CharComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - char[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, CharByteComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final char[] keys = delegate.keys; - final byte[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(char key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(CharContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(CharBytePredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final char[] keys = delegate.keys; - final byte[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final char[] keys = delegate.keys; - final byte[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public CharCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public ByteContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public byte get(char key) { - return delegate.get(key); - } - - @Override - public byte getOrDefault(char key, byte defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public byte put(char key, byte value) { - throw readOnlyException(); - } - - @Override - public int putAll(CharByteAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public byte putOrAdd(char key, byte putValue, byte incrementValue) { - throw readOnlyException(); - } - - @Override - public byte addTo(char key, byte additionValue) { - throw readOnlyException(); - } - - @Override - public byte remove(char key) { - throw readOnlyException(); - } - - @Override - public int indexOf(char key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public byte indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public byte indexReplace(int index, byte newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, char key, byte value) { - throw readOnlyException(); - } - - @Override - public byte indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final CharByteCursor cursor = new CharByteCursor(); - private int index; - - @Override - protected CharByteCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractCharCollection implements CharLookupContainer { - private final SortedIterationCharByteHashMap owner = SortedIterationCharByteHashMap.this; - - @Override - public boolean contains(char e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((CharByteProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((CharBytePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final char e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final CharCursor cursor = new CharCursor(); - private int index; - - @Override - protected CharCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractByteCollection { - private final SortedIterationCharByteHashMap owner = SortedIterationCharByteHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(byte value) { - for (CharByteCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((CharByteProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((CharBytePredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final byte e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final BytePredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ByteCursor cursor = new ByteCursor(); - private int index; - - @Override - protected ByteCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationCharCharHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationCharCharHashMap.java deleted file mode 100755 index e5f5017d..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationCharCharHashMap.java +++ /dev/null @@ -1,440 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link CharCharHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link CharCharHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationCharCharHashMap implements CharCharMap { - public final CharCharHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationCharCharHashMap(CharCharHashMap delegate, CharComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationCharCharHashMap(CharCharHashMap delegate, CharCharComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final char[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, CharComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - char[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, CharCharComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final char[] keys = delegate.keys; - final char[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(char key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(CharContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(CharCharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final char[] keys = delegate.keys; - final char[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final char[] keys = delegate.keys; - final char[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public CharCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public CharContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public char get(char key) { - return delegate.get(key); - } - - @Override - public char getOrDefault(char key, char defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public char put(char key, char value) { - throw readOnlyException(); - } - - @Override - public int putAll(CharCharAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public char putOrAdd(char key, char putValue, char incrementValue) { - throw readOnlyException(); - } - - @Override - public char addTo(char key, char additionValue) { - throw readOnlyException(); - } - - @Override - public char remove(char key) { - throw readOnlyException(); - } - - @Override - public int indexOf(char key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public char indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public char indexReplace(int index, char newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, char key, char value) { - throw readOnlyException(); - } - - @Override - public char indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final CharCharCursor cursor = new CharCharCursor(); - private int index; - - @Override - protected CharCharCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractCharCollection implements CharLookupContainer { - private final SortedIterationCharCharHashMap owner = SortedIterationCharCharHashMap.this; - - @Override - public boolean contains(char e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((CharCharProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((CharCharPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final char e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final CharCursor cursor = new CharCursor(); - private int index; - - @Override - protected CharCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractCharCollection { - private final SortedIterationCharCharHashMap owner = SortedIterationCharCharHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(char value) { - for (CharCharCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((CharCharProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((CharCharPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final char e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final CharCursor cursor = new CharCursor(); - private int index; - - @Override - protected CharCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationCharDoubleHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationCharDoubleHashMap.java deleted file mode 100755 index 0d54399c..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationCharDoubleHashMap.java +++ /dev/null @@ -1,441 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link CharDoubleHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link CharDoubleHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationCharDoubleHashMap implements CharDoubleMap { - public final CharDoubleHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationCharDoubleHashMap(CharDoubleHashMap delegate, CharComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationCharDoubleHashMap( - CharDoubleHashMap delegate, CharDoubleComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final char[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, CharComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - char[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, CharDoubleComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final char[] keys = delegate.keys; - final double[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(char key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(CharContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(CharDoublePredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final char[] keys = delegate.keys; - final double[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final char[] keys = delegate.keys; - final double[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public CharCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public DoubleContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public double get(char key) { - return delegate.get(key); - } - - @Override - public double getOrDefault(char key, double defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public double put(char key, double value) { - throw readOnlyException(); - } - - @Override - public int putAll(CharDoubleAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public double putOrAdd(char key, double putValue, double incrementValue) { - throw readOnlyException(); - } - - @Override - public double addTo(char key, double additionValue) { - throw readOnlyException(); - } - - @Override - public double remove(char key) { - throw readOnlyException(); - } - - @Override - public int indexOf(char key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public double indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public double indexReplace(int index, double newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, char key, double value) { - throw readOnlyException(); - } - - @Override - public double indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final CharDoubleCursor cursor = new CharDoubleCursor(); - private int index; - - @Override - protected CharDoubleCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractCharCollection implements CharLookupContainer { - private final SortedIterationCharDoubleHashMap owner = SortedIterationCharDoubleHashMap.this; - - @Override - public boolean contains(char e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((CharDoubleProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((CharDoublePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final char e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final CharCursor cursor = new CharCursor(); - private int index; - - @Override - protected CharCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractDoubleCollection { - private final SortedIterationCharDoubleHashMap owner = SortedIterationCharDoubleHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(double value) { - for (CharDoubleCursor c : owner) { - if ((Double.doubleToLongBits(value) == Double.doubleToLongBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((CharDoubleProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((CharDoublePredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final double e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final DoublePredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final DoubleCursor cursor = new DoubleCursor(); - private int index; - - @Override - protected DoubleCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationCharFloatHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationCharFloatHashMap.java deleted file mode 100755 index 90c227ef..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationCharFloatHashMap.java +++ /dev/null @@ -1,441 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link CharFloatHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link CharFloatHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationCharFloatHashMap implements CharFloatMap { - public final CharFloatHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationCharFloatHashMap(CharFloatHashMap delegate, CharComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationCharFloatHashMap( - CharFloatHashMap delegate, CharFloatComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final char[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, CharComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - char[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, CharFloatComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final char[] keys = delegate.keys; - final float[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(char key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(CharContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(CharFloatPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final char[] keys = delegate.keys; - final float[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final char[] keys = delegate.keys; - final float[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public CharCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public FloatContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public float get(char key) { - return delegate.get(key); - } - - @Override - public float getOrDefault(char key, float defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public float put(char key, float value) { - throw readOnlyException(); - } - - @Override - public int putAll(CharFloatAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public float putOrAdd(char key, float putValue, float incrementValue) { - throw readOnlyException(); - } - - @Override - public float addTo(char key, float additionValue) { - throw readOnlyException(); - } - - @Override - public float remove(char key) { - throw readOnlyException(); - } - - @Override - public int indexOf(char key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public float indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public float indexReplace(int index, float newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, char key, float value) { - throw readOnlyException(); - } - - @Override - public float indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final CharFloatCursor cursor = new CharFloatCursor(); - private int index; - - @Override - protected CharFloatCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractCharCollection implements CharLookupContainer { - private final SortedIterationCharFloatHashMap owner = SortedIterationCharFloatHashMap.this; - - @Override - public boolean contains(char e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((CharFloatProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((CharFloatPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final char e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final CharCursor cursor = new CharCursor(); - private int index; - - @Override - protected CharCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractFloatCollection { - private final SortedIterationCharFloatHashMap owner = SortedIterationCharFloatHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(float value) { - for (CharFloatCursor c : owner) { - if ((Float.floatToIntBits(value) == Float.floatToIntBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((CharFloatProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((CharFloatPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final float e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final FloatPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final FloatCursor cursor = new FloatCursor(); - private int index; - - @Override - protected FloatCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationCharIntHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationCharIntHashMap.java deleted file mode 100755 index dde200f4..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationCharIntHashMap.java +++ /dev/null @@ -1,440 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link CharIntHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link CharIntHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationCharIntHashMap implements CharIntMap { - public final CharIntHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationCharIntHashMap(CharIntHashMap delegate, CharComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationCharIntHashMap(CharIntHashMap delegate, CharIntComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final char[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, CharComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - char[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, CharIntComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final char[] keys = delegate.keys; - final int[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(char key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(CharContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(CharIntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final char[] keys = delegate.keys; - final int[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final char[] keys = delegate.keys; - final int[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public CharCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public IntContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public int get(char key) { - return delegate.get(key); - } - - @Override - public int getOrDefault(char key, int defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public int put(char key, int value) { - throw readOnlyException(); - } - - @Override - public int putAll(CharIntAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public int putOrAdd(char key, int putValue, int incrementValue) { - throw readOnlyException(); - } - - @Override - public int addTo(char key, int additionValue) { - throw readOnlyException(); - } - - @Override - public int remove(char key) { - throw readOnlyException(); - } - - @Override - public int indexOf(char key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public int indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public int indexReplace(int index, int newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, char key, int value) { - throw readOnlyException(); - } - - @Override - public int indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final CharIntCursor cursor = new CharIntCursor(); - private int index; - - @Override - protected CharIntCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractCharCollection implements CharLookupContainer { - private final SortedIterationCharIntHashMap owner = SortedIterationCharIntHashMap.this; - - @Override - public boolean contains(char e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((CharIntProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((CharIntPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final char e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final CharCursor cursor = new CharCursor(); - private int index; - - @Override - protected CharCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractIntCollection { - private final SortedIterationCharIntHashMap owner = SortedIterationCharIntHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(int value) { - for (CharIntCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((CharIntProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((CharIntPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final int e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final IntCursor cursor = new IntCursor(); - private int index; - - @Override - protected IntCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationCharLongHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationCharLongHashMap.java deleted file mode 100755 index 76ec360d..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationCharLongHashMap.java +++ /dev/null @@ -1,440 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link CharLongHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link CharLongHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationCharLongHashMap implements CharLongMap { - public final CharLongHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationCharLongHashMap(CharLongHashMap delegate, CharComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationCharLongHashMap(CharLongHashMap delegate, CharLongComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final char[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, CharComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - char[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, CharLongComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final char[] keys = delegate.keys; - final long[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(char key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(CharContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(CharLongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final char[] keys = delegate.keys; - final long[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final char[] keys = delegate.keys; - final long[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public CharCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public LongContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public long get(char key) { - return delegate.get(key); - } - - @Override - public long getOrDefault(char key, long defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public long put(char key, long value) { - throw readOnlyException(); - } - - @Override - public int putAll(CharLongAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public long putOrAdd(char key, long putValue, long incrementValue) { - throw readOnlyException(); - } - - @Override - public long addTo(char key, long additionValue) { - throw readOnlyException(); - } - - @Override - public long remove(char key) { - throw readOnlyException(); - } - - @Override - public int indexOf(char key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public long indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public long indexReplace(int index, long newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, char key, long value) { - throw readOnlyException(); - } - - @Override - public long indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final CharLongCursor cursor = new CharLongCursor(); - private int index; - - @Override - protected CharLongCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractCharCollection implements CharLookupContainer { - private final SortedIterationCharLongHashMap owner = SortedIterationCharLongHashMap.this; - - @Override - public boolean contains(char e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((CharLongProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((CharLongPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final char e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final CharCursor cursor = new CharCursor(); - private int index; - - @Override - protected CharCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractLongCollection { - private final SortedIterationCharLongHashMap owner = SortedIterationCharLongHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(long value) { - for (CharLongCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((CharLongProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((CharLongPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final long e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final LongCursor cursor = new LongCursor(); - private int index; - - @Override - protected LongCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationCharObjectHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationCharObjectHashMap.java deleted file mode 100755 index b4eef4b9..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationCharObjectHashMap.java +++ /dev/null @@ -1,435 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link CharObjectHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link CharObjectHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationCharObjectHashMap implements CharObjectMap { - public final CharObjectHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationCharObjectHashMap( - CharObjectHashMap delegate, CharComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationCharObjectHashMap( - CharObjectHashMap delegate, CharObjectComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final char[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, CharComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - char[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, CharObjectComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final char[] keys = delegate.keys; - final VType[] values = (VType[]) delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator> iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(char key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(CharContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(CharObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public > T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final char[] keys = delegate.keys; - final VType[] values = (VType[]) delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public > T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final char[] keys = delegate.keys; - final VType[] values = (VType[]) delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public CharCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public ObjectContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public VType get(char key) { - return delegate.get(key); - } - - @Override - public VType getOrDefault(char key, VType defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public VType put(char key, VType value) { - throw readOnlyException(); - } - - @Override - public int putAll(CharObjectAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable> iterable) { - throw readOnlyException(); - } - - @Override - public VType remove(char key) { - throw readOnlyException(); - } - - @Override - public int indexOf(char key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public VType indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public VType indexReplace(int index, VType newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, char key, VType value) { - throw readOnlyException(); - } - - @Override - public VType indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final CharObjectCursor cursor = new CharObjectCursor(); - private int index; - - @Override - protected CharObjectCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = (VType) delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractCharCollection implements CharLookupContainer { - private final SortedIterationCharObjectHashMap owner = - SortedIterationCharObjectHashMap.this; - - @Override - public boolean contains(char e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((CharObjectProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((CharObjectPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final char e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final CharCursor cursor = new CharCursor(); - private int index; - - @Override - protected CharCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractObjectCollection { - private final SortedIterationCharObjectHashMap owner = - SortedIterationCharObjectHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(VType value) { - for (CharObjectCursor c : owner) { - if (java.util.Objects.equals(value, c.value)) { - return true; - } - } - return false; - } - - @Override - public > T forEach(T procedure) { - owner.forEach((CharObjectProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public > T forEach(T predicate) { - owner.forEach((CharObjectPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator> iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final VType e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator> { - private final ObjectCursor cursor = new ObjectCursor(); - private int index; - - @Override - protected ObjectCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = (VType) delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationCharShortHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationCharShortHashMap.java deleted file mode 100755 index 8097e0c0..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationCharShortHashMap.java +++ /dev/null @@ -1,441 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link CharShortHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link CharShortHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationCharShortHashMap implements CharShortMap { - public final CharShortHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationCharShortHashMap(CharShortHashMap delegate, CharComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationCharShortHashMap( - CharShortHashMap delegate, CharShortComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final char[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, CharComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - char[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, CharShortComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final char[] keys = delegate.keys; - final short[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(char key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(CharContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(CharShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final char[] keys = delegate.keys; - final short[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final char[] keys = delegate.keys; - final short[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public CharCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public ShortContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public short get(char key) { - return delegate.get(key); - } - - @Override - public short getOrDefault(char key, short defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public short put(char key, short value) { - throw readOnlyException(); - } - - @Override - public int putAll(CharShortAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public short putOrAdd(char key, short putValue, short incrementValue) { - throw readOnlyException(); - } - - @Override - public short addTo(char key, short additionValue) { - throw readOnlyException(); - } - - @Override - public short remove(char key) { - throw readOnlyException(); - } - - @Override - public int indexOf(char key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public short indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public short indexReplace(int index, short newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, char key, short value) { - throw readOnlyException(); - } - - @Override - public short indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final CharShortCursor cursor = new CharShortCursor(); - private int index; - - @Override - protected CharShortCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractCharCollection implements CharLookupContainer { - private final SortedIterationCharShortHashMap owner = SortedIterationCharShortHashMap.this; - - @Override - public boolean contains(char e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((CharShortProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((CharShortPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final char e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final CharCursor cursor = new CharCursor(); - private int index; - - @Override - protected CharCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractShortCollection { - private final SortedIterationCharShortHashMap owner = SortedIterationCharShortHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(short value) { - for (CharShortCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((CharShortProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((CharShortPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final short e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ShortCursor cursor = new ShortCursor(); - private int index; - - @Override - protected ShortCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationIntByteHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationIntByteHashMap.java deleted file mode 100755 index ff9ca502..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationIntByteHashMap.java +++ /dev/null @@ -1,440 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link IntByteHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link IntByteHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationIntByteHashMap implements IntByteMap { - public final IntByteHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationIntByteHashMap(IntByteHashMap delegate, IntComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationIntByteHashMap(IntByteHashMap delegate, IntByteComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final int[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, IntComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - int[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, IntByteComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final int[] keys = delegate.keys; - final byte[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(int key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(IntContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(IntBytePredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final int[] keys = delegate.keys; - final byte[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final int[] keys = delegate.keys; - final byte[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public IntCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public ByteContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public byte get(int key) { - return delegate.get(key); - } - - @Override - public byte getOrDefault(int key, byte defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public byte put(int key, byte value) { - throw readOnlyException(); - } - - @Override - public int putAll(IntByteAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public byte putOrAdd(int key, byte putValue, byte incrementValue) { - throw readOnlyException(); - } - - @Override - public byte addTo(int key, byte additionValue) { - throw readOnlyException(); - } - - @Override - public byte remove(int key) { - throw readOnlyException(); - } - - @Override - public int indexOf(int key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public byte indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public byte indexReplace(int index, byte newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, int key, byte value) { - throw readOnlyException(); - } - - @Override - public byte indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final IntByteCursor cursor = new IntByteCursor(); - private int index; - - @Override - protected IntByteCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractIntCollection implements IntLookupContainer { - private final SortedIterationIntByteHashMap owner = SortedIterationIntByteHashMap.this; - - @Override - public boolean contains(int e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((IntByteProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((IntBytePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final int e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final IntCursor cursor = new IntCursor(); - private int index; - - @Override - protected IntCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractByteCollection { - private final SortedIterationIntByteHashMap owner = SortedIterationIntByteHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(byte value) { - for (IntByteCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((IntByteProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((IntBytePredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final byte e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final BytePredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ByteCursor cursor = new ByteCursor(); - private int index; - - @Override - protected ByteCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationIntCharHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationIntCharHashMap.java deleted file mode 100755 index c9190720..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationIntCharHashMap.java +++ /dev/null @@ -1,440 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link IntCharHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link IntCharHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationIntCharHashMap implements IntCharMap { - public final IntCharHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationIntCharHashMap(IntCharHashMap delegate, IntComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationIntCharHashMap(IntCharHashMap delegate, IntCharComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final int[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, IntComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - int[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, IntCharComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final int[] keys = delegate.keys; - final char[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(int key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(IntContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(IntCharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final int[] keys = delegate.keys; - final char[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final int[] keys = delegate.keys; - final char[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public IntCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public CharContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public char get(int key) { - return delegate.get(key); - } - - @Override - public char getOrDefault(int key, char defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public char put(int key, char value) { - throw readOnlyException(); - } - - @Override - public int putAll(IntCharAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public char putOrAdd(int key, char putValue, char incrementValue) { - throw readOnlyException(); - } - - @Override - public char addTo(int key, char additionValue) { - throw readOnlyException(); - } - - @Override - public char remove(int key) { - throw readOnlyException(); - } - - @Override - public int indexOf(int key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public char indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public char indexReplace(int index, char newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, int key, char value) { - throw readOnlyException(); - } - - @Override - public char indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final IntCharCursor cursor = new IntCharCursor(); - private int index; - - @Override - protected IntCharCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractIntCollection implements IntLookupContainer { - private final SortedIterationIntCharHashMap owner = SortedIterationIntCharHashMap.this; - - @Override - public boolean contains(int e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((IntCharProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((IntCharPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final int e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final IntCursor cursor = new IntCursor(); - private int index; - - @Override - protected IntCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractCharCollection { - private final SortedIterationIntCharHashMap owner = SortedIterationIntCharHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(char value) { - for (IntCharCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((IntCharProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((IntCharPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final char e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final CharCursor cursor = new CharCursor(); - private int index; - - @Override - protected CharCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationIntDoubleHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationIntDoubleHashMap.java deleted file mode 100755 index 107abe8f..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationIntDoubleHashMap.java +++ /dev/null @@ -1,441 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link IntDoubleHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link IntDoubleHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationIntDoubleHashMap implements IntDoubleMap { - public final IntDoubleHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationIntDoubleHashMap(IntDoubleHashMap delegate, IntComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationIntDoubleHashMap( - IntDoubleHashMap delegate, IntDoubleComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final int[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, IntComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - int[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, IntDoubleComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final int[] keys = delegate.keys; - final double[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(int key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(IntContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(IntDoublePredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final int[] keys = delegate.keys; - final double[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final int[] keys = delegate.keys; - final double[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public IntCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public DoubleContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public double get(int key) { - return delegate.get(key); - } - - @Override - public double getOrDefault(int key, double defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public double put(int key, double value) { - throw readOnlyException(); - } - - @Override - public int putAll(IntDoubleAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public double putOrAdd(int key, double putValue, double incrementValue) { - throw readOnlyException(); - } - - @Override - public double addTo(int key, double additionValue) { - throw readOnlyException(); - } - - @Override - public double remove(int key) { - throw readOnlyException(); - } - - @Override - public int indexOf(int key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public double indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public double indexReplace(int index, double newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, int key, double value) { - throw readOnlyException(); - } - - @Override - public double indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final IntDoubleCursor cursor = new IntDoubleCursor(); - private int index; - - @Override - protected IntDoubleCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractIntCollection implements IntLookupContainer { - private final SortedIterationIntDoubleHashMap owner = SortedIterationIntDoubleHashMap.this; - - @Override - public boolean contains(int e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((IntDoubleProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((IntDoublePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final int e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final IntCursor cursor = new IntCursor(); - private int index; - - @Override - protected IntCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractDoubleCollection { - private final SortedIterationIntDoubleHashMap owner = SortedIterationIntDoubleHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(double value) { - for (IntDoubleCursor c : owner) { - if ((Double.doubleToLongBits(value) == Double.doubleToLongBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((IntDoubleProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((IntDoublePredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final double e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final DoublePredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final DoubleCursor cursor = new DoubleCursor(); - private int index; - - @Override - protected DoubleCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationIntFloatHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationIntFloatHashMap.java deleted file mode 100755 index ff98b064..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationIntFloatHashMap.java +++ /dev/null @@ -1,440 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link IntFloatHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link IntFloatHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationIntFloatHashMap implements IntFloatMap { - public final IntFloatHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationIntFloatHashMap(IntFloatHashMap delegate, IntComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationIntFloatHashMap(IntFloatHashMap delegate, IntFloatComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final int[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, IntComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - int[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, IntFloatComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final int[] keys = delegate.keys; - final float[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(int key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(IntContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(IntFloatPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final int[] keys = delegate.keys; - final float[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final int[] keys = delegate.keys; - final float[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public IntCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public FloatContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public float get(int key) { - return delegate.get(key); - } - - @Override - public float getOrDefault(int key, float defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public float put(int key, float value) { - throw readOnlyException(); - } - - @Override - public int putAll(IntFloatAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public float putOrAdd(int key, float putValue, float incrementValue) { - throw readOnlyException(); - } - - @Override - public float addTo(int key, float additionValue) { - throw readOnlyException(); - } - - @Override - public float remove(int key) { - throw readOnlyException(); - } - - @Override - public int indexOf(int key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public float indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public float indexReplace(int index, float newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, int key, float value) { - throw readOnlyException(); - } - - @Override - public float indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final IntFloatCursor cursor = new IntFloatCursor(); - private int index; - - @Override - protected IntFloatCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractIntCollection implements IntLookupContainer { - private final SortedIterationIntFloatHashMap owner = SortedIterationIntFloatHashMap.this; - - @Override - public boolean contains(int e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((IntFloatProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((IntFloatPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final int e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final IntCursor cursor = new IntCursor(); - private int index; - - @Override - protected IntCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractFloatCollection { - private final SortedIterationIntFloatHashMap owner = SortedIterationIntFloatHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(float value) { - for (IntFloatCursor c : owner) { - if ((Float.floatToIntBits(value) == Float.floatToIntBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((IntFloatProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((IntFloatPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final float e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final FloatPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final FloatCursor cursor = new FloatCursor(); - private int index; - - @Override - protected FloatCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationIntIntHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationIntIntHashMap.java deleted file mode 100755 index 8cb4c22c..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationIntIntHashMap.java +++ /dev/null @@ -1,440 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link IntIntHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link IntIntHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationIntIntHashMap implements IntIntMap { - public final IntIntHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationIntIntHashMap(IntIntHashMap delegate, IntComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationIntIntHashMap(IntIntHashMap delegate, IntIntComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final int[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, IntComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - int[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, IntIntComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final int[] keys = delegate.keys; - final int[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(int key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(IntContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(IntIntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final int[] keys = delegate.keys; - final int[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final int[] keys = delegate.keys; - final int[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public IntCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public IntContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public int get(int key) { - return delegate.get(key); - } - - @Override - public int getOrDefault(int key, int defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public int put(int key, int value) { - throw readOnlyException(); - } - - @Override - public int putAll(IntIntAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public int putOrAdd(int key, int putValue, int incrementValue) { - throw readOnlyException(); - } - - @Override - public int addTo(int key, int additionValue) { - throw readOnlyException(); - } - - @Override - public int remove(int key) { - throw readOnlyException(); - } - - @Override - public int indexOf(int key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public int indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public int indexReplace(int index, int newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, int key, int value) { - throw readOnlyException(); - } - - @Override - public int indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final IntIntCursor cursor = new IntIntCursor(); - private int index; - - @Override - protected IntIntCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractIntCollection implements IntLookupContainer { - private final SortedIterationIntIntHashMap owner = SortedIterationIntIntHashMap.this; - - @Override - public boolean contains(int e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((IntIntProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((IntIntPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final int e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final IntCursor cursor = new IntCursor(); - private int index; - - @Override - protected IntCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractIntCollection { - private final SortedIterationIntIntHashMap owner = SortedIterationIntIntHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(int value) { - for (IntIntCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((IntIntProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((IntIntPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final int e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final IntCursor cursor = new IntCursor(); - private int index; - - @Override - protected IntCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationIntLongHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationIntLongHashMap.java deleted file mode 100755 index bbbfb306..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationIntLongHashMap.java +++ /dev/null @@ -1,440 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link IntLongHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link IntLongHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationIntLongHashMap implements IntLongMap { - public final IntLongHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationIntLongHashMap(IntLongHashMap delegate, IntComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationIntLongHashMap(IntLongHashMap delegate, IntLongComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final int[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, IntComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - int[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, IntLongComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final int[] keys = delegate.keys; - final long[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(int key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(IntContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(IntLongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final int[] keys = delegate.keys; - final long[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final int[] keys = delegate.keys; - final long[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public IntCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public LongContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public long get(int key) { - return delegate.get(key); - } - - @Override - public long getOrDefault(int key, long defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public long put(int key, long value) { - throw readOnlyException(); - } - - @Override - public int putAll(IntLongAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public long putOrAdd(int key, long putValue, long incrementValue) { - throw readOnlyException(); - } - - @Override - public long addTo(int key, long additionValue) { - throw readOnlyException(); - } - - @Override - public long remove(int key) { - throw readOnlyException(); - } - - @Override - public int indexOf(int key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public long indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public long indexReplace(int index, long newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, int key, long value) { - throw readOnlyException(); - } - - @Override - public long indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final IntLongCursor cursor = new IntLongCursor(); - private int index; - - @Override - protected IntLongCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractIntCollection implements IntLookupContainer { - private final SortedIterationIntLongHashMap owner = SortedIterationIntLongHashMap.this; - - @Override - public boolean contains(int e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((IntLongProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((IntLongPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final int e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final IntCursor cursor = new IntCursor(); - private int index; - - @Override - protected IntCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractLongCollection { - private final SortedIterationIntLongHashMap owner = SortedIterationIntLongHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(long value) { - for (IntLongCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((IntLongProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((IntLongPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final long e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final LongCursor cursor = new LongCursor(); - private int index; - - @Override - protected LongCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationIntObjectHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationIntObjectHashMap.java deleted file mode 100755 index cd8591ef..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationIntObjectHashMap.java +++ /dev/null @@ -1,435 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link IntObjectHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link IntObjectHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationIntObjectHashMap implements IntObjectMap { - public final IntObjectHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationIntObjectHashMap( - IntObjectHashMap delegate, IntComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationIntObjectHashMap( - IntObjectHashMap delegate, IntObjectComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final int[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, IntComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - int[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, IntObjectComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final int[] keys = delegate.keys; - final VType[] values = (VType[]) delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator> iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(int key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(IntContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(IntObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public > T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final int[] keys = delegate.keys; - final VType[] values = (VType[]) delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public > T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final int[] keys = delegate.keys; - final VType[] values = (VType[]) delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public IntCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public ObjectContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public VType get(int key) { - return delegate.get(key); - } - - @Override - public VType getOrDefault(int key, VType defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public VType put(int key, VType value) { - throw readOnlyException(); - } - - @Override - public int putAll(IntObjectAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable> iterable) { - throw readOnlyException(); - } - - @Override - public VType remove(int key) { - throw readOnlyException(); - } - - @Override - public int indexOf(int key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public VType indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public VType indexReplace(int index, VType newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, int key, VType value) { - throw readOnlyException(); - } - - @Override - public VType indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final IntObjectCursor cursor = new IntObjectCursor(); - private int index; - - @Override - protected IntObjectCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = (VType) delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractIntCollection implements IntLookupContainer { - private final SortedIterationIntObjectHashMap owner = - SortedIterationIntObjectHashMap.this; - - @Override - public boolean contains(int e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((IntObjectProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((IntObjectPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final int e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final IntCursor cursor = new IntCursor(); - private int index; - - @Override - protected IntCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractObjectCollection { - private final SortedIterationIntObjectHashMap owner = - SortedIterationIntObjectHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(VType value) { - for (IntObjectCursor c : owner) { - if (java.util.Objects.equals(value, c.value)) { - return true; - } - } - return false; - } - - @Override - public > T forEach(T procedure) { - owner.forEach((IntObjectProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public > T forEach(T predicate) { - owner.forEach((IntObjectPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator> iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final VType e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator> { - private final ObjectCursor cursor = new ObjectCursor(); - private int index; - - @Override - protected ObjectCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = (VType) delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationIntShortHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationIntShortHashMap.java deleted file mode 100755 index 243bf989..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationIntShortHashMap.java +++ /dev/null @@ -1,440 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link IntShortHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link IntShortHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationIntShortHashMap implements IntShortMap { - public final IntShortHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationIntShortHashMap(IntShortHashMap delegate, IntComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationIntShortHashMap(IntShortHashMap delegate, IntShortComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final int[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, IntComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - int[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, IntShortComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final int[] keys = delegate.keys; - final short[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(int key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(IntContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(IntShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final int[] keys = delegate.keys; - final short[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final int[] keys = delegate.keys; - final short[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public IntCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public ShortContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public short get(int key) { - return delegate.get(key); - } - - @Override - public short getOrDefault(int key, short defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public short put(int key, short value) { - throw readOnlyException(); - } - - @Override - public int putAll(IntShortAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public short putOrAdd(int key, short putValue, short incrementValue) { - throw readOnlyException(); - } - - @Override - public short addTo(int key, short additionValue) { - throw readOnlyException(); - } - - @Override - public short remove(int key) { - throw readOnlyException(); - } - - @Override - public int indexOf(int key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public short indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public short indexReplace(int index, short newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, int key, short value) { - throw readOnlyException(); - } - - @Override - public short indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final IntShortCursor cursor = new IntShortCursor(); - private int index; - - @Override - protected IntShortCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractIntCollection implements IntLookupContainer { - private final SortedIterationIntShortHashMap owner = SortedIterationIntShortHashMap.this; - - @Override - public boolean contains(int e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((IntShortProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((IntShortPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final int e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final IntCursor cursor = new IntCursor(); - private int index; - - @Override - protected IntCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractShortCollection { - private final SortedIterationIntShortHashMap owner = SortedIterationIntShortHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(short value) { - for (IntShortCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((IntShortProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((IntShortPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final short e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ShortCursor cursor = new ShortCursor(); - private int index; - - @Override - protected ShortCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationLongByteHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationLongByteHashMap.java deleted file mode 100755 index 46b063f7..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationLongByteHashMap.java +++ /dev/null @@ -1,440 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link LongByteHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link LongByteHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationLongByteHashMap implements LongByteMap { - public final LongByteHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationLongByteHashMap(LongByteHashMap delegate, LongComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationLongByteHashMap(LongByteHashMap delegate, LongByteComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final long[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, LongComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - long[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, LongByteComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final long[] keys = delegate.keys; - final byte[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(long key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(LongContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(LongBytePredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final long[] keys = delegate.keys; - final byte[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final long[] keys = delegate.keys; - final byte[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public LongCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public ByteContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public byte get(long key) { - return delegate.get(key); - } - - @Override - public byte getOrDefault(long key, byte defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public byte put(long key, byte value) { - throw readOnlyException(); - } - - @Override - public int putAll(LongByteAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public byte putOrAdd(long key, byte putValue, byte incrementValue) { - throw readOnlyException(); - } - - @Override - public byte addTo(long key, byte additionValue) { - throw readOnlyException(); - } - - @Override - public byte remove(long key) { - throw readOnlyException(); - } - - @Override - public int indexOf(long key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public byte indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public byte indexReplace(int index, byte newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, long key, byte value) { - throw readOnlyException(); - } - - @Override - public byte indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final LongByteCursor cursor = new LongByteCursor(); - private int index; - - @Override - protected LongByteCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractLongCollection implements LongLookupContainer { - private final SortedIterationLongByteHashMap owner = SortedIterationLongByteHashMap.this; - - @Override - public boolean contains(long e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((LongByteProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((LongBytePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final long e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final LongCursor cursor = new LongCursor(); - private int index; - - @Override - protected LongCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractByteCollection { - private final SortedIterationLongByteHashMap owner = SortedIterationLongByteHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(byte value) { - for (LongByteCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((LongByteProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((LongBytePredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final byte e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final BytePredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ByteCursor cursor = new ByteCursor(); - private int index; - - @Override - protected ByteCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationLongCharHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationLongCharHashMap.java deleted file mode 100755 index c4daf3d0..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationLongCharHashMap.java +++ /dev/null @@ -1,440 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link LongCharHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link LongCharHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationLongCharHashMap implements LongCharMap { - public final LongCharHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationLongCharHashMap(LongCharHashMap delegate, LongComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationLongCharHashMap(LongCharHashMap delegate, LongCharComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final long[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, LongComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - long[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, LongCharComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final long[] keys = delegate.keys; - final char[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(long key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(LongContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(LongCharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final long[] keys = delegate.keys; - final char[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final long[] keys = delegate.keys; - final char[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public LongCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public CharContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public char get(long key) { - return delegate.get(key); - } - - @Override - public char getOrDefault(long key, char defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public char put(long key, char value) { - throw readOnlyException(); - } - - @Override - public int putAll(LongCharAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public char putOrAdd(long key, char putValue, char incrementValue) { - throw readOnlyException(); - } - - @Override - public char addTo(long key, char additionValue) { - throw readOnlyException(); - } - - @Override - public char remove(long key) { - throw readOnlyException(); - } - - @Override - public int indexOf(long key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public char indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public char indexReplace(int index, char newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, long key, char value) { - throw readOnlyException(); - } - - @Override - public char indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final LongCharCursor cursor = new LongCharCursor(); - private int index; - - @Override - protected LongCharCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractLongCollection implements LongLookupContainer { - private final SortedIterationLongCharHashMap owner = SortedIterationLongCharHashMap.this; - - @Override - public boolean contains(long e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((LongCharProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((LongCharPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final long e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final LongCursor cursor = new LongCursor(); - private int index; - - @Override - protected LongCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractCharCollection { - private final SortedIterationLongCharHashMap owner = SortedIterationLongCharHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(char value) { - for (LongCharCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((LongCharProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((LongCharPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final char e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final CharCursor cursor = new CharCursor(); - private int index; - - @Override - protected CharCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationLongDoubleHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationLongDoubleHashMap.java deleted file mode 100755 index bbd41530..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationLongDoubleHashMap.java +++ /dev/null @@ -1,441 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link LongDoubleHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link LongDoubleHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationLongDoubleHashMap implements LongDoubleMap { - public final LongDoubleHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationLongDoubleHashMap(LongDoubleHashMap delegate, LongComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationLongDoubleHashMap( - LongDoubleHashMap delegate, LongDoubleComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final long[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, LongComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - long[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, LongDoubleComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final long[] keys = delegate.keys; - final double[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(long key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(LongContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(LongDoublePredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final long[] keys = delegate.keys; - final double[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final long[] keys = delegate.keys; - final double[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public LongCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public DoubleContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public double get(long key) { - return delegate.get(key); - } - - @Override - public double getOrDefault(long key, double defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public double put(long key, double value) { - throw readOnlyException(); - } - - @Override - public int putAll(LongDoubleAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public double putOrAdd(long key, double putValue, double incrementValue) { - throw readOnlyException(); - } - - @Override - public double addTo(long key, double additionValue) { - throw readOnlyException(); - } - - @Override - public double remove(long key) { - throw readOnlyException(); - } - - @Override - public int indexOf(long key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public double indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public double indexReplace(int index, double newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, long key, double value) { - throw readOnlyException(); - } - - @Override - public double indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final LongDoubleCursor cursor = new LongDoubleCursor(); - private int index; - - @Override - protected LongDoubleCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractLongCollection implements LongLookupContainer { - private final SortedIterationLongDoubleHashMap owner = SortedIterationLongDoubleHashMap.this; - - @Override - public boolean contains(long e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((LongDoubleProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((LongDoublePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final long e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final LongCursor cursor = new LongCursor(); - private int index; - - @Override - protected LongCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractDoubleCollection { - private final SortedIterationLongDoubleHashMap owner = SortedIterationLongDoubleHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(double value) { - for (LongDoubleCursor c : owner) { - if ((Double.doubleToLongBits(value) == Double.doubleToLongBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((LongDoubleProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((LongDoublePredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final double e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final DoublePredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final DoubleCursor cursor = new DoubleCursor(); - private int index; - - @Override - protected DoubleCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationLongFloatHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationLongFloatHashMap.java deleted file mode 100755 index 2a8e61d2..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationLongFloatHashMap.java +++ /dev/null @@ -1,441 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link LongFloatHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link LongFloatHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationLongFloatHashMap implements LongFloatMap { - public final LongFloatHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationLongFloatHashMap(LongFloatHashMap delegate, LongComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationLongFloatHashMap( - LongFloatHashMap delegate, LongFloatComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final long[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, LongComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - long[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, LongFloatComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final long[] keys = delegate.keys; - final float[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(long key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(LongContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(LongFloatPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final long[] keys = delegate.keys; - final float[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final long[] keys = delegate.keys; - final float[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public LongCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public FloatContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public float get(long key) { - return delegate.get(key); - } - - @Override - public float getOrDefault(long key, float defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public float put(long key, float value) { - throw readOnlyException(); - } - - @Override - public int putAll(LongFloatAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public float putOrAdd(long key, float putValue, float incrementValue) { - throw readOnlyException(); - } - - @Override - public float addTo(long key, float additionValue) { - throw readOnlyException(); - } - - @Override - public float remove(long key) { - throw readOnlyException(); - } - - @Override - public int indexOf(long key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public float indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public float indexReplace(int index, float newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, long key, float value) { - throw readOnlyException(); - } - - @Override - public float indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final LongFloatCursor cursor = new LongFloatCursor(); - private int index; - - @Override - protected LongFloatCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractLongCollection implements LongLookupContainer { - private final SortedIterationLongFloatHashMap owner = SortedIterationLongFloatHashMap.this; - - @Override - public boolean contains(long e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((LongFloatProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((LongFloatPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final long e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final LongCursor cursor = new LongCursor(); - private int index; - - @Override - protected LongCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractFloatCollection { - private final SortedIterationLongFloatHashMap owner = SortedIterationLongFloatHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(float value) { - for (LongFloatCursor c : owner) { - if ((Float.floatToIntBits(value) == Float.floatToIntBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((LongFloatProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((LongFloatPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final float e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final FloatPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final FloatCursor cursor = new FloatCursor(); - private int index; - - @Override - protected FloatCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationLongIntHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationLongIntHashMap.java deleted file mode 100755 index 77620aa9..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationLongIntHashMap.java +++ /dev/null @@ -1,440 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link LongIntHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link LongIntHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationLongIntHashMap implements LongIntMap { - public final LongIntHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationLongIntHashMap(LongIntHashMap delegate, LongComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationLongIntHashMap(LongIntHashMap delegate, LongIntComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final long[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, LongComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - long[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, LongIntComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final long[] keys = delegate.keys; - final int[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(long key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(LongContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(LongIntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final long[] keys = delegate.keys; - final int[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final long[] keys = delegate.keys; - final int[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public LongCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public IntContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public int get(long key) { - return delegate.get(key); - } - - @Override - public int getOrDefault(long key, int defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public int put(long key, int value) { - throw readOnlyException(); - } - - @Override - public int putAll(LongIntAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public int putOrAdd(long key, int putValue, int incrementValue) { - throw readOnlyException(); - } - - @Override - public int addTo(long key, int additionValue) { - throw readOnlyException(); - } - - @Override - public int remove(long key) { - throw readOnlyException(); - } - - @Override - public int indexOf(long key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public int indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public int indexReplace(int index, int newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, long key, int value) { - throw readOnlyException(); - } - - @Override - public int indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final LongIntCursor cursor = new LongIntCursor(); - private int index; - - @Override - protected LongIntCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractLongCollection implements LongLookupContainer { - private final SortedIterationLongIntHashMap owner = SortedIterationLongIntHashMap.this; - - @Override - public boolean contains(long e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((LongIntProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((LongIntPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final long e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final LongCursor cursor = new LongCursor(); - private int index; - - @Override - protected LongCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractIntCollection { - private final SortedIterationLongIntHashMap owner = SortedIterationLongIntHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(int value) { - for (LongIntCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((LongIntProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((LongIntPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final int e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final IntCursor cursor = new IntCursor(); - private int index; - - @Override - protected IntCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationLongLongHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationLongLongHashMap.java deleted file mode 100755 index 605189b3..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationLongLongHashMap.java +++ /dev/null @@ -1,440 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link LongLongHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link LongLongHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationLongLongHashMap implements LongLongMap { - public final LongLongHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationLongLongHashMap(LongLongHashMap delegate, LongComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationLongLongHashMap(LongLongHashMap delegate, LongLongComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final long[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, LongComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - long[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, LongLongComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final long[] keys = delegate.keys; - final long[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(long key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(LongContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(LongLongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final long[] keys = delegate.keys; - final long[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final long[] keys = delegate.keys; - final long[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public LongCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public LongContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public long get(long key) { - return delegate.get(key); - } - - @Override - public long getOrDefault(long key, long defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public long put(long key, long value) { - throw readOnlyException(); - } - - @Override - public int putAll(LongLongAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public long putOrAdd(long key, long putValue, long incrementValue) { - throw readOnlyException(); - } - - @Override - public long addTo(long key, long additionValue) { - throw readOnlyException(); - } - - @Override - public long remove(long key) { - throw readOnlyException(); - } - - @Override - public int indexOf(long key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public long indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public long indexReplace(int index, long newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, long key, long value) { - throw readOnlyException(); - } - - @Override - public long indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final LongLongCursor cursor = new LongLongCursor(); - private int index; - - @Override - protected LongLongCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractLongCollection implements LongLookupContainer { - private final SortedIterationLongLongHashMap owner = SortedIterationLongLongHashMap.this; - - @Override - public boolean contains(long e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((LongLongProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((LongLongPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final long e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final LongCursor cursor = new LongCursor(); - private int index; - - @Override - protected LongCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractLongCollection { - private final SortedIterationLongLongHashMap owner = SortedIterationLongLongHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(long value) { - for (LongLongCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((LongLongProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((LongLongPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final long e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final LongCursor cursor = new LongCursor(); - private int index; - - @Override - protected LongCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationLongObjectHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationLongObjectHashMap.java deleted file mode 100755 index b7f64fe4..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationLongObjectHashMap.java +++ /dev/null @@ -1,435 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link LongObjectHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link LongObjectHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationLongObjectHashMap implements LongObjectMap { - public final LongObjectHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationLongObjectHashMap( - LongObjectHashMap delegate, LongComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationLongObjectHashMap( - LongObjectHashMap delegate, LongObjectComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final long[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, LongComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - long[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, LongObjectComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final long[] keys = delegate.keys; - final VType[] values = (VType[]) delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator> iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(long key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(LongContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(LongObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public > T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final long[] keys = delegate.keys; - final VType[] values = (VType[]) delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public > T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final long[] keys = delegate.keys; - final VType[] values = (VType[]) delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public LongCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public ObjectContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public VType get(long key) { - return delegate.get(key); - } - - @Override - public VType getOrDefault(long key, VType defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public VType put(long key, VType value) { - throw readOnlyException(); - } - - @Override - public int putAll(LongObjectAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable> iterable) { - throw readOnlyException(); - } - - @Override - public VType remove(long key) { - throw readOnlyException(); - } - - @Override - public int indexOf(long key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public VType indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public VType indexReplace(int index, VType newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, long key, VType value) { - throw readOnlyException(); - } - - @Override - public VType indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final LongObjectCursor cursor = new LongObjectCursor(); - private int index; - - @Override - protected LongObjectCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = (VType) delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractLongCollection implements LongLookupContainer { - private final SortedIterationLongObjectHashMap owner = - SortedIterationLongObjectHashMap.this; - - @Override - public boolean contains(long e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((LongObjectProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((LongObjectPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final long e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final LongCursor cursor = new LongCursor(); - private int index; - - @Override - protected LongCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractObjectCollection { - private final SortedIterationLongObjectHashMap owner = - SortedIterationLongObjectHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(VType value) { - for (LongObjectCursor c : owner) { - if (java.util.Objects.equals(value, c.value)) { - return true; - } - } - return false; - } - - @Override - public > T forEach(T procedure) { - owner.forEach((LongObjectProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public > T forEach(T predicate) { - owner.forEach((LongObjectPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator> iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final VType e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator> { - private final ObjectCursor cursor = new ObjectCursor(); - private int index; - - @Override - protected ObjectCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = (VType) delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationLongShortHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationLongShortHashMap.java deleted file mode 100755 index 63de5c26..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationLongShortHashMap.java +++ /dev/null @@ -1,441 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link LongShortHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link LongShortHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationLongShortHashMap implements LongShortMap { - public final LongShortHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationLongShortHashMap(LongShortHashMap delegate, LongComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationLongShortHashMap( - LongShortHashMap delegate, LongShortComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final long[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, LongComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - long[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, LongShortComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final long[] keys = delegate.keys; - final short[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(long key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(LongContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(LongShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final long[] keys = delegate.keys; - final short[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final long[] keys = delegate.keys; - final short[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public LongCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public ShortContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public short get(long key) { - return delegate.get(key); - } - - @Override - public short getOrDefault(long key, short defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public short put(long key, short value) { - throw readOnlyException(); - } - - @Override - public int putAll(LongShortAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public short putOrAdd(long key, short putValue, short incrementValue) { - throw readOnlyException(); - } - - @Override - public short addTo(long key, short additionValue) { - throw readOnlyException(); - } - - @Override - public short remove(long key) { - throw readOnlyException(); - } - - @Override - public int indexOf(long key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public short indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public short indexReplace(int index, short newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, long key, short value) { - throw readOnlyException(); - } - - @Override - public short indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final LongShortCursor cursor = new LongShortCursor(); - private int index; - - @Override - protected LongShortCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractLongCollection implements LongLookupContainer { - private final SortedIterationLongShortHashMap owner = SortedIterationLongShortHashMap.this; - - @Override - public boolean contains(long e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((LongShortProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((LongShortPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final long e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final LongCursor cursor = new LongCursor(); - private int index; - - @Override - protected LongCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractShortCollection { - private final SortedIterationLongShortHashMap owner = SortedIterationLongShortHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(short value) { - for (LongShortCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((LongShortProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((LongShortPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final short e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ShortCursor cursor = new ShortCursor(); - private int index; - - @Override - protected ShortCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationObjectByteHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationObjectByteHashMap.java deleted file mode 100755 index 011ace2f..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationObjectByteHashMap.java +++ /dev/null @@ -1,447 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Comparator; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link ObjectByteHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link ObjectByteHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationObjectByteHashMap implements ObjectByteMap { - public final ObjectByteHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationObjectByteHashMap( - ObjectByteHashMap delegate, Comparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationObjectByteHashMap( - ObjectByteHashMap delegate, ObjectByteComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final KType[] keys = (KType[]) delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!(((KType) keys[keyIndex]) == null)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, Comparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - KType[] keys = (KType[]) delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, ObjectByteComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final KType[] keys = (KType[]) delegate.keys; - final byte[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator> iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(KType key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(ObjectContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectBytePredicate predicate) { - throw readOnlyException(); - } - - @Override - public > T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final KType[] keys = (KType[]) delegate.keys; - final byte[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public > T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final KType[] keys = (KType[]) delegate.keys; - final byte[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public ObjectCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public ByteContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public byte get(KType key) { - return delegate.get(key); - } - - @Override - public byte getOrDefault(KType key, byte defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public byte put(KType key, byte value) { - throw readOnlyException(); - } - - @Override - public int putAll(ObjectByteAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable> iterable) { - throw readOnlyException(); - } - - @Override - public byte putOrAdd(KType key, byte putValue, byte incrementValue) { - throw readOnlyException(); - } - - @Override - public byte addTo(KType key, byte additionValue) { - throw readOnlyException(); - } - - @Override - public byte remove(KType key) { - throw readOnlyException(); - } - - @Override - public int indexOf(KType key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public byte indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public byte indexReplace(int index, byte newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, KType key, byte value) { - throw readOnlyException(); - } - - @Override - public byte indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final ObjectByteCursor cursor = new ObjectByteCursor(); - private int index; - - @Override - protected ObjectByteCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = (KType) delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractObjectCollection - implements ObjectLookupContainer { - private final SortedIterationObjectByteHashMap owner = - SortedIterationObjectByteHashMap.this; - - @Override - public boolean contains(KType e) { - return owner.containsKey(e); - } - - @Override - public > T forEach(final T procedure) { - owner.forEach((ObjectByteProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public > T forEach(final T predicate) { - owner.forEach((ObjectBytePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator> iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final KType e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator> { - private final ObjectCursor cursor = new ObjectCursor(); - private int index; - - @Override - protected ObjectCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = (KType) delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractByteCollection { - private final SortedIterationObjectByteHashMap owner = - SortedIterationObjectByteHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(byte value) { - for (ObjectByteCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((ObjectByteProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((ObjectBytePredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final byte e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final BytePredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ByteCursor cursor = new ByteCursor(); - private int index; - - @Override - protected ByteCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationObjectCharHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationObjectCharHashMap.java deleted file mode 100755 index 37f0f25a..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationObjectCharHashMap.java +++ /dev/null @@ -1,447 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Comparator; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link ObjectCharHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link ObjectCharHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationObjectCharHashMap implements ObjectCharMap { - public final ObjectCharHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationObjectCharHashMap( - ObjectCharHashMap delegate, Comparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationObjectCharHashMap( - ObjectCharHashMap delegate, ObjectCharComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final KType[] keys = (KType[]) delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!(((KType) keys[keyIndex]) == null)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, Comparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - KType[] keys = (KType[]) delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, ObjectCharComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final KType[] keys = (KType[]) delegate.keys; - final char[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator> iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(KType key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(ObjectContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectCharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public > T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final KType[] keys = (KType[]) delegate.keys; - final char[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public > T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final KType[] keys = (KType[]) delegate.keys; - final char[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public ObjectCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public CharContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public char get(KType key) { - return delegate.get(key); - } - - @Override - public char getOrDefault(KType key, char defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public char put(KType key, char value) { - throw readOnlyException(); - } - - @Override - public int putAll(ObjectCharAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable> iterable) { - throw readOnlyException(); - } - - @Override - public char putOrAdd(KType key, char putValue, char incrementValue) { - throw readOnlyException(); - } - - @Override - public char addTo(KType key, char additionValue) { - throw readOnlyException(); - } - - @Override - public char remove(KType key) { - throw readOnlyException(); - } - - @Override - public int indexOf(KType key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public char indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public char indexReplace(int index, char newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, KType key, char value) { - throw readOnlyException(); - } - - @Override - public char indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final ObjectCharCursor cursor = new ObjectCharCursor(); - private int index; - - @Override - protected ObjectCharCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = (KType) delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractObjectCollection - implements ObjectLookupContainer { - private final SortedIterationObjectCharHashMap owner = - SortedIterationObjectCharHashMap.this; - - @Override - public boolean contains(KType e) { - return owner.containsKey(e); - } - - @Override - public > T forEach(final T procedure) { - owner.forEach((ObjectCharProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public > T forEach(final T predicate) { - owner.forEach((ObjectCharPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator> iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final KType e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator> { - private final ObjectCursor cursor = new ObjectCursor(); - private int index; - - @Override - protected ObjectCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = (KType) delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractCharCollection { - private final SortedIterationObjectCharHashMap owner = - SortedIterationObjectCharHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(char value) { - for (ObjectCharCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((ObjectCharProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((ObjectCharPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final char e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final CharCursor cursor = new CharCursor(); - private int index; - - @Override - protected CharCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationObjectDoubleHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationObjectDoubleHashMap.java deleted file mode 100755 index f593fe6a..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationObjectDoubleHashMap.java +++ /dev/null @@ -1,447 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Comparator; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link ObjectDoubleHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link ObjectDoubleHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationObjectDoubleHashMap implements ObjectDoubleMap { - public final ObjectDoubleHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationObjectDoubleHashMap( - ObjectDoubleHashMap delegate, Comparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationObjectDoubleHashMap( - ObjectDoubleHashMap delegate, ObjectDoubleComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final KType[] keys = (KType[]) delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!(((KType) keys[keyIndex]) == null)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, Comparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - KType[] keys = (KType[]) delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, ObjectDoubleComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final KType[] keys = (KType[]) delegate.keys; - final double[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator> iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(KType key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(ObjectContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectDoublePredicate predicate) { - throw readOnlyException(); - } - - @Override - public > T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final KType[] keys = (KType[]) delegate.keys; - final double[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public > T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final KType[] keys = (KType[]) delegate.keys; - final double[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public ObjectCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public DoubleContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public double get(KType key) { - return delegate.get(key); - } - - @Override - public double getOrDefault(KType key, double defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public double put(KType key, double value) { - throw readOnlyException(); - } - - @Override - public int putAll(ObjectDoubleAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable> iterable) { - throw readOnlyException(); - } - - @Override - public double putOrAdd(KType key, double putValue, double incrementValue) { - throw readOnlyException(); - } - - @Override - public double addTo(KType key, double additionValue) { - throw readOnlyException(); - } - - @Override - public double remove(KType key) { - throw readOnlyException(); - } - - @Override - public int indexOf(KType key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public double indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public double indexReplace(int index, double newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, KType key, double value) { - throw readOnlyException(); - } - - @Override - public double indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final ObjectDoubleCursor cursor = new ObjectDoubleCursor(); - private int index; - - @Override - protected ObjectDoubleCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = (KType) delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractObjectCollection - implements ObjectLookupContainer { - private final SortedIterationObjectDoubleHashMap owner = - SortedIterationObjectDoubleHashMap.this; - - @Override - public boolean contains(KType e) { - return owner.containsKey(e); - } - - @Override - public > T forEach(final T procedure) { - owner.forEach((ObjectDoubleProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public > T forEach(final T predicate) { - owner.forEach((ObjectDoublePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator> iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final KType e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator> { - private final ObjectCursor cursor = new ObjectCursor(); - private int index; - - @Override - protected ObjectCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = (KType) delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractDoubleCollection { - private final SortedIterationObjectDoubleHashMap owner = - SortedIterationObjectDoubleHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(double value) { - for (ObjectDoubleCursor c : owner) { - if ((Double.doubleToLongBits(value) == Double.doubleToLongBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((ObjectDoubleProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((ObjectDoublePredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final double e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final DoublePredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final DoubleCursor cursor = new DoubleCursor(); - private int index; - - @Override - protected DoubleCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationObjectFloatHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationObjectFloatHashMap.java deleted file mode 100755 index 4781dd42..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationObjectFloatHashMap.java +++ /dev/null @@ -1,447 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Comparator; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link ObjectFloatHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link ObjectFloatHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationObjectFloatHashMap implements ObjectFloatMap { - public final ObjectFloatHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationObjectFloatHashMap( - ObjectFloatHashMap delegate, Comparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationObjectFloatHashMap( - ObjectFloatHashMap delegate, ObjectFloatComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final KType[] keys = (KType[]) delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!(((KType) keys[keyIndex]) == null)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, Comparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - KType[] keys = (KType[]) delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, ObjectFloatComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final KType[] keys = (KType[]) delegate.keys; - final float[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator> iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(KType key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(ObjectContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectFloatPredicate predicate) { - throw readOnlyException(); - } - - @Override - public > T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final KType[] keys = (KType[]) delegate.keys; - final float[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public > T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final KType[] keys = (KType[]) delegate.keys; - final float[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public ObjectCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public FloatContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public float get(KType key) { - return delegate.get(key); - } - - @Override - public float getOrDefault(KType key, float defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public float put(KType key, float value) { - throw readOnlyException(); - } - - @Override - public int putAll(ObjectFloatAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable> iterable) { - throw readOnlyException(); - } - - @Override - public float putOrAdd(KType key, float putValue, float incrementValue) { - throw readOnlyException(); - } - - @Override - public float addTo(KType key, float additionValue) { - throw readOnlyException(); - } - - @Override - public float remove(KType key) { - throw readOnlyException(); - } - - @Override - public int indexOf(KType key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public float indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public float indexReplace(int index, float newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, KType key, float value) { - throw readOnlyException(); - } - - @Override - public float indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final ObjectFloatCursor cursor = new ObjectFloatCursor(); - private int index; - - @Override - protected ObjectFloatCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = (KType) delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractObjectCollection - implements ObjectLookupContainer { - private final SortedIterationObjectFloatHashMap owner = - SortedIterationObjectFloatHashMap.this; - - @Override - public boolean contains(KType e) { - return owner.containsKey(e); - } - - @Override - public > T forEach(final T procedure) { - owner.forEach((ObjectFloatProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public > T forEach(final T predicate) { - owner.forEach((ObjectFloatPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator> iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final KType e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator> { - private final ObjectCursor cursor = new ObjectCursor(); - private int index; - - @Override - protected ObjectCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = (KType) delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractFloatCollection { - private final SortedIterationObjectFloatHashMap owner = - SortedIterationObjectFloatHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(float value) { - for (ObjectFloatCursor c : owner) { - if ((Float.floatToIntBits(value) == Float.floatToIntBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((ObjectFloatProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((ObjectFloatPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final float e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final FloatPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final FloatCursor cursor = new FloatCursor(); - private int index; - - @Override - protected FloatCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationObjectIntHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationObjectIntHashMap.java deleted file mode 100755 index 84b1aad2..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationObjectIntHashMap.java +++ /dev/null @@ -1,447 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Comparator; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link ObjectIntHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link ObjectIntHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationObjectIntHashMap implements ObjectIntMap { - public final ObjectIntHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationObjectIntHashMap( - ObjectIntHashMap delegate, Comparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationObjectIntHashMap( - ObjectIntHashMap delegate, ObjectIntComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final KType[] keys = (KType[]) delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!(((KType) keys[keyIndex]) == null)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, Comparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - KType[] keys = (KType[]) delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, ObjectIntComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final KType[] keys = (KType[]) delegate.keys; - final int[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator> iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(KType key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(ObjectContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectIntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public > T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final KType[] keys = (KType[]) delegate.keys; - final int[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public > T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final KType[] keys = (KType[]) delegate.keys; - final int[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public ObjectCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public IntContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public int get(KType key) { - return delegate.get(key); - } - - @Override - public int getOrDefault(KType key, int defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public int put(KType key, int value) { - throw readOnlyException(); - } - - @Override - public int putAll(ObjectIntAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable> iterable) { - throw readOnlyException(); - } - - @Override - public int putOrAdd(KType key, int putValue, int incrementValue) { - throw readOnlyException(); - } - - @Override - public int addTo(KType key, int additionValue) { - throw readOnlyException(); - } - - @Override - public int remove(KType key) { - throw readOnlyException(); - } - - @Override - public int indexOf(KType key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public int indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public int indexReplace(int index, int newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, KType key, int value) { - throw readOnlyException(); - } - - @Override - public int indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final ObjectIntCursor cursor = new ObjectIntCursor(); - private int index; - - @Override - protected ObjectIntCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = (KType) delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractObjectCollection - implements ObjectLookupContainer { - private final SortedIterationObjectIntHashMap owner = - SortedIterationObjectIntHashMap.this; - - @Override - public boolean contains(KType e) { - return owner.containsKey(e); - } - - @Override - public > T forEach(final T procedure) { - owner.forEach((ObjectIntProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public > T forEach(final T predicate) { - owner.forEach((ObjectIntPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator> iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final KType e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator> { - private final ObjectCursor cursor = new ObjectCursor(); - private int index; - - @Override - protected ObjectCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = (KType) delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractIntCollection { - private final SortedIterationObjectIntHashMap owner = - SortedIterationObjectIntHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(int value) { - for (ObjectIntCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((ObjectIntProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((ObjectIntPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final int e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final IntCursor cursor = new IntCursor(); - private int index; - - @Override - protected IntCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationObjectLongHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationObjectLongHashMap.java deleted file mode 100755 index f353773c..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationObjectLongHashMap.java +++ /dev/null @@ -1,447 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Comparator; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link ObjectLongHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link ObjectLongHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationObjectLongHashMap implements ObjectLongMap { - public final ObjectLongHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationObjectLongHashMap( - ObjectLongHashMap delegate, Comparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationObjectLongHashMap( - ObjectLongHashMap delegate, ObjectLongComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final KType[] keys = (KType[]) delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!(((KType) keys[keyIndex]) == null)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, Comparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - KType[] keys = (KType[]) delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, ObjectLongComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final KType[] keys = (KType[]) delegate.keys; - final long[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator> iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(KType key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(ObjectContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectLongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public > T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final KType[] keys = (KType[]) delegate.keys; - final long[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public > T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final KType[] keys = (KType[]) delegate.keys; - final long[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public ObjectCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public LongContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public long get(KType key) { - return delegate.get(key); - } - - @Override - public long getOrDefault(KType key, long defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public long put(KType key, long value) { - throw readOnlyException(); - } - - @Override - public int putAll(ObjectLongAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable> iterable) { - throw readOnlyException(); - } - - @Override - public long putOrAdd(KType key, long putValue, long incrementValue) { - throw readOnlyException(); - } - - @Override - public long addTo(KType key, long additionValue) { - throw readOnlyException(); - } - - @Override - public long remove(KType key) { - throw readOnlyException(); - } - - @Override - public int indexOf(KType key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public long indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public long indexReplace(int index, long newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, KType key, long value) { - throw readOnlyException(); - } - - @Override - public long indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final ObjectLongCursor cursor = new ObjectLongCursor(); - private int index; - - @Override - protected ObjectLongCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = (KType) delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractObjectCollection - implements ObjectLookupContainer { - private final SortedIterationObjectLongHashMap owner = - SortedIterationObjectLongHashMap.this; - - @Override - public boolean contains(KType e) { - return owner.containsKey(e); - } - - @Override - public > T forEach(final T procedure) { - owner.forEach((ObjectLongProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public > T forEach(final T predicate) { - owner.forEach((ObjectLongPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator> iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final KType e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator> { - private final ObjectCursor cursor = new ObjectCursor(); - private int index; - - @Override - protected ObjectCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = (KType) delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractLongCollection { - private final SortedIterationObjectLongHashMap owner = - SortedIterationObjectLongHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(long value) { - for (ObjectLongCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((ObjectLongProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((ObjectLongPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final long e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final LongCursor cursor = new LongCursor(); - private int index; - - @Override - protected LongCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationObjectObjectHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationObjectObjectHashMap.java deleted file mode 100755 index d1c881be..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationObjectObjectHashMap.java +++ /dev/null @@ -1,440 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Comparator; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link ObjectObjectHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link ObjectObjectHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationObjectObjectHashMap - implements ObjectObjectMap { - public final ObjectObjectHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationObjectObjectHashMap( - ObjectObjectHashMap delegate, Comparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationObjectObjectHashMap( - ObjectObjectHashMap delegate, ObjectObjectComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final KType[] keys = (KType[]) delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!(((KType) keys[keyIndex]) == null)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, Comparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - KType[] keys = (KType[]) delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder( - int[] entryIndexes, ObjectObjectComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final KType[] keys = (KType[]) delegate.keys; - final VType[] values = (VType[]) delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator> iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(KType key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(ObjectContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public > T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final KType[] keys = (KType[]) delegate.keys; - final VType[] values = (VType[]) delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public > T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final KType[] keys = (KType[]) delegate.keys; - final VType[] values = (VType[]) delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public ObjectCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public ObjectContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public VType get(KType key) { - return delegate.get(key); - } - - @Override - public VType getOrDefault(KType key, VType defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public VType put(KType key, VType value) { - throw readOnlyException(); - } - - @Override - public int putAll(ObjectObjectAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll( - Iterable> iterable) { - throw readOnlyException(); - } - - @Override - public VType remove(KType key) { - throw readOnlyException(); - } - - @Override - public int indexOf(KType key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public VType indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public VType indexReplace(int index, VType newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, KType key, VType value) { - throw readOnlyException(); - } - - @Override - public VType indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final ObjectObjectCursor cursor = new ObjectObjectCursor(); - private int index; - - @Override - protected ObjectObjectCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = (KType) delegate.keys[slot]; - cursor.value = (VType) delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractObjectCollection - implements ObjectLookupContainer { - private final SortedIterationObjectObjectHashMap owner = - SortedIterationObjectObjectHashMap.this; - - @Override - public boolean contains(KType e) { - return owner.containsKey(e); - } - - @Override - public > T forEach(final T procedure) { - owner.forEach((ObjectObjectProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public > T forEach(final T predicate) { - owner.forEach((ObjectObjectPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator> iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final KType e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator> { - private final ObjectCursor cursor = new ObjectCursor(); - private int index; - - @Override - protected ObjectCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = (KType) delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractObjectCollection { - private final SortedIterationObjectObjectHashMap owner = - SortedIterationObjectObjectHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(VType value) { - for (ObjectObjectCursor c : owner) { - if (java.util.Objects.equals(value, c.value)) { - return true; - } - } - return false; - } - - @Override - public > T forEach(T procedure) { - owner.forEach((ObjectObjectProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public > T forEach(T predicate) { - owner.forEach((ObjectObjectPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator> iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final VType e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator> { - private final ObjectCursor cursor = new ObjectCursor(); - private int index; - - @Override - protected ObjectCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = (VType) delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationObjectShortHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationObjectShortHashMap.java deleted file mode 100755 index 69e7bd7e..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationObjectShortHashMap.java +++ /dev/null @@ -1,447 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Comparator; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link ObjectShortHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link ObjectShortHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationObjectShortHashMap implements ObjectShortMap { - public final ObjectShortHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationObjectShortHashMap( - ObjectShortHashMap delegate, Comparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationObjectShortHashMap( - ObjectShortHashMap delegate, ObjectShortComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final KType[] keys = (KType[]) delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!(((KType) keys[keyIndex]) == null)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, Comparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - KType[] keys = (KType[]) delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, ObjectShortComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final KType[] keys = (KType[]) delegate.keys; - final short[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator> iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(KType key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(ObjectContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public > T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final KType[] keys = (KType[]) delegate.keys; - final short[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public > T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final KType[] keys = (KType[]) delegate.keys; - final short[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public ObjectCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public ShortContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public short get(KType key) { - return delegate.get(key); - } - - @Override - public short getOrDefault(KType key, short defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public short put(KType key, short value) { - throw readOnlyException(); - } - - @Override - public int putAll(ObjectShortAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable> iterable) { - throw readOnlyException(); - } - - @Override - public short putOrAdd(KType key, short putValue, short incrementValue) { - throw readOnlyException(); - } - - @Override - public short addTo(KType key, short additionValue) { - throw readOnlyException(); - } - - @Override - public short remove(KType key) { - throw readOnlyException(); - } - - @Override - public int indexOf(KType key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public short indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public short indexReplace(int index, short newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, KType key, short value) { - throw readOnlyException(); - } - - @Override - public short indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final ObjectShortCursor cursor = new ObjectShortCursor(); - private int index; - - @Override - protected ObjectShortCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = (KType) delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractObjectCollection - implements ObjectLookupContainer { - private final SortedIterationObjectShortHashMap owner = - SortedIterationObjectShortHashMap.this; - - @Override - public boolean contains(KType e) { - return owner.containsKey(e); - } - - @Override - public > T forEach(final T procedure) { - owner.forEach((ObjectShortProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public > T forEach(final T predicate) { - owner.forEach((ObjectShortPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator> iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final KType e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator> { - private final ObjectCursor cursor = new ObjectCursor(); - private int index; - - @Override - protected ObjectCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = (KType) delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractShortCollection { - private final SortedIterationObjectShortHashMap owner = - SortedIterationObjectShortHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(short value) { - for (ObjectShortCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((ObjectShortProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((ObjectShortPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final short e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ShortCursor cursor = new ShortCursor(); - private int index; - - @Override - protected ShortCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationShortByteHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationShortByteHashMap.java deleted file mode 100755 index 478d3dec..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationShortByteHashMap.java +++ /dev/null @@ -1,442 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link ShortByteHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link ShortByteHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationShortByteHashMap implements ShortByteMap { - public final ShortByteHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationShortByteHashMap(ShortByteHashMap delegate, ShortComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationShortByteHashMap( - ShortByteHashMap delegate, ShortByteComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final short[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, ShortComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - short[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, ShortByteComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final short[] keys = delegate.keys; - final byte[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(short key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(ShortContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortBytePredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final short[] keys = delegate.keys; - final byte[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final short[] keys = delegate.keys; - final byte[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public ShortCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public ByteContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public byte get(short key) { - return delegate.get(key); - } - - @Override - public byte getOrDefault(short key, byte defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public byte put(short key, byte value) { - throw readOnlyException(); - } - - @Override - public int putAll(ShortByteAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public byte putOrAdd(short key, byte putValue, byte incrementValue) { - throw readOnlyException(); - } - - @Override - public byte addTo(short key, byte additionValue) { - throw readOnlyException(); - } - - @Override - public byte remove(short key) { - throw readOnlyException(); - } - - @Override - public int indexOf(short key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public byte indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public byte indexReplace(int index, byte newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, short key, byte value) { - throw readOnlyException(); - } - - @Override - public byte indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final ShortByteCursor cursor = new ShortByteCursor(); - private int index; - - @Override - protected ShortByteCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractShortCollection - implements ShortLookupContainer { - private final SortedIterationShortByteHashMap owner = SortedIterationShortByteHashMap.this; - - @Override - public boolean contains(short e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((ShortByteProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((ShortBytePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final short e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final ShortCursor cursor = new ShortCursor(); - private int index; - - @Override - protected ShortCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractByteCollection { - private final SortedIterationShortByteHashMap owner = SortedIterationShortByteHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(byte value) { - for (ShortByteCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((ShortByteProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((ShortBytePredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final byte e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final BytePredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ByteCursor cursor = new ByteCursor(); - private int index; - - @Override - protected ByteCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationShortCharHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationShortCharHashMap.java deleted file mode 100755 index 3f4ad7df..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationShortCharHashMap.java +++ /dev/null @@ -1,442 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link ShortCharHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link ShortCharHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationShortCharHashMap implements ShortCharMap { - public final ShortCharHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationShortCharHashMap(ShortCharHashMap delegate, ShortComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationShortCharHashMap( - ShortCharHashMap delegate, ShortCharComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final short[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, ShortComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - short[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, ShortCharComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final short[] keys = delegate.keys; - final char[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(short key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(ShortContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortCharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final short[] keys = delegate.keys; - final char[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final short[] keys = delegate.keys; - final char[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public ShortCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public CharContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public char get(short key) { - return delegate.get(key); - } - - @Override - public char getOrDefault(short key, char defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public char put(short key, char value) { - throw readOnlyException(); - } - - @Override - public int putAll(ShortCharAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public char putOrAdd(short key, char putValue, char incrementValue) { - throw readOnlyException(); - } - - @Override - public char addTo(short key, char additionValue) { - throw readOnlyException(); - } - - @Override - public char remove(short key) { - throw readOnlyException(); - } - - @Override - public int indexOf(short key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public char indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public char indexReplace(int index, char newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, short key, char value) { - throw readOnlyException(); - } - - @Override - public char indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final ShortCharCursor cursor = new ShortCharCursor(); - private int index; - - @Override - protected ShortCharCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractShortCollection - implements ShortLookupContainer { - private final SortedIterationShortCharHashMap owner = SortedIterationShortCharHashMap.this; - - @Override - public boolean contains(short e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((ShortCharProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((ShortCharPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final short e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final ShortCursor cursor = new ShortCursor(); - private int index; - - @Override - protected ShortCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractCharCollection { - private final SortedIterationShortCharHashMap owner = SortedIterationShortCharHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(char value) { - for (ShortCharCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((ShortCharProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((ShortCharPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final char e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final CharPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final CharCursor cursor = new CharCursor(); - private int index; - - @Override - protected CharCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationShortDoubleHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationShortDoubleHashMap.java deleted file mode 100755 index a9ae3265..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationShortDoubleHashMap.java +++ /dev/null @@ -1,443 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link ShortDoubleHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link ShortDoubleHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationShortDoubleHashMap implements ShortDoubleMap { - public final ShortDoubleHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationShortDoubleHashMap( - ShortDoubleHashMap delegate, ShortComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationShortDoubleHashMap( - ShortDoubleHashMap delegate, ShortDoubleComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final short[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, ShortComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - short[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, ShortDoubleComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final short[] keys = delegate.keys; - final double[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(short key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(ShortContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortDoublePredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final short[] keys = delegate.keys; - final double[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final short[] keys = delegate.keys; - final double[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public ShortCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public DoubleContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public double get(short key) { - return delegate.get(key); - } - - @Override - public double getOrDefault(short key, double defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public double put(short key, double value) { - throw readOnlyException(); - } - - @Override - public int putAll(ShortDoubleAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public double putOrAdd(short key, double putValue, double incrementValue) { - throw readOnlyException(); - } - - @Override - public double addTo(short key, double additionValue) { - throw readOnlyException(); - } - - @Override - public double remove(short key) { - throw readOnlyException(); - } - - @Override - public int indexOf(short key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public double indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public double indexReplace(int index, double newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, short key, double value) { - throw readOnlyException(); - } - - @Override - public double indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final ShortDoubleCursor cursor = new ShortDoubleCursor(); - private int index; - - @Override - protected ShortDoubleCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractShortCollection - implements ShortLookupContainer { - private final SortedIterationShortDoubleHashMap owner = SortedIterationShortDoubleHashMap.this; - - @Override - public boolean contains(short e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((ShortDoubleProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((ShortDoublePredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final short e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final ShortCursor cursor = new ShortCursor(); - private int index; - - @Override - protected ShortCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractDoubleCollection { - private final SortedIterationShortDoubleHashMap owner = SortedIterationShortDoubleHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(double value) { - for (ShortDoubleCursor c : owner) { - if ((Double.doubleToLongBits(value) == Double.doubleToLongBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((ShortDoubleProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((ShortDoublePredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final double e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final DoublePredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final DoubleCursor cursor = new DoubleCursor(); - private int index; - - @Override - protected DoubleCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationShortFloatHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationShortFloatHashMap.java deleted file mode 100755 index bad3ad03..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationShortFloatHashMap.java +++ /dev/null @@ -1,442 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link ShortFloatHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link ShortFloatHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationShortFloatHashMap implements ShortFloatMap { - public final ShortFloatHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationShortFloatHashMap(ShortFloatHashMap delegate, ShortComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationShortFloatHashMap( - ShortFloatHashMap delegate, ShortFloatComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final short[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, ShortComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - short[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, ShortFloatComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final short[] keys = delegate.keys; - final float[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(short key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(ShortContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortFloatPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final short[] keys = delegate.keys; - final float[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final short[] keys = delegate.keys; - final float[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public ShortCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public FloatContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public float get(short key) { - return delegate.get(key); - } - - @Override - public float getOrDefault(short key, float defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public float put(short key, float value) { - throw readOnlyException(); - } - - @Override - public int putAll(ShortFloatAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public float putOrAdd(short key, float putValue, float incrementValue) { - throw readOnlyException(); - } - - @Override - public float addTo(short key, float additionValue) { - throw readOnlyException(); - } - - @Override - public float remove(short key) { - throw readOnlyException(); - } - - @Override - public int indexOf(short key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public float indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public float indexReplace(int index, float newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, short key, float value) { - throw readOnlyException(); - } - - @Override - public float indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final ShortFloatCursor cursor = new ShortFloatCursor(); - private int index; - - @Override - protected ShortFloatCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractShortCollection - implements ShortLookupContainer { - private final SortedIterationShortFloatHashMap owner = SortedIterationShortFloatHashMap.this; - - @Override - public boolean contains(short e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((ShortFloatProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((ShortFloatPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final short e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final ShortCursor cursor = new ShortCursor(); - private int index; - - @Override - protected ShortCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractFloatCollection { - private final SortedIterationShortFloatHashMap owner = SortedIterationShortFloatHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(float value) { - for (ShortFloatCursor c : owner) { - if ((Float.floatToIntBits(value) == Float.floatToIntBits(c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((ShortFloatProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((ShortFloatPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final float e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final FloatPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final FloatCursor cursor = new FloatCursor(); - private int index; - - @Override - protected FloatCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationShortIntHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationShortIntHashMap.java deleted file mode 100755 index 501b0a07..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationShortIntHashMap.java +++ /dev/null @@ -1,441 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link ShortIntHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link ShortIntHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationShortIntHashMap implements ShortIntMap { - public final ShortIntHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationShortIntHashMap(ShortIntHashMap delegate, ShortComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationShortIntHashMap(ShortIntHashMap delegate, ShortIntComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final short[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, ShortComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - short[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, ShortIntComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final short[] keys = delegate.keys; - final int[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(short key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(ShortContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortIntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final short[] keys = delegate.keys; - final int[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final short[] keys = delegate.keys; - final int[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public ShortCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public IntContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public int get(short key) { - return delegate.get(key); - } - - @Override - public int getOrDefault(short key, int defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public int put(short key, int value) { - throw readOnlyException(); - } - - @Override - public int putAll(ShortIntAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public int putOrAdd(short key, int putValue, int incrementValue) { - throw readOnlyException(); - } - - @Override - public int addTo(short key, int additionValue) { - throw readOnlyException(); - } - - @Override - public int remove(short key) { - throw readOnlyException(); - } - - @Override - public int indexOf(short key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public int indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public int indexReplace(int index, int newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, short key, int value) { - throw readOnlyException(); - } - - @Override - public int indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final ShortIntCursor cursor = new ShortIntCursor(); - private int index; - - @Override - protected ShortIntCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractShortCollection - implements ShortLookupContainer { - private final SortedIterationShortIntHashMap owner = SortedIterationShortIntHashMap.this; - - @Override - public boolean contains(short e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((ShortIntProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((ShortIntPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final short e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final ShortCursor cursor = new ShortCursor(); - private int index; - - @Override - protected ShortCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractIntCollection { - private final SortedIterationShortIntHashMap owner = SortedIterationShortIntHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(int value) { - for (ShortIntCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((ShortIntProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((ShortIntPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final int e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final IntPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final IntCursor cursor = new IntCursor(); - private int index; - - @Override - protected IntCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationShortLongHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationShortLongHashMap.java deleted file mode 100755 index 5aae2e1f..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationShortLongHashMap.java +++ /dev/null @@ -1,442 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link ShortLongHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link ShortLongHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationShortLongHashMap implements ShortLongMap { - public final ShortLongHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationShortLongHashMap(ShortLongHashMap delegate, ShortComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationShortLongHashMap( - ShortLongHashMap delegate, ShortLongComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final short[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, ShortComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - short[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, ShortLongComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final short[] keys = delegate.keys; - final long[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(short key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(ShortContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortLongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final short[] keys = delegate.keys; - final long[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final short[] keys = delegate.keys; - final long[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public ShortCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public LongContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public long get(short key) { - return delegate.get(key); - } - - @Override - public long getOrDefault(short key, long defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public long put(short key, long value) { - throw readOnlyException(); - } - - @Override - public int putAll(ShortLongAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public long putOrAdd(short key, long putValue, long incrementValue) { - throw readOnlyException(); - } - - @Override - public long addTo(short key, long additionValue) { - throw readOnlyException(); - } - - @Override - public long remove(short key) { - throw readOnlyException(); - } - - @Override - public int indexOf(short key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public long indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public long indexReplace(int index, long newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, short key, long value) { - throw readOnlyException(); - } - - @Override - public long indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final ShortLongCursor cursor = new ShortLongCursor(); - private int index; - - @Override - protected ShortLongCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractShortCollection - implements ShortLookupContainer { - private final SortedIterationShortLongHashMap owner = SortedIterationShortLongHashMap.this; - - @Override - public boolean contains(short e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((ShortLongProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((ShortLongPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final short e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final ShortCursor cursor = new ShortCursor(); - private int index; - - @Override - protected ShortCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractLongCollection { - private final SortedIterationShortLongHashMap owner = SortedIterationShortLongHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(long value) { - for (ShortLongCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((ShortLongProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((ShortLongPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final long e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final LongPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final LongCursor cursor = new LongCursor(); - private int index; - - @Override - protected LongCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationShortObjectHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationShortObjectHashMap.java deleted file mode 100755 index 989690ae..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationShortObjectHashMap.java +++ /dev/null @@ -1,436 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link ShortObjectHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link ShortObjectHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@SuppressWarnings("unchecked") -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationShortObjectHashMap implements ShortObjectMap { - public final ShortObjectHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationShortObjectHashMap( - ShortObjectHashMap delegate, ShortComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationShortObjectHashMap( - ShortObjectHashMap delegate, ShortObjectComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final short[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, ShortComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - short[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, ShortObjectComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final short[] keys = delegate.keys; - final VType[] values = (VType[]) delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator> iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(short key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(ShortContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public > T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final short[] keys = delegate.keys; - final VType[] values = (VType[]) delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public > T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final short[] keys = delegate.keys; - final VType[] values = (VType[]) delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public ShortCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public ObjectContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public VType get(short key) { - return delegate.get(key); - } - - @Override - public VType getOrDefault(short key, VType defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public VType put(short key, VType value) { - throw readOnlyException(); - } - - @Override - public int putAll(ShortObjectAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable> iterable) { - throw readOnlyException(); - } - - @Override - public VType remove(short key) { - throw readOnlyException(); - } - - @Override - public int indexOf(short key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public VType indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public VType indexReplace(int index, VType newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, short key, VType value) { - throw readOnlyException(); - } - - @Override - public VType indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator> { - private final ShortObjectCursor cursor = new ShortObjectCursor(); - private int index; - - @Override - protected ShortObjectCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = (VType) delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractShortCollection - implements ShortLookupContainer { - private final SortedIterationShortObjectHashMap owner = - SortedIterationShortObjectHashMap.this; - - @Override - public boolean contains(short e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((ShortObjectProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((ShortObjectPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final short e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final ShortCursor cursor = new ShortCursor(); - private int index; - - @Override - protected ShortCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractObjectCollection { - private final SortedIterationShortObjectHashMap owner = - SortedIterationShortObjectHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(VType value) { - for (ShortObjectCursor c : owner) { - if (java.util.Objects.equals(value, c.value)) { - return true; - } - } - return false; - } - - @Override - public > T forEach(T procedure) { - owner.forEach((ShortObjectProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public > T forEach(T predicate) { - owner.forEach((ShortObjectPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator> iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final VType e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final ObjectPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator> { - private final ObjectCursor cursor = new ObjectCursor(); - private int index; - - @Override - protected ObjectCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = (VType) delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/SortedIterationShortShortHashMap.java b/src/main/java/com/carrotsearch/hppc/SortedIterationShortShortHashMap.java deleted file mode 100755 index d7266230..00000000 --- a/src/main/java/com/carrotsearch/hppc/SortedIterationShortShortHashMap.java +++ /dev/null @@ -1,442 +0,0 @@ -package com.carrotsearch.hppc; - -import com.carrotsearch.hppc.comparators.*; -import com.carrotsearch.hppc.cursors.*; -import com.carrotsearch.hppc.predicates.*; -import com.carrotsearch.hppc.procedures.*; -import com.carrotsearch.hppc.sorting.QuickSort; -import java.util.Iterator; -import java.util.function.IntBinaryOperator; - -/** - * Read-only view with sorted iteration order on a delegate {@link ShortShortHashMap}. - * - *

In its constructor, this view creates its own iteration order array and sorts it, which is in - * O(n.log(n)) of the size of the delegate map. Afterward, calls to any method have the same - * performance as the delegate map. - * - *

This view is read-only. In addition, the delegate map must not be modified while the view is - * used, otherwise the iteration is undefined. - * - *

Since this view provides a fixed iteration order, it must not be used to add entries to - * another {@link ShortShortHashMap} as this may result in a runtime deadlock. See HPPC-103 and HPPC-186 for more information. - */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "SortedIterationKTypeVTypeHashMap.java") -public class SortedIterationShortShortHashMap implements ShortShortMap { - public final ShortShortHashMap delegate; - public final int[] iterationOrder; - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on the keys. - */ - public SortedIterationShortShortHashMap(ShortShortHashMap delegate, ShortComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - /** - * Creates a read-only view with sorted iteration order on the given delegate map. The ordering is - * based on the provided comparator on keys and values. - */ - public SortedIterationShortShortHashMap( - ShortShortHashMap delegate, ShortShortComparator comparator) { - this.delegate = delegate; - this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator); - } - - private int[] createEntryIndexes() { - final short[] keys = delegate.keys; - final int size = delegate.size(); - int[] entryIndexes = new int[size]; - int entry = 0; - if (delegate.hasEmptyKey) { - entryIndexes[entry++] = delegate.mask + 1; - } - for (int keyIndex = 0; entry < size; keyIndex++) { - if (!((keys[keyIndex]) == 0)) { - entryIndexes[entry++] = keyIndex; - } - } - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on the keys. */ - protected int[] sortIterationOrder(int[] entryIndexes, ShortComparator comparator) { - QuickSort.sort( - entryIndexes, - (i, j) -> { - short[] keys = delegate.keys; - return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]); - }); - return entryIndexes; - } - - /** Sort the iteration order array based on the provided comparator on keys and values. */ - protected int[] sortIterationOrder(int[] entryIndexes, ShortShortComparator comparator) { - QuickSort.sort( - entryIndexes, - new IntBinaryOperator() { - final short[] keys = delegate.keys; - final short[] values = delegate.values; - - @Override - public int applyAsInt(int i, int j) { - int index1 = entryIndexes[i]; - int index2 = entryIndexes[j]; - return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]); - } - }); - return entryIndexes; - } - - @Override - public Iterator iterator() { - assert checkUnmodified(); - return new EntryIterator(); - } - - @Override - public boolean containsKey(short key) { - return delegate.containsKey(key); - } - - @Override - public int size() { - assert checkUnmodified(); - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public int removeAll(ShortContainer container) { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public T forEach(T procedure) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final short[] keys = delegate.keys; - final short[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - procedure.apply(keys[slot], values[slot]); - } - return procedure; - } - - @Override - public T forEach(T predicate) { - assert checkUnmodified(); - final int[] iterationOrder = this.iterationOrder; - final short[] keys = delegate.keys; - final short[] values = delegate.values; - for (int i = 0, size = size(); i < size; i++) { - int slot = iterationOrder[i]; - if (!predicate.apply(keys[slot], values[slot])) { - break; - } - } - return predicate; - } - - @Override - public ShortCollection keys() { - assert checkUnmodified(); - return new KeysContainer(); - } - - @Override - public ShortContainer values() { - assert checkUnmodified(); - return new ValuesContainer(); - } - - @Override - public short get(short key) { - return delegate.get(key); - } - - @Override - public short getOrDefault(short key, short defaultValue) { - return delegate.getOrDefault(key, defaultValue); - } - - @Override - public short put(short key, short value) { - throw readOnlyException(); - } - - @Override - public int putAll(ShortShortAssociativeContainer container) { - throw readOnlyException(); - } - - @Override - public int putAll(Iterable iterable) { - throw readOnlyException(); - } - - @Override - public short putOrAdd(short key, short putValue, short incrementValue) { - throw readOnlyException(); - } - - @Override - public short addTo(short key, short additionValue) { - throw readOnlyException(); - } - - @Override - public short remove(short key) { - throw readOnlyException(); - } - - @Override - public int indexOf(short key) { - return delegate.indexOf(key); - } - - @Override - public boolean indexExists(int index) { - return delegate.indexExists(index); - } - - @Override - public short indexGet(int index) { - return delegate.indexGet(index); - } - - @Override - public short indexReplace(int index, short newValue) { - throw readOnlyException(); - } - - @Override - public void indexInsert(int index, short key, short value) { - throw readOnlyException(); - } - - @Override - public short indexRemove(int index) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public String visualizeKeyDistribution(int characters) { - return delegate.visualizeKeyDistribution(characters); - } - - private static RuntimeException readOnlyException() { - throw new UnsupportedOperationException("Read-only view cannot be modified"); - } - - private boolean checkUnmodified() { - // Cheap size comparison. - // We could also check the hashcode, but this is heavy for a frequent check. - assert delegate.size() == iterationOrder.length - : "The delegate map changed; this is not supported by this read-only view"; - return true; - } - - /** An iterator implementation for {@link #iterator}. */ - private final class EntryIterator extends AbstractIterator { - private final ShortShortCursor cursor = new ShortShortCursor(); - private int index; - - @Override - protected ShortShortCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.key = delegate.keys[slot]; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the keys in sorted order. */ - private final class KeysContainer extends AbstractShortCollection - implements ShortLookupContainer { - private final SortedIterationShortShortHashMap owner = SortedIterationShortShortHashMap.this; - - @Override - public boolean contains(short e) { - return owner.containsKey(e); - } - - @Override - public T forEach(final T procedure) { - owner.forEach((ShortShortProcedure) (k, v) -> procedure.apply(k)); - return procedure; - } - - @Override - public T forEach(final T predicate) { - owner.forEach((ShortShortPredicate) (key, value) -> predicate.apply(key)); - return predicate; - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public Iterator iterator() { - return new KeysIterator(); - } - - @Override - public int size() { - return owner.size(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - - @Override - public int removeAll(ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public int removeAll(final short e) { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned keys. */ - private final class KeysIterator extends AbstractIterator { - private final ShortCursor cursor = new ShortCursor(); - private int index; - - @Override - protected ShortCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.keys[slot]; - return cursor; - } - return done(); - } - } - - /** A view of the values in sorted order. */ - private final class ValuesContainer extends AbstractShortCollection { - private final SortedIterationShortShortHashMap owner = SortedIterationShortShortHashMap.this; - - @Override - public int size() { - return owner.size(); - } - - @Override - public boolean isEmpty() { - return owner.isEmpty(); - } - - @Override - public boolean contains(short value) { - for (ShortShortCursor c : owner) { - if (((value) == (c.value))) { - return true; - } - } - return false; - } - - @Override - public T forEach(T procedure) { - owner.forEach((ShortShortProcedure) (k, v) -> procedure.apply(v)); - return procedure; - } - - @Override - public T forEach(T predicate) { - owner.forEach((ShortShortPredicate) (k, v) -> predicate.apply(v)); - return predicate; - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public int removeAll(final short e) { - throw readOnlyException(); - } - - @Override - public int removeAll(final ShortPredicate predicate) { - throw readOnlyException(); - } - - @Override - public void clear() { - throw readOnlyException(); - } - - @Override - public void release() { - throw readOnlyException(); - } - } - - /** A sorted iterator over the set of assigned values. */ - private final class ValuesIterator extends AbstractIterator { - private final ShortCursor cursor = new ShortCursor(); - private int index; - - @Override - protected ShortCursor fetch() { - if (index < iterationOrder.length) { - int slot = iterationOrder[index++]; - cursor.index = slot; - cursor.value = delegate.values[slot]; - return cursor; - } - return done(); - } - } -} diff --git a/src/main/java/com/carrotsearch/hppc/XorShift128P.java b/src/main/java/com/carrotsearch/hppc/XorShift128P.java deleted file mode 100755 index e9db0a0d..00000000 --- a/src/main/java/com/carrotsearch/hppc/XorShift128P.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc; - -import java.util.Random; - -/** - * A fast pseudo-random number generator. For simplicity, we do not implement all of {@link Random} - * methods. - * - * @see "https://xorshift.di.unimi.it/" - * @see "https://xorshift.di.unimi.it/xorshift128plus.c" - */ -public class XorShift128P { - /* - * 128 bits of state. - */ - private long state0, state1; - - public XorShift128P(long seed) { - state0 = notZero(BitMixer.mix64(seed)); - state1 = notZero(BitMixer.mix64(seed + 1)); - } - - public XorShift128P() { - this(Containers.randomSeed64()); - } - - public long nextLong() { - long s1 = state0; - long s0 = state1; - state0 = s0; - s1 ^= s1 << 23; - return (state1 = (s1 ^ s0 ^ (s1 >>> 17) ^ (s0 >>> 26))) + s0; - } - - public int nextInt() { - return (int) nextLong(); - } - - private static long notZero(long value) { - return value == 0 ? 0xdeadbeefbabeL : value; - } - - public int nextInt(int bound) { - if (bound <= 0) { - throw new IllegalArgumentException(); - } - - int r = (nextInt() >>> 1); - int m = bound - 1; - if ((bound & m) == 0) { - r = (int) ((bound * (long) r) >> 31); - } else { - for (int u = r; u - (r = u % bound) + m < 0; u = nextInt() >>> 1) {} - } - - return r; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ByteByteComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ByteByteComparator.java deleted file mode 100755 index 734e028b..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ByteByteComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two byte, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ByteByteComparator { - int compare(byte k1, byte v1, byte k2, byte v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ByteCharComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ByteCharComparator.java deleted file mode 100755 index 6682edd4..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ByteCharComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two byte, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ByteCharComparator { - int compare(byte k1, char v1, byte k2, char v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ByteComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ByteComparator.java deleted file mode 100755 index 0ff9f381..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ByteComparator.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two byte values. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeComparator.java") -public interface ByteComparator { - int compare(byte a, byte b); - - static ByteComparator naturalOrder() { - return Byte::compare; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ByteDoubleComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ByteDoubleComparator.java deleted file mode 100755 index 84b6105f..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ByteDoubleComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two byte, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ByteDoubleComparator { - int compare(byte k1, double v1, byte k2, double v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ByteFloatComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ByteFloatComparator.java deleted file mode 100755 index a54901d6..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ByteFloatComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two byte, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ByteFloatComparator { - int compare(byte k1, float v1, byte k2, float v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ByteIntComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ByteIntComparator.java deleted file mode 100755 index e2fed664..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ByteIntComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two byte, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ByteIntComparator { - int compare(byte k1, int v1, byte k2, int v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ByteLongComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ByteLongComparator.java deleted file mode 100755 index 5684a8bc..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ByteLongComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two byte, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ByteLongComparator { - int compare(byte k1, long v1, byte k2, long v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ByteObjectComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ByteObjectComparator.java deleted file mode 100755 index fc1c19a8..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ByteObjectComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two byte, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ByteObjectComparator { - int compare(byte k1, VType v1, byte k2, VType v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ByteShortComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ByteShortComparator.java deleted file mode 100755 index 154a2167..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ByteShortComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two byte, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ByteShortComparator { - int compare(byte k1, short v1, byte k2, short v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/CharByteComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/CharByteComparator.java deleted file mode 100755 index 807f0e0c..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/CharByteComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two char, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface CharByteComparator { - int compare(char k1, byte v1, char k2, byte v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/CharCharComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/CharCharComparator.java deleted file mode 100755 index 0f790620..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/CharCharComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two char, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface CharCharComparator { - int compare(char k1, char v1, char k2, char v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/CharComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/CharComparator.java deleted file mode 100755 index acedac68..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/CharComparator.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two char values. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeComparator.java") -public interface CharComparator { - int compare(char a, char b); - - static CharComparator naturalOrder() { - return Character::compare; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/CharDoubleComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/CharDoubleComparator.java deleted file mode 100755 index 486620ca..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/CharDoubleComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two char, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface CharDoubleComparator { - int compare(char k1, double v1, char k2, double v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/CharFloatComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/CharFloatComparator.java deleted file mode 100755 index 47bc5950..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/CharFloatComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two char, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface CharFloatComparator { - int compare(char k1, float v1, char k2, float v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/CharIntComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/CharIntComparator.java deleted file mode 100755 index c2a225d4..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/CharIntComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two char, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface CharIntComparator { - int compare(char k1, int v1, char k2, int v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/CharLongComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/CharLongComparator.java deleted file mode 100755 index 390998ec..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/CharLongComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two char, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface CharLongComparator { - int compare(char k1, long v1, char k2, long v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/CharObjectComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/CharObjectComparator.java deleted file mode 100755 index fdab147d..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/CharObjectComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two char, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface CharObjectComparator { - int compare(char k1, VType v1, char k2, VType v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/CharShortComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/CharShortComparator.java deleted file mode 100755 index 91ba9976..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/CharShortComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two char, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface CharShortComparator { - int compare(char k1, short v1, char k2, short v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/DoubleByteComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/DoubleByteComparator.java deleted file mode 100755 index 60fae03f..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/DoubleByteComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two double, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface DoubleByteComparator { - int compare(double k1, byte v1, double k2, byte v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/DoubleCharComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/DoubleCharComparator.java deleted file mode 100755 index ec215c93..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/DoubleCharComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two double, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface DoubleCharComparator { - int compare(double k1, char v1, double k2, char v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/DoubleComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/DoubleComparator.java deleted file mode 100755 index 193b5f90..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/DoubleComparator.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two double values. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeComparator.java") -public interface DoubleComparator { - int compare(double a, double b); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/DoubleDoubleComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/DoubleDoubleComparator.java deleted file mode 100755 index 20ba2dcc..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/DoubleDoubleComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two double, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface DoubleDoubleComparator { - int compare(double k1, double v1, double k2, double v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/DoubleFloatComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/DoubleFloatComparator.java deleted file mode 100755 index 50aca950..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/DoubleFloatComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two double, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface DoubleFloatComparator { - int compare(double k1, float v1, double k2, float v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/DoubleIntComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/DoubleIntComparator.java deleted file mode 100755 index afd12514..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/DoubleIntComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two double, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface DoubleIntComparator { - int compare(double k1, int v1, double k2, int v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/DoubleLongComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/DoubleLongComparator.java deleted file mode 100755 index a843ec6a..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/DoubleLongComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two double, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface DoubleLongComparator { - int compare(double k1, long v1, double k2, long v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/DoubleObjectComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/DoubleObjectComparator.java deleted file mode 100755 index 9d654aab..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/DoubleObjectComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two double, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface DoubleObjectComparator { - int compare(double k1, VType v1, double k2, VType v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/DoubleShortComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/DoubleShortComparator.java deleted file mode 100755 index 4b463161..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/DoubleShortComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two double, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface DoubleShortComparator { - int compare(double k1, short v1, double k2, short v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/FloatByteComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/FloatByteComparator.java deleted file mode 100755 index 298f94b0..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/FloatByteComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two float, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface FloatByteComparator { - int compare(float k1, byte v1, float k2, byte v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/FloatCharComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/FloatCharComparator.java deleted file mode 100755 index a2af271b..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/FloatCharComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two float, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface FloatCharComparator { - int compare(float k1, char v1, float k2, char v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/FloatComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/FloatComparator.java deleted file mode 100755 index 8831a3bc..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/FloatComparator.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two float values. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeComparator.java") -public interface FloatComparator { - int compare(float a, float b); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/FloatDoubleComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/FloatDoubleComparator.java deleted file mode 100755 index 26cfcb64..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/FloatDoubleComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two float, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface FloatDoubleComparator { - int compare(float k1, double v1, float k2, double v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/FloatFloatComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/FloatFloatComparator.java deleted file mode 100755 index 8fc373bb..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/FloatFloatComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two float, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface FloatFloatComparator { - int compare(float k1, float v1, float k2, float v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/FloatIntComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/FloatIntComparator.java deleted file mode 100755 index 86199e53..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/FloatIntComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two float, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface FloatIntComparator { - int compare(float k1, int v1, float k2, int v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/FloatLongComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/FloatLongComparator.java deleted file mode 100755 index 7dcc9e44..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/FloatLongComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two float, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface FloatLongComparator { - int compare(float k1, long v1, float k2, long v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/FloatObjectComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/FloatObjectComparator.java deleted file mode 100755 index f1e9cd64..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/FloatObjectComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two float, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface FloatObjectComparator { - int compare(float k1, VType v1, float k2, VType v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/FloatShortComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/FloatShortComparator.java deleted file mode 100755 index baac7d4f..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/FloatShortComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two float, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface FloatShortComparator { - int compare(float k1, short v1, float k2, short v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/IntByteComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/IntByteComparator.java deleted file mode 100755 index fe34ad58..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/IntByteComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two int, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface IntByteComparator { - int compare(int k1, byte v1, int k2, byte v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/IntCharComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/IntCharComparator.java deleted file mode 100755 index b682c529..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/IntCharComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two int, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface IntCharComparator { - int compare(int k1, char v1, int k2, char v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/IntComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/IntComparator.java deleted file mode 100755 index 3501d91a..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/IntComparator.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two int values. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeComparator.java") -public interface IntComparator { - int compare(int a, int b); - - static IntComparator naturalOrder() { - return Integer::compare; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/IntDoubleComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/IntDoubleComparator.java deleted file mode 100755 index 5d6580d9..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/IntDoubleComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two int, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface IntDoubleComparator { - int compare(int k1, double v1, int k2, double v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/IntFloatComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/IntFloatComparator.java deleted file mode 100755 index 6cb79069..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/IntFloatComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two int, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface IntFloatComparator { - int compare(int k1, float v1, int k2, float v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/IntIntComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/IntIntComparator.java deleted file mode 100755 index fe4214fc..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/IntIntComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two int, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface IntIntComparator { - int compare(int k1, int v1, int k2, int v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/IntLongComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/IntLongComparator.java deleted file mode 100755 index 620a6978..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/IntLongComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two int, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface IntLongComparator { - int compare(int k1, long v1, int k2, long v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/IntObjectComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/IntObjectComparator.java deleted file mode 100755 index 8fbbb04c..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/IntObjectComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two int, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface IntObjectComparator { - int compare(int k1, VType v1, int k2, VType v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/IntShortComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/IntShortComparator.java deleted file mode 100755 index 5657bdb0..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/IntShortComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two int, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface IntShortComparator { - int compare(int k1, short v1, int k2, short v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/LongByteComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/LongByteComparator.java deleted file mode 100755 index 326d0f1c..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/LongByteComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two long, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface LongByteComparator { - int compare(long k1, byte v1, long k2, byte v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/LongCharComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/LongCharComparator.java deleted file mode 100755 index 7c2267f5..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/LongCharComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two long, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface LongCharComparator { - int compare(long k1, char v1, long k2, char v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/LongComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/LongComparator.java deleted file mode 100755 index d090607c..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/LongComparator.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two long values. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeComparator.java") -public interface LongComparator { - int compare(long a, long b); - - static LongComparator naturalOrder() { - return Long::compare; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/LongDoubleComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/LongDoubleComparator.java deleted file mode 100755 index 25f39e20..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/LongDoubleComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two long, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface LongDoubleComparator { - int compare(long k1, double v1, long k2, double v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/LongFloatComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/LongFloatComparator.java deleted file mode 100755 index dde992f6..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/LongFloatComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two long, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface LongFloatComparator { - int compare(long k1, float v1, long k2, float v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/LongIntComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/LongIntComparator.java deleted file mode 100755 index de63af1d..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/LongIntComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two long, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface LongIntComparator { - int compare(long k1, int v1, long k2, int v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/LongLongComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/LongLongComparator.java deleted file mode 100755 index 9b0822d8..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/LongLongComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two long, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface LongLongComparator { - int compare(long k1, long v1, long k2, long v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/LongObjectComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/LongObjectComparator.java deleted file mode 100755 index 42fd64ba..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/LongObjectComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two long, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface LongObjectComparator { - int compare(long k1, VType v1, long k2, VType v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/LongShortComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/LongShortComparator.java deleted file mode 100755 index b6377dd2..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/LongShortComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two long, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface LongShortComparator { - int compare(long k1, short v1, long k2, short v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ObjectByteComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ObjectByteComparator.java deleted file mode 100755 index 54d86da0..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ObjectByteComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two Object, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ObjectByteComparator { - int compare(KType k1, byte v1, KType k2, byte v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ObjectCharComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ObjectCharComparator.java deleted file mode 100755 index 0c8f42dc..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ObjectCharComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two Object, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ObjectCharComparator { - int compare(KType k1, char v1, KType k2, char v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ObjectDoubleComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ObjectDoubleComparator.java deleted file mode 100755 index 1ec17275..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ObjectDoubleComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two Object, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ObjectDoubleComparator { - int compare(KType k1, double v1, KType k2, double v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ObjectFloatComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ObjectFloatComparator.java deleted file mode 100755 index 1c289f20..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ObjectFloatComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two Object, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ObjectFloatComparator { - int compare(KType k1, float v1, KType k2, float v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ObjectIntComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ObjectIntComparator.java deleted file mode 100755 index fe143279..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ObjectIntComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two Object, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ObjectIntComparator { - int compare(KType k1, int v1, KType k2, int v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ObjectLongComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ObjectLongComparator.java deleted file mode 100755 index e031f4ea..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ObjectLongComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two Object, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ObjectLongComparator { - int compare(KType k1, long v1, KType k2, long v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ObjectObjectComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ObjectObjectComparator.java deleted file mode 100755 index ed5a2d68..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ObjectObjectComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two Object, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ObjectObjectComparator { - int compare(KType k1, VType v1, KType k2, VType v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ObjectShortComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ObjectShortComparator.java deleted file mode 100755 index c545a557..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ObjectShortComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two Object, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ObjectShortComparator { - int compare(KType k1, short v1, KType k2, short v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ShortByteComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ShortByteComparator.java deleted file mode 100755 index b372f942..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ShortByteComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two short, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ShortByteComparator { - int compare(short k1, byte v1, short k2, byte v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ShortCharComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ShortCharComparator.java deleted file mode 100755 index 82b9b0a3..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ShortCharComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two short, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ShortCharComparator { - int compare(short k1, char v1, short k2, char v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ShortComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ShortComparator.java deleted file mode 100755 index b935f60b..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ShortComparator.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two short values. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeComparator.java") -public interface ShortComparator { - int compare(short a, short b); - - static ShortComparator naturalOrder() { - return Short::compare; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ShortDoubleComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ShortDoubleComparator.java deleted file mode 100755 index 37c2a7b5..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ShortDoubleComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two short, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ShortDoubleComparator { - int compare(short k1, double v1, short k2, double v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ShortFloatComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ShortFloatComparator.java deleted file mode 100755 index 15eec4a8..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ShortFloatComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two short, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ShortFloatComparator { - int compare(short k1, float v1, short k2, float v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ShortIntComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ShortIntComparator.java deleted file mode 100755 index 9d5ad1fe..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ShortIntComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two short, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ShortIntComparator { - int compare(short k1, int v1, short k2, int v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ShortLongComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ShortLongComparator.java deleted file mode 100755 index 9e394e1a..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ShortLongComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two short, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ShortLongComparator { - int compare(short k1, long v1, short k2, long v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ShortObjectComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ShortObjectComparator.java deleted file mode 100755 index 6d1715f3..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ShortObjectComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two short, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ShortObjectComparator { - int compare(short k1, VType v1, short k2, VType v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/comparators/ShortShortComparator.java b/src/main/java/com/carrotsearch/hppc/comparators/ShortShortComparator.java deleted file mode 100755 index ccd78621..00000000 --- a/src/main/java/com/carrotsearch/hppc/comparators/ShortShortComparator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.comparators; - -/** Compares two short, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:17+0200", - value = "KTypeVTypeComparator.java") -public interface ShortShortComparator { - int compare(short k1, short v1, short k2, short v2); -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ByteCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ByteCursor.java deleted file mode 100755 index e0b695dd..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ByteCursor.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over a collection of bytes. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeCursor.java") -public final class ByteCursor { - /** - * The current value's index in the container this cursor belongs to. The meaning of this index is - * defined by the container (usually it will be an index in the underlying storage buffer). - */ - public int index; - - /** The current value. */ - public byte value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/CharByteCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/CharByteCursor.java deleted file mode 100755 index 9e36f8f1..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/CharByteCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (char keys and byte values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class CharByteCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public char key; - - /** The current value. */ - public byte value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/CharCharCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/CharCharCursor.java deleted file mode 100755 index 079a2682..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/CharCharCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (char keys and char values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class CharCharCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public char key; - - /** The current value. */ - public char value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/CharCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/CharCursor.java deleted file mode 100755 index 20a0ec58..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/CharCursor.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over a collection of chars. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeCursor.java") -public final class CharCursor { - /** - * The current value's index in the container this cursor belongs to. The meaning of this index is - * defined by the container (usually it will be an index in the underlying storage buffer). - */ - public int index; - - /** The current value. */ - public char value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/CharDoubleCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/CharDoubleCursor.java deleted file mode 100755 index 5cdcd581..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/CharDoubleCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (char keys and double values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class CharDoubleCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public char key; - - /** The current value. */ - public double value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/CharFloatCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/CharFloatCursor.java deleted file mode 100755 index 109f0ae3..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/CharFloatCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (char keys and float values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class CharFloatCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public char key; - - /** The current value. */ - public float value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/CharIntCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/CharIntCursor.java deleted file mode 100755 index b706f1f4..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/CharIntCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (char keys and int values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class CharIntCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public char key; - - /** The current value. */ - public int value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/CharLongCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/CharLongCursor.java deleted file mode 100755 index 1d5259ff..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/CharLongCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (char keys and long values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class CharLongCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public char key; - - /** The current value. */ - public long value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/CharObjectCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/CharObjectCursor.java deleted file mode 100755 index fa98d620..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/CharObjectCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (char keys and Object values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class CharObjectCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public char key; - - /** The current value. */ - public VType value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/CharShortCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/CharShortCursor.java deleted file mode 100755 index d6407c80..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/CharShortCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (char keys and short values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class CharShortCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public char key; - - /** The current value. */ - public short value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/DoubleCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/DoubleCursor.java deleted file mode 100755 index cc4f42c9..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/DoubleCursor.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over a collection of doubles. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeCursor.java") -public final class DoubleCursor { - /** - * The current value's index in the container this cursor belongs to. The meaning of this index is - * defined by the container (usually it will be an index in the underlying storage buffer). - */ - public int index; - - /** The current value. */ - public double value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/FloatCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/FloatCursor.java deleted file mode 100755 index 417f27eb..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/FloatCursor.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over a collection of floats. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeCursor.java") -public final class FloatCursor { - /** - * The current value's index in the container this cursor belongs to. The meaning of this index is - * defined by the container (usually it will be an index in the underlying storage buffer). - */ - public int index; - - /** The current value. */ - public float value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/IntByteCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/IntByteCursor.java deleted file mode 100755 index b0dede73..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/IntByteCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (int keys and byte values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class IntByteCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public int key; - - /** The current value. */ - public byte value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/IntCharCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/IntCharCursor.java deleted file mode 100755 index d49fd001..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/IntCharCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (int keys and char values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class IntCharCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public int key; - - /** The current value. */ - public char value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/IntCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/IntCursor.java deleted file mode 100755 index 8fd34781..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/IntCursor.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over a collection of ints. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeCursor.java") -public final class IntCursor { - /** - * The current value's index in the container this cursor belongs to. The meaning of this index is - * defined by the container (usually it will be an index in the underlying storage buffer). - */ - public int index; - - /** The current value. */ - public int value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/IntDoubleCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/IntDoubleCursor.java deleted file mode 100755 index 03e56888..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/IntDoubleCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (int keys and double values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class IntDoubleCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public int key; - - /** The current value. */ - public double value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/IntFloatCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/IntFloatCursor.java deleted file mode 100755 index 0cbc76e1..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/IntFloatCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (int keys and float values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class IntFloatCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public int key; - - /** The current value. */ - public float value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/IntIntCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/IntIntCursor.java deleted file mode 100755 index c02ea79e..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/IntIntCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (int keys and int values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class IntIntCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public int key; - - /** The current value. */ - public int value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/IntLongCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/IntLongCursor.java deleted file mode 100755 index 299ad712..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/IntLongCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (int keys and long values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class IntLongCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public int key; - - /** The current value. */ - public long value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/IntObjectCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/IntObjectCursor.java deleted file mode 100755 index 673a06d1..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/IntObjectCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (int keys and Object values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class IntObjectCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public int key; - - /** The current value. */ - public VType value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/IntShortCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/IntShortCursor.java deleted file mode 100755 index 7f72eba4..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/IntShortCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (int keys and short values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class IntShortCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public int key; - - /** The current value. */ - public short value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/LongByteCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/LongByteCursor.java deleted file mode 100755 index 74cdbd90..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/LongByteCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (long keys and byte values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class LongByteCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public long key; - - /** The current value. */ - public byte value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/LongCharCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/LongCharCursor.java deleted file mode 100755 index 800389a1..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/LongCharCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (long keys and char values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class LongCharCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public long key; - - /** The current value. */ - public char value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/LongCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/LongCursor.java deleted file mode 100755 index 53da311c..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/LongCursor.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over a collection of longs. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeCursor.java") -public final class LongCursor { - /** - * The current value's index in the container this cursor belongs to. The meaning of this index is - * defined by the container (usually it will be an index in the underlying storage buffer). - */ - public int index; - - /** The current value. */ - public long value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/LongDoubleCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/LongDoubleCursor.java deleted file mode 100755 index ed4ceef2..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/LongDoubleCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (long keys and double values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class LongDoubleCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public long key; - - /** The current value. */ - public double value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/LongFloatCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/LongFloatCursor.java deleted file mode 100755 index 0ff3ebb7..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/LongFloatCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (long keys and float values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class LongFloatCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public long key; - - /** The current value. */ - public float value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/LongIntCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/LongIntCursor.java deleted file mode 100755 index 202d409a..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/LongIntCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (long keys and int values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class LongIntCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public long key; - - /** The current value. */ - public int value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/LongLongCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/LongLongCursor.java deleted file mode 100755 index 824852f8..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/LongLongCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (long keys and long values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class LongLongCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public long key; - - /** The current value. */ - public long value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/LongObjectCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/LongObjectCursor.java deleted file mode 100755 index 26f413c0..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/LongObjectCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (long keys and Object values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class LongObjectCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public long key; - - /** The current value. */ - public VType value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/LongShortCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/LongShortCursor.java deleted file mode 100755 index 5d82d4e5..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/LongShortCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (long keys and short values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class LongShortCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public long key; - - /** The current value. */ - public short value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ObjectByteCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ObjectByteCursor.java deleted file mode 100755 index 380b48af..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ObjectByteCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (Object keys and byte values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class ObjectByteCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public KType key; - - /** The current value. */ - public byte value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ObjectCharCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ObjectCharCursor.java deleted file mode 100755 index 1e4df4fc..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ObjectCharCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (Object keys and char values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class ObjectCharCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public KType key; - - /** The current value. */ - public char value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ObjectCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ObjectCursor.java deleted file mode 100755 index 9f3ef5c0..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ObjectCursor.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over a collection of Objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeCursor.java") -public final class ObjectCursor { - /** - * The current value's index in the container this cursor belongs to. The meaning of this index is - * defined by the container (usually it will be an index in the underlying storage buffer). - */ - public int index; - - /** The current value. */ - public KType value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ObjectDoubleCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ObjectDoubleCursor.java deleted file mode 100755 index edc16aea..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ObjectDoubleCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (Object keys and double values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class ObjectDoubleCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public KType key; - - /** The current value. */ - public double value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ObjectFloatCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ObjectFloatCursor.java deleted file mode 100755 index 767777e4..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ObjectFloatCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (Object keys and float values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class ObjectFloatCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public KType key; - - /** The current value. */ - public float value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ObjectIntCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ObjectIntCursor.java deleted file mode 100755 index b83b3672..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ObjectIntCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (Object keys and int values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class ObjectIntCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public KType key; - - /** The current value. */ - public int value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ObjectLongCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ObjectLongCursor.java deleted file mode 100755 index 4eba0b0b..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ObjectLongCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (Object keys and long values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class ObjectLongCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public KType key; - - /** The current value. */ - public long value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ObjectObjectCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ObjectObjectCursor.java deleted file mode 100755 index 85514da4..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ObjectObjectCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (Object keys and Object values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class ObjectObjectCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public KType key; - - /** The current value. */ - public VType value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ObjectShortCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ObjectShortCursor.java deleted file mode 100755 index 30f6fa60..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ObjectShortCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (Object keys and short values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class ObjectShortCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public KType key; - - /** The current value. */ - public short value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ShortByteCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ShortByteCursor.java deleted file mode 100755 index 1a2bd555..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ShortByteCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (short keys and byte values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class ShortByteCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public short key; - - /** The current value. */ - public byte value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ShortCharCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ShortCharCursor.java deleted file mode 100755 index 120fd566..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ShortCharCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (short keys and char values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class ShortCharCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public short key; - - /** The current value. */ - public char value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ShortCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ShortCursor.java deleted file mode 100755 index 37117970..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ShortCursor.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over a collection of shorts. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeCursor.java") -public final class ShortCursor { - /** - * The current value's index in the container this cursor belongs to. The meaning of this index is - * defined by the container (usually it will be an index in the underlying storage buffer). - */ - public int index; - - /** The current value. */ - public short value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ShortDoubleCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ShortDoubleCursor.java deleted file mode 100755 index f4b2d389..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ShortDoubleCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (short keys and double values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class ShortDoubleCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public short key; - - /** The current value. */ - public double value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ShortFloatCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ShortFloatCursor.java deleted file mode 100755 index 8a605078..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ShortFloatCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (short keys and float values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class ShortFloatCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public short key; - - /** The current value. */ - public float value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ShortIntCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ShortIntCursor.java deleted file mode 100755 index 997a6792..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ShortIntCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (short keys and int values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class ShortIntCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public short key; - - /** The current value. */ - public int value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ShortLongCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ShortLongCursor.java deleted file mode 100755 index 576e7f0e..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ShortLongCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (short keys and long values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class ShortLongCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public short key; - - /** The current value. */ - public long value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ShortObjectCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ShortObjectCursor.java deleted file mode 100755 index 485514f6..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ShortObjectCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (short keys and Object values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class ShortObjectCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public short key; - - /** The current value. */ - public VType value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/cursors/ShortShortCursor.java b/src/main/java/com/carrotsearch/hppc/cursors/ShortShortCursor.java deleted file mode 100755 index 5fa724eb..00000000 --- a/src/main/java/com/carrotsearch/hppc/cursors/ShortShortCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.carrotsearch.hppc.cursors; - -/** A cursor over entries of an associative container (short keys and short values). */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "KTypeVTypeCursor.java") -public final class ShortShortCursor { - /** - * The current key and value's index in the container this cursor belongs to. The meaning of this - * index is defined by the container (usually it will be an index in the underlying storage - * buffer). - */ - public int index; - - /** The current key. */ - public short key; - - /** The current value. */ - public short value; - - @Override - public String toString() { - return "[cursor, index: " + index + ", key: " + key + ", value: " + value + "]"; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/internals/SuppressForbidden.java b/src/main/java/com/carrotsearch/hppc/internals/SuppressForbidden.java deleted file mode 100755 index c746e0f7..00000000 --- a/src/main/java/com/carrotsearch/hppc/internals/SuppressForbidden.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc.internals; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** Suppresses forbidden-API checks. */ -@Retention(RetentionPolicy.CLASS) -@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) -public @interface SuppressForbidden {} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ByteBytePredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ByteBytePredicate.java deleted file mode 100755 index d3819903..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ByteBytePredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to byte, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ByteBytePredicate { - public boolean apply(byte key, byte value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ByteCharPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ByteCharPredicate.java deleted file mode 100755 index 7e1877cc..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ByteCharPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to byte, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ByteCharPredicate { - public boolean apply(byte key, char value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ByteDoublePredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ByteDoublePredicate.java deleted file mode 100755 index 598b185b..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ByteDoublePredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to byte, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ByteDoublePredicate { - public boolean apply(byte key, double value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ByteFloatPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ByteFloatPredicate.java deleted file mode 100755 index 0a7dde15..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ByteFloatPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to byte, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ByteFloatPredicate { - public boolean apply(byte key, float value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ByteIntPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ByteIntPredicate.java deleted file mode 100755 index 8b523f51..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ByteIntPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to byte, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ByteIntPredicate { - public boolean apply(byte key, int value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ByteLongPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ByteLongPredicate.java deleted file mode 100755 index bff16b84..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ByteLongPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to byte, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ByteLongPredicate { - public boolean apply(byte key, long value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ByteObjectPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ByteObjectPredicate.java deleted file mode 100755 index 322f132b..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ByteObjectPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to byte, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ByteObjectPredicate { - public boolean apply(byte key, VType value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/BytePredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/BytePredicate.java deleted file mode 100755 index 16c023b1..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/BytePredicate.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to byte objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypePredicate.java") -public interface BytePredicate { - public boolean apply(byte value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ByteShortPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ByteShortPredicate.java deleted file mode 100755 index 975073f7..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ByteShortPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to byte, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ByteShortPredicate { - public boolean apply(byte key, short value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/CharBytePredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/CharBytePredicate.java deleted file mode 100755 index 1bd258e4..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/CharBytePredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to char, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface CharBytePredicate { - public boolean apply(char key, byte value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/CharCharPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/CharCharPredicate.java deleted file mode 100755 index b2142e99..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/CharCharPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to char, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface CharCharPredicate { - public boolean apply(char key, char value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/CharDoublePredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/CharDoublePredicate.java deleted file mode 100755 index d7409d75..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/CharDoublePredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to char, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface CharDoublePredicate { - public boolean apply(char key, double value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/CharFloatPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/CharFloatPredicate.java deleted file mode 100755 index d2e3c890..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/CharFloatPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to char, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface CharFloatPredicate { - public boolean apply(char key, float value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/CharIntPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/CharIntPredicate.java deleted file mode 100755 index e68d9dac..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/CharIntPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to char, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface CharIntPredicate { - public boolean apply(char key, int value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/CharLongPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/CharLongPredicate.java deleted file mode 100755 index de3ed2dc..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/CharLongPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to char, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface CharLongPredicate { - public boolean apply(char key, long value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/CharObjectPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/CharObjectPredicate.java deleted file mode 100755 index c85875e6..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/CharObjectPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to char, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface CharObjectPredicate { - public boolean apply(char key, VType value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/CharPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/CharPredicate.java deleted file mode 100755 index efe6e9dc..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/CharPredicate.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to char objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypePredicate.java") -public interface CharPredicate { - public boolean apply(char value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/CharShortPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/CharShortPredicate.java deleted file mode 100755 index 54d462c2..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/CharShortPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to char, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface CharShortPredicate { - public boolean apply(char key, short value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/DoubleBytePredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/DoubleBytePredicate.java deleted file mode 100755 index 1ab1d41c..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/DoubleBytePredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to double, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface DoubleBytePredicate { - public boolean apply(double key, byte value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/DoubleCharPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/DoubleCharPredicate.java deleted file mode 100755 index ac7f394e..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/DoubleCharPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to double, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface DoubleCharPredicate { - public boolean apply(double key, char value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/DoubleDoublePredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/DoubleDoublePredicate.java deleted file mode 100755 index d3e39487..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/DoubleDoublePredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to double, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface DoubleDoublePredicate { - public boolean apply(double key, double value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/DoubleFloatPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/DoubleFloatPredicate.java deleted file mode 100755 index 1a0343d8..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/DoubleFloatPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to double, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface DoubleFloatPredicate { - public boolean apply(double key, float value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/DoubleIntPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/DoubleIntPredicate.java deleted file mode 100755 index a1a135be..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/DoubleIntPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to double, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface DoubleIntPredicate { - public boolean apply(double key, int value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/DoubleLongPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/DoubleLongPredicate.java deleted file mode 100755 index 2014b2a1..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/DoubleLongPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to double, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface DoubleLongPredicate { - public boolean apply(double key, long value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/DoubleObjectPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/DoubleObjectPredicate.java deleted file mode 100755 index 290912f9..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/DoubleObjectPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to double, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface DoubleObjectPredicate { - public boolean apply(double key, VType value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/DoublePredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/DoublePredicate.java deleted file mode 100755 index 57227e9f..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/DoublePredicate.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to double objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypePredicate.java") -public interface DoublePredicate { - public boolean apply(double value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/DoubleShortPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/DoubleShortPredicate.java deleted file mode 100755 index 6ee48dbf..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/DoubleShortPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to double, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface DoubleShortPredicate { - public boolean apply(double key, short value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/FloatBytePredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/FloatBytePredicate.java deleted file mode 100755 index 07a763b5..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/FloatBytePredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to float, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface FloatBytePredicate { - public boolean apply(float key, byte value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/FloatCharPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/FloatCharPredicate.java deleted file mode 100755 index a14461d7..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/FloatCharPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to float, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface FloatCharPredicate { - public boolean apply(float key, char value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/FloatDoublePredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/FloatDoublePredicate.java deleted file mode 100755 index 8c8dda0d..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/FloatDoublePredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to float, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface FloatDoublePredicate { - public boolean apply(float key, double value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/FloatFloatPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/FloatFloatPredicate.java deleted file mode 100755 index fae9df7a..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/FloatFloatPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to float, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface FloatFloatPredicate { - public boolean apply(float key, float value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/FloatIntPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/FloatIntPredicate.java deleted file mode 100755 index dd37320b..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/FloatIntPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to float, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface FloatIntPredicate { - public boolean apply(float key, int value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/FloatLongPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/FloatLongPredicate.java deleted file mode 100755 index ed1232a7..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/FloatLongPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to float, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface FloatLongPredicate { - public boolean apply(float key, long value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/FloatObjectPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/FloatObjectPredicate.java deleted file mode 100755 index a74dca85..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/FloatObjectPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to float, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface FloatObjectPredicate { - public boolean apply(float key, VType value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/FloatPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/FloatPredicate.java deleted file mode 100755 index eb728862..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/FloatPredicate.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to float objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypePredicate.java") -public interface FloatPredicate { - public boolean apply(float value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/FloatShortPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/FloatShortPredicate.java deleted file mode 100755 index 1a61beb0..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/FloatShortPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to float, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface FloatShortPredicate { - public boolean apply(float key, short value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/IntBytePredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/IntBytePredicate.java deleted file mode 100755 index 3bfa0aac..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/IntBytePredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to int, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface IntBytePredicate { - public boolean apply(int key, byte value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/IntCharPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/IntCharPredicate.java deleted file mode 100755 index c262f415..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/IntCharPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to int, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface IntCharPredicate { - public boolean apply(int key, char value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/IntDoublePredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/IntDoublePredicate.java deleted file mode 100755 index 3ec64db1..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/IntDoublePredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to int, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface IntDoublePredicate { - public boolean apply(int key, double value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/IntFloatPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/IntFloatPredicate.java deleted file mode 100755 index 449b5f0d..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/IntFloatPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to int, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface IntFloatPredicate { - public boolean apply(int key, float value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/IntIntPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/IntIntPredicate.java deleted file mode 100755 index 384d33cd..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/IntIntPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to int, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface IntIntPredicate { - public boolean apply(int key, int value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/IntLongPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/IntLongPredicate.java deleted file mode 100755 index f3d4811b..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/IntLongPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to int, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface IntLongPredicate { - public boolean apply(int key, long value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/IntObjectPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/IntObjectPredicate.java deleted file mode 100755 index 5a8063a8..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/IntObjectPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to int, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface IntObjectPredicate { - public boolean apply(int key, VType value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/IntPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/IntPredicate.java deleted file mode 100755 index 77d1c38f..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/IntPredicate.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to int objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypePredicate.java") -public interface IntPredicate { - public boolean apply(int value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/IntShortPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/IntShortPredicate.java deleted file mode 100755 index 443642d7..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/IntShortPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to int, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface IntShortPredicate { - public boolean apply(int key, short value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/LongBytePredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/LongBytePredicate.java deleted file mode 100755 index dce65d8e..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/LongBytePredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to long, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface LongBytePredicate { - public boolean apply(long key, byte value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/LongCharPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/LongCharPredicate.java deleted file mode 100755 index f2974d72..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/LongCharPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to long, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface LongCharPredicate { - public boolean apply(long key, char value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/LongDoublePredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/LongDoublePredicate.java deleted file mode 100755 index 34947e08..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/LongDoublePredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to long, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface LongDoublePredicate { - public boolean apply(long key, double value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/LongFloatPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/LongFloatPredicate.java deleted file mode 100755 index c0bf1e54..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/LongFloatPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to long, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface LongFloatPredicate { - public boolean apply(long key, float value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/LongIntPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/LongIntPredicate.java deleted file mode 100755 index a31d293a..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/LongIntPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to long, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface LongIntPredicate { - public boolean apply(long key, int value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/LongLongPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/LongLongPredicate.java deleted file mode 100755 index 45315193..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/LongLongPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to long, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface LongLongPredicate { - public boolean apply(long key, long value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/LongObjectPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/LongObjectPredicate.java deleted file mode 100755 index fef9bbc3..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/LongObjectPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to long, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface LongObjectPredicate { - public boolean apply(long key, VType value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/LongPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/LongPredicate.java deleted file mode 100755 index 52f0b87a..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/LongPredicate.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to long objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypePredicate.java") -public interface LongPredicate { - public boolean apply(long value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/LongShortPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/LongShortPredicate.java deleted file mode 100755 index 5621fd73..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/LongShortPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to long, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface LongShortPredicate { - public boolean apply(long key, short value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ObjectBytePredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ObjectBytePredicate.java deleted file mode 100755 index 57657903..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ObjectBytePredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to Object, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ObjectBytePredicate { - public boolean apply(KType key, byte value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ObjectCharPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ObjectCharPredicate.java deleted file mode 100755 index 2c108dbb..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ObjectCharPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to Object, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ObjectCharPredicate { - public boolean apply(KType key, char value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ObjectDoublePredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ObjectDoublePredicate.java deleted file mode 100755 index e8d4ff50..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ObjectDoublePredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to Object, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ObjectDoublePredicate { - public boolean apply(KType key, double value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ObjectFloatPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ObjectFloatPredicate.java deleted file mode 100755 index 166d0c56..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ObjectFloatPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to Object, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ObjectFloatPredicate { - public boolean apply(KType key, float value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ObjectIntPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ObjectIntPredicate.java deleted file mode 100755 index d683332c..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ObjectIntPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to Object, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ObjectIntPredicate { - public boolean apply(KType key, int value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ObjectLongPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ObjectLongPredicate.java deleted file mode 100755 index bd97d954..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ObjectLongPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to Object, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ObjectLongPredicate { - public boolean apply(KType key, long value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ObjectObjectPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ObjectObjectPredicate.java deleted file mode 100755 index 6970aa65..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ObjectObjectPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to Object, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ObjectObjectPredicate { - public boolean apply(KType key, VType value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ObjectPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ObjectPredicate.java deleted file mode 100755 index c9426651..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ObjectPredicate.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to Object objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypePredicate.java") -public interface ObjectPredicate { - public boolean apply(KType value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ObjectShortPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ObjectShortPredicate.java deleted file mode 100755 index 2049fabf..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ObjectShortPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to Object, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ObjectShortPredicate { - public boolean apply(KType key, short value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ShortBytePredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ShortBytePredicate.java deleted file mode 100755 index 86305fa4..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ShortBytePredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to short, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ShortBytePredicate { - public boolean apply(short key, byte value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ShortCharPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ShortCharPredicate.java deleted file mode 100755 index 14a726ef..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ShortCharPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to short, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ShortCharPredicate { - public boolean apply(short key, char value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ShortDoublePredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ShortDoublePredicate.java deleted file mode 100755 index 1fdd8733..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ShortDoublePredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to short, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ShortDoublePredicate { - public boolean apply(short key, double value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ShortFloatPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ShortFloatPredicate.java deleted file mode 100755 index 4a55b962..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ShortFloatPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to short, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ShortFloatPredicate { - public boolean apply(short key, float value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ShortIntPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ShortIntPredicate.java deleted file mode 100755 index 039c59ab..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ShortIntPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to short, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ShortIntPredicate { - public boolean apply(short key, int value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ShortLongPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ShortLongPredicate.java deleted file mode 100755 index 7036942c..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ShortLongPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to short, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ShortLongPredicate { - public boolean apply(short key, long value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ShortObjectPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ShortObjectPredicate.java deleted file mode 100755 index 1b4bf8b7..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ShortObjectPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to short, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ShortObjectPredicate { - public boolean apply(short key, VType value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ShortPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ShortPredicate.java deleted file mode 100755 index 40fa91c8..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ShortPredicate.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to short objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypePredicate.java") -public interface ShortPredicate { - public boolean apply(short value); -} diff --git a/src/main/java/com/carrotsearch/hppc/predicates/ShortShortPredicate.java b/src/main/java/com/carrotsearch/hppc/predicates/ShortShortPredicate.java deleted file mode 100755 index 51d7b0a7..00000000 --- a/src/main/java/com/carrotsearch/hppc/predicates/ShortShortPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.predicates; - -/** A predicate that applies to short, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypePredicate.java") -public interface ShortShortPredicate { - public boolean apply(short key, short value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ByteByteProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ByteByteProcedure.java deleted file mode 100755 index bd4a18ef..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ByteByteProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to byte, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ByteByteProcedure { - public void apply(byte key, byte value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ByteCharProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ByteCharProcedure.java deleted file mode 100755 index 6b4c5dea..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ByteCharProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to byte, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ByteCharProcedure { - public void apply(byte key, char value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ByteDoubleProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ByteDoubleProcedure.java deleted file mode 100755 index 3269170a..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ByteDoubleProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to byte, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ByteDoubleProcedure { - public void apply(byte key, double value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ByteFloatProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ByteFloatProcedure.java deleted file mode 100755 index 011427e7..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ByteFloatProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to byte, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ByteFloatProcedure { - public void apply(byte key, float value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ByteIntProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ByteIntProcedure.java deleted file mode 100755 index aca3dc2f..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ByteIntProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to byte, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ByteIntProcedure { - public void apply(byte key, int value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ByteLongProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ByteLongProcedure.java deleted file mode 100755 index 01e9239e..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ByteLongProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to byte, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ByteLongProcedure { - public void apply(byte key, long value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ByteObjectProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ByteObjectProcedure.java deleted file mode 100755 index 8d954b0d..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ByteObjectProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to byte, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ByteObjectProcedure { - public void apply(byte key, VType value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ByteProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ByteProcedure.java deleted file mode 100755 index 9959f0d1..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ByteProcedure.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to byte objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypeProcedure.java") -public interface ByteProcedure { - public void apply(byte value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ByteShortProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ByteShortProcedure.java deleted file mode 100755 index 83e34cc9..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ByteShortProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to byte, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ByteShortProcedure { - public void apply(byte key, short value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/CharByteProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/CharByteProcedure.java deleted file mode 100755 index 58fa2d40..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/CharByteProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to char, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface CharByteProcedure { - public void apply(char key, byte value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/CharCharProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/CharCharProcedure.java deleted file mode 100755 index 2cd5d298..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/CharCharProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to char, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface CharCharProcedure { - public void apply(char key, char value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/CharDoubleProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/CharDoubleProcedure.java deleted file mode 100755 index 091e5c9d..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/CharDoubleProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to char, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface CharDoubleProcedure { - public void apply(char key, double value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/CharFloatProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/CharFloatProcedure.java deleted file mode 100755 index 73b54532..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/CharFloatProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to char, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface CharFloatProcedure { - public void apply(char key, float value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/CharIntProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/CharIntProcedure.java deleted file mode 100755 index 8118508d..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/CharIntProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to char, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface CharIntProcedure { - public void apply(char key, int value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/CharLongProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/CharLongProcedure.java deleted file mode 100755 index 9657edd1..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/CharLongProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to char, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface CharLongProcedure { - public void apply(char key, long value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/CharObjectProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/CharObjectProcedure.java deleted file mode 100755 index 145e6f7d..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/CharObjectProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to char, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface CharObjectProcedure { - public void apply(char key, VType value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/CharProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/CharProcedure.java deleted file mode 100755 index 2bd7e5d3..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/CharProcedure.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to char objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypeProcedure.java") -public interface CharProcedure { - public void apply(char value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/CharShortProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/CharShortProcedure.java deleted file mode 100755 index 50b2584f..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/CharShortProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to char, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface CharShortProcedure { - public void apply(char key, short value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/DoubleByteProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/DoubleByteProcedure.java deleted file mode 100755 index 70adadf6..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/DoubleByteProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to double, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface DoubleByteProcedure { - public void apply(double key, byte value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/DoubleCharProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/DoubleCharProcedure.java deleted file mode 100755 index 101e0d99..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/DoubleCharProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to double, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface DoubleCharProcedure { - public void apply(double key, char value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/DoubleDoubleProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/DoubleDoubleProcedure.java deleted file mode 100755 index 9ba2012c..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/DoubleDoubleProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to double, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface DoubleDoubleProcedure { - public void apply(double key, double value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/DoubleFloatProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/DoubleFloatProcedure.java deleted file mode 100755 index 3dabb0c9..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/DoubleFloatProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to double, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface DoubleFloatProcedure { - public void apply(double key, float value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/DoubleIntProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/DoubleIntProcedure.java deleted file mode 100755 index be62b25a..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/DoubleIntProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to double, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface DoubleIntProcedure { - public void apply(double key, int value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/DoubleLongProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/DoubleLongProcedure.java deleted file mode 100755 index a648e97a..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/DoubleLongProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to double, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface DoubleLongProcedure { - public void apply(double key, long value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/DoubleObjectProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/DoubleObjectProcedure.java deleted file mode 100755 index 5973dd22..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/DoubleObjectProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to double, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface DoubleObjectProcedure { - public void apply(double key, VType value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/DoubleProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/DoubleProcedure.java deleted file mode 100755 index c23a5617..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/DoubleProcedure.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to double objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypeProcedure.java") -public interface DoubleProcedure { - public void apply(double value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/DoubleShortProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/DoubleShortProcedure.java deleted file mode 100755 index aea0b9ec..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/DoubleShortProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to double, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface DoubleShortProcedure { - public void apply(double key, short value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/FloatByteProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/FloatByteProcedure.java deleted file mode 100755 index df914d43..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/FloatByteProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to float, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface FloatByteProcedure { - public void apply(float key, byte value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/FloatCharProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/FloatCharProcedure.java deleted file mode 100755 index 8d54dba1..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/FloatCharProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to float, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface FloatCharProcedure { - public void apply(float key, char value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/FloatDoubleProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/FloatDoubleProcedure.java deleted file mode 100755 index e398c47f..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/FloatDoubleProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to float, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface FloatDoubleProcedure { - public void apply(float key, double value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/FloatFloatProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/FloatFloatProcedure.java deleted file mode 100755 index 00cfa5b4..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/FloatFloatProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to float, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface FloatFloatProcedure { - public void apply(float key, float value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/FloatIntProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/FloatIntProcedure.java deleted file mode 100755 index f09f375d..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/FloatIntProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to float, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface FloatIntProcedure { - public void apply(float key, int value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/FloatLongProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/FloatLongProcedure.java deleted file mode 100755 index ee7e3d89..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/FloatLongProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to float, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface FloatLongProcedure { - public void apply(float key, long value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/FloatObjectProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/FloatObjectProcedure.java deleted file mode 100755 index 3e23e199..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/FloatObjectProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to float, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface FloatObjectProcedure { - public void apply(float key, VType value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/FloatProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/FloatProcedure.java deleted file mode 100755 index 17543434..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/FloatProcedure.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to float objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypeProcedure.java") -public interface FloatProcedure { - public void apply(float value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/FloatShortProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/FloatShortProcedure.java deleted file mode 100755 index 9ae0b7f8..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/FloatShortProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to float, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface FloatShortProcedure { - public void apply(float key, short value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/IntByteProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/IntByteProcedure.java deleted file mode 100755 index 2232f85a..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/IntByteProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to int, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface IntByteProcedure { - public void apply(int key, byte value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/IntCharProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/IntCharProcedure.java deleted file mode 100755 index f5f1d509..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/IntCharProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to int, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface IntCharProcedure { - public void apply(int key, char value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/IntDoubleProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/IntDoubleProcedure.java deleted file mode 100755 index 1fca14bb..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/IntDoubleProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to int, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface IntDoubleProcedure { - public void apply(int key, double value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/IntFloatProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/IntFloatProcedure.java deleted file mode 100755 index 8c91a104..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/IntFloatProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to int, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface IntFloatProcedure { - public void apply(int key, float value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/IntIntProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/IntIntProcedure.java deleted file mode 100755 index b870c6b8..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/IntIntProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to int, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface IntIntProcedure { - public void apply(int key, int value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/IntLongProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/IntLongProcedure.java deleted file mode 100755 index 741858f0..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/IntLongProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to int, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface IntLongProcedure { - public void apply(int key, long value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/IntObjectProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/IntObjectProcedure.java deleted file mode 100755 index 575a1f2a..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/IntObjectProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to int, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface IntObjectProcedure { - public void apply(int key, VType value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/IntProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/IntProcedure.java deleted file mode 100755 index 4fab32fd..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/IntProcedure.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to int objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypeProcedure.java") -public interface IntProcedure { - public void apply(int value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/IntShortProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/IntShortProcedure.java deleted file mode 100755 index c3bac031..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/IntShortProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to int, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface IntShortProcedure { - public void apply(int key, short value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/LongByteProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/LongByteProcedure.java deleted file mode 100755 index 670af78b..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/LongByteProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to long, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface LongByteProcedure { - public void apply(long key, byte value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/LongCharProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/LongCharProcedure.java deleted file mode 100755 index 5ca456d0..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/LongCharProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to long, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface LongCharProcedure { - public void apply(long key, char value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/LongDoubleProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/LongDoubleProcedure.java deleted file mode 100755 index 2aaa2c3d..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/LongDoubleProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to long, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface LongDoubleProcedure { - public void apply(long key, double value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/LongFloatProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/LongFloatProcedure.java deleted file mode 100755 index 6b88a579..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/LongFloatProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to long, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface LongFloatProcedure { - public void apply(long key, float value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/LongIntProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/LongIntProcedure.java deleted file mode 100755 index 297f3878..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/LongIntProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to long, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface LongIntProcedure { - public void apply(long key, int value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/LongLongProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/LongLongProcedure.java deleted file mode 100755 index f9f1fba4..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/LongLongProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to long, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface LongLongProcedure { - public void apply(long key, long value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/LongObjectProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/LongObjectProcedure.java deleted file mode 100755 index 2e0a8f86..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/LongObjectProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to long, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface LongObjectProcedure { - public void apply(long key, VType value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/LongProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/LongProcedure.java deleted file mode 100755 index 9397055f..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/LongProcedure.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to long objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypeProcedure.java") -public interface LongProcedure { - public void apply(long value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/LongShortProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/LongShortProcedure.java deleted file mode 100755 index 2ad0865a..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/LongShortProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to long, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface LongShortProcedure { - public void apply(long key, short value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ObjectByteProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ObjectByteProcedure.java deleted file mode 100755 index 85d69130..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ObjectByteProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to Object, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ObjectByteProcedure { - public void apply(KType key, byte value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ObjectCharProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ObjectCharProcedure.java deleted file mode 100755 index 3393875f..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ObjectCharProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to Object, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ObjectCharProcedure { - public void apply(KType key, char value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ObjectDoubleProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ObjectDoubleProcedure.java deleted file mode 100755 index 4d9345d2..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ObjectDoubleProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to Object, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ObjectDoubleProcedure { - public void apply(KType key, double value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ObjectFloatProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ObjectFloatProcedure.java deleted file mode 100755 index 237c2863..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ObjectFloatProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to Object, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ObjectFloatProcedure { - public void apply(KType key, float value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ObjectIntProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ObjectIntProcedure.java deleted file mode 100755 index 11bb001c..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ObjectIntProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to Object, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ObjectIntProcedure { - public void apply(KType key, int value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ObjectLongProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ObjectLongProcedure.java deleted file mode 100755 index 9aec4cdd..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ObjectLongProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to Object, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ObjectLongProcedure { - public void apply(KType key, long value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ObjectObjectProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ObjectObjectProcedure.java deleted file mode 100755 index 382d76f8..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ObjectObjectProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to Object, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ObjectObjectProcedure { - public void apply(KType key, VType value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ObjectProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ObjectProcedure.java deleted file mode 100755 index cb0f00fc..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ObjectProcedure.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to Object objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypeProcedure.java") -public interface ObjectProcedure { - public void apply(KType value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ObjectShortProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ObjectShortProcedure.java deleted file mode 100755 index 871cac43..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ObjectShortProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to Object, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ObjectShortProcedure { - public void apply(KType key, short value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ShortByteProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ShortByteProcedure.java deleted file mode 100755 index a9b86f29..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ShortByteProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to short, byte pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ShortByteProcedure { - public void apply(short key, byte value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ShortCharProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ShortCharProcedure.java deleted file mode 100755 index 2ebbb819..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ShortCharProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to short, char pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ShortCharProcedure { - public void apply(short key, char value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ShortDoubleProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ShortDoubleProcedure.java deleted file mode 100755 index ada12e27..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ShortDoubleProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to short, double pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ShortDoubleProcedure { - public void apply(short key, double value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ShortFloatProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ShortFloatProcedure.java deleted file mode 100755 index a99af694..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ShortFloatProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to short, float pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ShortFloatProcedure { - public void apply(short key, float value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ShortIntProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ShortIntProcedure.java deleted file mode 100755 index e633c55d..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ShortIntProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to short, int pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ShortIntProcedure { - public void apply(short key, int value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ShortLongProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ShortLongProcedure.java deleted file mode 100755 index 99275f7e..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ShortLongProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to short, long pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ShortLongProcedure { - public void apply(short key, long value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ShortObjectProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ShortObjectProcedure.java deleted file mode 100755 index 5ab91d02..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ShortObjectProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to short, Object pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ShortObjectProcedure { - public void apply(short key, VType value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ShortProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ShortProcedure.java deleted file mode 100755 index d3482c7b..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ShortProcedure.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to short objects. */ -@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:16+0200", value = "KTypeProcedure.java") -public interface ShortProcedure { - public void apply(short value); -} diff --git a/src/main/java/com/carrotsearch/hppc/procedures/ShortShortProcedure.java b/src/main/java/com/carrotsearch/hppc/procedures/ShortShortProcedure.java deleted file mode 100755 index 05873e51..00000000 --- a/src/main/java/com/carrotsearch/hppc/procedures/ShortShortProcedure.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.carrotsearch.hppc.procedures; - -/** A procedure that applies to short, short pairs. */ -@com.carrotsearch.hppc.Generated( - date = "2024-06-04T15:20:16+0200", - value = "KTypeVTypeProcedure.java") -public interface ShortShortProcedure { - public void apply(short key, short value); -} diff --git a/src/main/java/com/carrotsearch/hppc/sorting/IndirectSort.java b/src/main/java/com/carrotsearch/hppc/sorting/IndirectSort.java deleted file mode 100755 index 2d4722ed..00000000 --- a/src/main/java/com/carrotsearch/hppc/sorting/IndirectSort.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc.sorting; - -import java.util.Comparator; -import java.util.function.IntBinaryOperator; - -/** - * Sorting routines that return an array of sorted indices implied by a given comparator rather than - * move elements of whatever the comparator is using for comparisons. - * - *

A practical use case for this class is when the index of an array is meaningful and one wants - * to acquire the order of values in that array. None of the methods in Java Collections would - * provide such functionality directly and creating a collection of boxed {@link Integer} objects - * for indices seems to be too costly. - */ -public final class IndirectSort { - /** Minimum window length to apply insertion sort in merge sort. */ - static int MIN_LENGTH_FOR_INSERTION_SORT = 30; - - /** No instantiation. */ - private IndirectSort() { - // No instantiation. - } - - /** - * Returns the order of elements between indices start and length, as - * indicated by the given comparator. - * - *

This routine uses merge sort. It is guaranteed to be stable. It creates a new indices array, - * and clones it while sorting. - */ - public static int[] mergesort(int start, int length, IntBinaryOperator comparator) { - final int[] src = createOrderArray(start, length); - return mergesort(src, comparator); - } - - /** - * Returns a sorted copy of the order array provided, using the given comparator. - * - *

This routine uses merge sort. It is guaranteed to be stable. The provided {@code - * indicesArray} is cloned while sorting and the clone is returned. - */ - public static int[] mergesort(int[] orderArray, IntBinaryOperator comparator) { - if (orderArray.length <= 1) { - return orderArray; - } - final int[] dst = orderArray.clone(); - topDownMergeSort(orderArray, dst, 0, orderArray.length, comparator); - return dst; - } - - /** - * Returns the order of elements between indices start and length, as - * indicated by the given comparator. - * - *

This routine uses merge sort. It is guaranteed to be stable. It creates a new indices array, - * and clones it while sorting. - */ - public static int[] mergesort( - T[] input, int start, int length, Comparator comparator) { - return mergesort(start, length, (a, b) -> comparator.compare(input[a], input[b])); - } - - /** - * Perform a recursive, descending merge sort. - * - * @param fromIndex inclusive - * @param toIndex exclusive - */ - private static void topDownMergeSort( - int[] src, int[] dst, int fromIndex, int toIndex, IntBinaryOperator comp) { - if (toIndex - fromIndex <= MIN_LENGTH_FOR_INSERTION_SORT) { - insertionSort(fromIndex, toIndex - fromIndex, dst, comp); - return; - } - - final int mid = (fromIndex + toIndex) >>> 1; - topDownMergeSort(dst, src, fromIndex, mid, comp); - topDownMergeSort(dst, src, mid, toIndex, comp); - - /* - * Both splits in of src are now sorted. - */ - if (comp.applyAsInt(src[mid - 1], src[mid]) <= 0) { - /* - * If the lowest element in upper slice is larger than the highest element in - * the lower slice, simply copy over, the data is fully sorted. - */ - System.arraycopy(src, fromIndex, dst, fromIndex, toIndex - fromIndex); - } else { - /* - * Run a manual merge. - */ - for (int i = fromIndex, j = mid, k = fromIndex; k < toIndex; k++) { - if (j == toIndex || (i < mid && comp.applyAsInt(src[i], src[j]) <= 0)) { - dst[k] = src[i++]; - } else { - dst[k] = src[j++]; - } - } - } - } - - /** Internal insertion sort for ints. */ - private static void insertionSort( - final int off, final int len, int[] order, IntBinaryOperator intComparator) { - for (int i = off + 1; i < off + len; i++) { - final int v = order[i]; - int j = i, t; - while (j > off && intComparator.applyAsInt(t = order[j - 1], v) > 0) { - order[j--] = t; - } - order[j] = v; - } - } - - /** Creates the initial order array. */ - private static int[] createOrderArray(final int start, final int length) { - final int[] order = new int[length]; - for (int i = 0; i < length; i++) { - order[i] = start + i; - } - return order; - } -} diff --git a/src/main/java/com/carrotsearch/hppc/sorting/QuickSort.java b/src/main/java/com/carrotsearch/hppc/sorting/QuickSort.java deleted file mode 100755 index b547dc68..00000000 --- a/src/main/java/com/carrotsearch/hppc/sorting/QuickSort.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * HPPC - * - * Copyright (C) 2010-2024 Carrot Search s.c. and contributors - * All rights reserved. - * - * Refer to the full license file "LICENSE.txt": - * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt - */ -package com.carrotsearch.hppc.sorting; - -import java.util.function.IntBinaryOperator; - -/** - * In-place Quick sort with 3-way partitioning and ending with Insertion sort. - * - *

The sorting is not stable. Performance is O(n.log(n)) and memory is O(1) (although recursion - * memory is O(log(n))). - */ -public final class QuickSort { - - /** Below this size threshold, the sub-range is sorted using Insertion sort. */ - static final int INSERTION_SORT_THRESHOLD = 16; - - /** /** Below this size threshold, the partition selection is simplified to a single median. */ - static final int SINGLE_MEDIAN_THRESHOLD = 40; - - /** No instantiation. */ - private QuickSort() { - // No instantiation. - } - - /** - * @see #sort(int, int, IntBinaryOperator, IntBinaryOperator) - */ - public static void sort(int[] array, IntBinaryOperator comparator) { - sort(array, 0, array.length, comparator); - } - - /** - * @see #sort(int, int, IntBinaryOperator, IntBinaryOperator) - */ - public static void sort(int[] array, int fromIndex, int toIndex, IntBinaryOperator comparator) { - sort( - fromIndex, - toIndex, - comparator, - (i, j) -> { - int swap = array[i]; - array[i] = array[j]; - array[j] = swap; - return 0; - }); - } - - /** - * Performs a recursive in-place Quick sort. The sorting is not stable. - * - * @param fromIndex Index where to start sorting in the array, inclusive. - * @param toIndex Index where to stop sorting in the array, exclusive. - * @param comparator Compares elements based on their indices. Given indices i and j in the - * provided array, this comparator returns respectively -1/0/1 if the element at index i is - * respectively less/equal/greater than the element at index j. - * @param swapper Swaps the elements in the array at the given indices. For example, a custom - * swapper may allow sorting two arrays simultaneously. - */ - public static void sort( - int fromIndex, int toIndex, IntBinaryOperator comparator, IntBinaryOperator swapper) { - int size; - while ((size = toIndex - fromIndex) > INSERTION_SORT_THRESHOLD) { - - // Pivot selection. - int last = toIndex - 1; - int middle = (fromIndex + last) >>> 1; - int pivot; - if (size <= SINGLE_MEDIAN_THRESHOLD) { - // Select the pivot with a single median around the middle element. - // Do not take the median between [from, mid, last] because it hurts performance - // if the order is descending. - int range = size >> 2; - pivot = median(middle - range, middle, middle + range, comparator); - } else { - // Select the pivot with the median of medians. - int range = size >> 3; - int doubleRange = range << 1; - int medianStart = median(fromIndex, fromIndex + range, fromIndex + doubleRange, comparator); - int medianMiddle = median(middle - range, middle, middle + range, comparator); - int medianEnd = median(last - doubleRange, last - range, last, comparator); - pivot = median(medianStart, medianMiddle, medianEnd, comparator); - } - - // Bentley-McIlroy 3-way partitioning. - swap(fromIndex, pivot, swapper); - int i = fromIndex; - int j = toIndex; - int p = fromIndex + 1; - int q = last; - while (true) { - int leftCmp, rightCmp; - while ((leftCmp = compare(++i, fromIndex, comparator)) < 0) { - // repeat - } - while ((rightCmp = compare(--j, fromIndex, comparator)) > 0) { - // repeat - } - if (i >= j) { - if (i == j && rightCmp == 0) { - swap(i, p, swapper); - } - break; - } - swap(i, j, swapper); - if (rightCmp == 0) { - swap(i, p++, swapper); - } - if (leftCmp == 0) { - swap(j, q--, swapper); - } - } - i = j + 1; - for (int k = fromIndex; k < p; ) { - swap(k++, j--, swapper); - } - for (int k = last; k > q; ) { - swap(k--, i++, swapper); - } - - // Recursion on the smallest partition. - // Replace the tail recursion by a loop. - if (j - fromIndex < last - i) { - sort(fromIndex, j + 1, comparator, swapper); - fromIndex = i; - } else { - sort(i, toIndex, comparator, swapper); - toIndex = j + 1; - } - } - - insertionSort(fromIndex, toIndex, comparator, swapper); - } - - /** Sorts between from (inclusive) and to (exclusive) with insertion sort. */ - private static void insertionSort( - int fromIndex, int toIndex, IntBinaryOperator comparator, IntBinaryOperator swapper) { - for (int i = fromIndex + 1; i < toIndex; ) { - int current = i++; - int previous; - while (compare((previous = current - 1), current, comparator) > 0) { - swap(previous, current, swapper); - if (previous == fromIndex) { - break; - } - current = previous; - } - } - } - - /** Returns the index of the median element among three elements at provided indices. */ - private static int median(int i, int j, int k, IntBinaryOperator comparator) { - if (compare(i, j, comparator) < 0) { - if (compare(j, k, comparator) <= 0) { - return j; - } - return compare(i, k, comparator) < 0 ? k : i; - } - if (compare(j, k, comparator) >= 0) { - return j; - } - return compare(i, k, comparator) < 0 ? i : k; - } - - /** Compares two elements at provided indices. */ - private static int compare(int i, int j, IntBinaryOperator comparator) { - return comparator.applyAsInt(i, j); - } - - /** Swaps two elements at provided indices. */ - private static void swap(int i, int j, IntBinaryOperator swapper) { - swapper.applyAsInt(i, j); - } -} diff --git a/src/main/java/javax/annotation/CheckForNull.java b/src/main/java/javax/annotation/CheckForNull.java deleted file mode 100755 index b289fa90..00000000 --- a/src/main/java/javax/annotation/CheckForNull.java +++ /dev/null @@ -1,23 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.annotation.meta.TypeQualifierNickname; -import javax.annotation.meta.When; - -/** - * The annotated element might be null, and uses of the element should check for - * null. - *

- * When this annotation is applied to a method it applies to the method return - * value. - */ -@Documented -@TypeQualifierNickname -@Nonnull(when = When.MAYBE) -@Retention(RetentionPolicy.RUNTIME) -public @interface CheckForNull { - -} diff --git a/src/main/java/javax/annotation/CheckForSigned.java b/src/main/java/javax/annotation/CheckForSigned.java deleted file mode 100755 index 31022dca..00000000 --- a/src/main/java/javax/annotation/CheckForSigned.java +++ /dev/null @@ -1,22 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.annotation.meta.TypeQualifierNickname; -import javax.annotation.meta.When; - -/** - * Used to annotate a value that may be either negative or nonnegative, and - * indicates that uses of it should check for negative values before using it in - * a way that requires the value to be nonnegative, and check for it being - * nonnegative before using it in a way that requires it to be negative. - */ -@Documented -@TypeQualifierNickname -@Nonnegative(when = When.MAYBE) -@Retention(RetentionPolicy.RUNTIME) -public @interface CheckForSigned { - -} diff --git a/src/main/java/javax/annotation/CheckReturnValue.java b/src/main/java/javax/annotation/CheckReturnValue.java deleted file mode 100755 index 24ac058f..00000000 --- a/src/main/java/javax/annotation/CheckReturnValue.java +++ /dev/null @@ -1,20 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import javax.annotation.meta.When; - -/** - * This annotation is used to denote a method whose return value should always - * be checked after invoking the method. - */ -@Documented -@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE, ElementType.PACKAGE }) -@Retention(RetentionPolicy.RUNTIME) -public @interface CheckReturnValue { - When when() default When.ALWAYS; -} diff --git a/src/main/java/javax/annotation/Detainted.java b/src/main/java/javax/annotation/Detainted.java deleted file mode 100755 index c07ce201..00000000 --- a/src/main/java/javax/annotation/Detainted.java +++ /dev/null @@ -1,16 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.annotation.meta.TypeQualifierNickname; -import javax.annotation.meta.When; - -@Documented -@TypeQualifierNickname -@Untainted(when = When.ALWAYS) -@Retention(RetentionPolicy.RUNTIME) -public @interface Detainted { - -} diff --git a/src/main/java/javax/annotation/Generated.java b/src/main/java/javax/annotation/Generated.java deleted file mode 100755 index bef398b0..00000000 --- a/src/main/java/javax/annotation/Generated.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2005-2018 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://oss.oracle.com/licenses/CDDL+GPL-1.1 - * or LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.annotation; - -import static java.lang.annotation.ElementType.ANNOTATION_TYPE; -import static java.lang.annotation.ElementType.CONSTRUCTOR; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.LOCAL_VARIABLE; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PACKAGE; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.SOURCE; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * The Generated annotation is used to mark source code that has - * been generated. It can also be used to differentiate user written code from - * generated code in a single file. - *

- * The value element must have the name of the code generator. The - * recommended convention is to use the fully qualified name of the code - * generator in the value field, for example - * com.company.package.classname. - *

- *

- * The date element is used to indicate the date the source was - * generated. The date element must follow the ISO 8601 standard. - * For example, the date element could have the value - * 2001-07-04T12:08:56.235-0700, which represents 2001-07-04 - * 12:08:56 local time in the U.S. Pacific time zone. - *

- *

- * The comment element is a place holder for any comments that the - * code generator may want to include in the generated code. - *

- * - * @since 1.6, Common Annotations 1.0 - */ - -@Documented -@Retention(SOURCE) -@Target({ PACKAGE, TYPE, ANNOTATION_TYPE, METHOD, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, PARAMETER }) -public @interface Generated { - /** - * The value element must have the name of the code generator. The recommended - * convention is to use the fully qualified name of the code generator. For - * example: com.acme.generator.CodeGen. - */ - String[] value(); - - /** - * Date when the source was generated. - */ - String date() default ""; - - /** - * A place holder for any comments that the code generator may want to include - * in the generated code. - */ - String comments() default ""; -} diff --git a/src/main/java/javax/annotation/ManagedBean.java b/src/main/java/javax/annotation/ManagedBean.java deleted file mode 100755 index aaa35bd8..00000000 --- a/src/main/java/javax/annotation/ManagedBean.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2009-2018 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://oss.oracle.com/licenses/CDDL+GPL-1.1 - * or LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.annotation; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * The ManagedBean annotation marks a POJO (Plain Old Java Object) - * as a ManagedBean. A ManagedBean supports a small set of basic services such - * as resource injection, lifecycle callbacks and interceptors. - * - * @since Common Annotations 1.1 - */ -@Target(TYPE) -@Retention(RUNTIME) -public @interface ManagedBean { - /** - * The name of the Managed Bean. Managed Bean names must be unique within a Java - * EE module. For each named Managed Bean, Java EE containers must make - * available the following entries in JNDI, using the same naming scheme used - * for EJB components. - *

- * In the application namespace: - *

- * java:app/<module-name>/<bean-name> - *

- * In the module namespace of the module containing the Managed Bean: - *

- * java:module/<bean-name> - * - */ - public String value() default ""; -} diff --git a/src/main/java/javax/annotation/MatchesPattern.java b/src/main/java/javax/annotation/MatchesPattern.java deleted file mode 100755 index a350b59f..00000000 --- a/src/main/java/javax/annotation/MatchesPattern.java +++ /dev/null @@ -1,37 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.regex.Pattern; - -import javax.annotation.meta.TypeQualifier; -import javax.annotation.meta.TypeQualifierValidator; -import javax.annotation.meta.When; - -/** - * This annotation is used to denote String values that should always match - * given pattern. - *

- * When this annotation is applied to a method it applies to the method return - * value. - */ -@Documented -@TypeQualifier(applicableTo = String.class) -@Retention(RetentionPolicy.RUNTIME) -public @interface MatchesPattern { - @RegEx - String value(); - - int flags() default 0; - - static class Checker implements TypeQualifierValidator { - public When forConstantValue(MatchesPattern annotation, Object value) { - Pattern p = Pattern.compile(annotation.value(), annotation.flags()); - if (p.matcher(((String) value)).matches()) - return When.ALWAYS; - return When.NEVER; - } - - } -} diff --git a/src/main/java/javax/annotation/Nonnegative.java b/src/main/java/javax/annotation/Nonnegative.java deleted file mode 100755 index 6c3b0173..00000000 --- a/src/main/java/javax/annotation/Nonnegative.java +++ /dev/null @@ -1,47 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.annotation.meta.TypeQualifier; -import javax.annotation.meta.TypeQualifierValidator; -import javax.annotation.meta.When; - -/** - * This annotation is used to annotate a value that should only contain - * nonnegative values. - *

- * When this annotation is applied to a method it applies to the method return - * value. - */ -@Documented -@TypeQualifier(applicableTo = Number.class) -@Retention(RetentionPolicy.RUNTIME) -public @interface Nonnegative { - When when() default When.ALWAYS; - - class Checker implements TypeQualifierValidator { - - public When forConstantValue(Nonnegative annotation, Object v) { - if (!(v instanceof Number)) - return When.NEVER; - boolean isNegative; - Number value = (Number) v; - if (value instanceof Long) - isNegative = value.longValue() < 0; - else if (value instanceof Double) - isNegative = value.doubleValue() < 0; - else if (value instanceof Float) - isNegative = value.floatValue() < 0; - else - isNegative = value.intValue() < 0; - - if (isNegative) - return When.NEVER; - else - return When.ALWAYS; - - } - } -} diff --git a/src/main/java/javax/annotation/Nonnull.java b/src/main/java/javax/annotation/Nonnull.java deleted file mode 100755 index 5bbfbd98..00000000 --- a/src/main/java/javax/annotation/Nonnull.java +++ /dev/null @@ -1,33 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.annotation.meta.TypeQualifier; -import javax.annotation.meta.TypeQualifierValidator; -import javax.annotation.meta.When; - -/** - * The annotated element must not be null. - *

- * Annotated fields must not be null after construction has completed. - *

- * When this annotation is applied to a method it applies to the method return - * value. - */ -@Documented -@TypeQualifier -@Retention(RetentionPolicy.RUNTIME) -public @interface Nonnull { - When when() default When.ALWAYS; - - class Checker implements TypeQualifierValidator { - - public When forConstantValue(Nonnull qualifierArgument, Object value) { - if (value == null) - return When.NEVER; - return When.ALWAYS; - } - } -} diff --git a/src/main/java/javax/annotation/Nullable.java b/src/main/java/javax/annotation/Nullable.java deleted file mode 100755 index a9e85ef8..00000000 --- a/src/main/java/javax/annotation/Nullable.java +++ /dev/null @@ -1,32 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.annotation.meta.TypeQualifierNickname; -import javax.annotation.meta.When; - -/** - * The annotated element could be null under some circumstances. - *

- * In general, this means developers will have to read the documentation to - * determine when a null value is acceptable and whether it is necessary to - * check for a null value. - *

- * This annotation is useful mostly for overriding a {@link Nonnull} annotation. - * Static analysis tools should generally treat the annotated items as though - * they had no annotation, unless they are configured to minimize false - * negatives. Use {@link CheckForNull} to indicate that the element value should - * always be checked for a null value. - *

- * When this annotation is applied to a method it applies to the method return - * value. - */ -@Documented -@TypeQualifierNickname -@Nonnull(when = When.UNKNOWN) -@Retention(RetentionPolicy.RUNTIME) -public @interface Nullable { - -} diff --git a/src/main/java/javax/annotation/OverridingMethodsMustInvokeSuper.java b/src/main/java/javax/annotation/OverridingMethodsMustInvokeSuper.java deleted file mode 100755 index e70f9f83..00000000 --- a/src/main/java/javax/annotation/OverridingMethodsMustInvokeSuper.java +++ /dev/null @@ -1,21 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * When this annotation is applied to a method, it indicates that if this method - * is overridden in a subclass, the overriding method should invoke this method - * (through method invocation on super). - *

- * An example of such method is {@link Object#finalize()}. - */ -@Documented -@Target({ ElementType.METHOD }) -@Retention(RetentionPolicy.RUNTIME) -public @interface OverridingMethodsMustInvokeSuper { - -} diff --git a/src/main/java/javax/annotation/ParametersAreNonnullByDefault.java b/src/main/java/javax/annotation/ParametersAreNonnullByDefault.java deleted file mode 100755 index 7835f2bb..00000000 --- a/src/main/java/javax/annotation/ParametersAreNonnullByDefault.java +++ /dev/null @@ -1,29 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.annotation.meta.TypeQualifierDefault; - -/** - * This annotation can be applied to a package, class or method to indicate that - * the method parameters in that element are nonnull by default unless there is: - *

    - *
  • An explicit nullness annotation - *
  • The method overrides a method in a superclass (in which case the - * annotation of the corresponding parameter in the superclass applies) - *
  • There is a default parameter annotation (like - * {@link ParametersAreNullableByDefault}) applied to a more tightly nested - * element. - *
- * - * @see Nonnull - */ -@Documented -@Nonnull -@TypeQualifierDefault(ElementType.PARAMETER) -@Retention(RetentionPolicy.RUNTIME) -public @interface ParametersAreNonnullByDefault { -} diff --git a/src/main/java/javax/annotation/ParametersAreNullableByDefault.java b/src/main/java/javax/annotation/ParametersAreNullableByDefault.java deleted file mode 100755 index 83dcd5dd..00000000 --- a/src/main/java/javax/annotation/ParametersAreNullableByDefault.java +++ /dev/null @@ -1,33 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.annotation.meta.TypeQualifierDefault; - -/** - * This annotation can be applied to a package, class or method to indicate that - * the method parameters in that element are nullable by default unless there - * is: - *
    - *
  • An explicit nullness annotation - *
  • The method overrides a method in a superclass (in which case the - * annotation of the corresponding parameter in the superclass applies) - *
  • There is a default parameter annotation applied to a more tightly nested - * element. - *
- *

- * This annotation implies the same "nullness" as no annotation. However, it is - * different than having no annotation, as it is inherited and it can override a - * {@link ParametersAreNonnullByDefault} annotation at an outer scope. - * - * @see Nullable - */ -@Documented -@Nullable -@TypeQualifierDefault(ElementType.PARAMETER) -@Retention(RetentionPolicy.RUNTIME) -public @interface ParametersAreNullableByDefault { -} diff --git a/src/main/java/javax/annotation/PostConstruct.java b/src/main/java/javax/annotation/PostConstruct.java deleted file mode 100755 index 12dd1575..00000000 --- a/src/main/java/javax/annotation/PostConstruct.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2005-2018 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://oss.oracle.com/licenses/CDDL+GPL-1.1 - * or LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.annotation; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * The PostConstruct annotation is used on a method that needs to - * be executed after dependency injection is done to perform any initialization. - * This method must be invoked before the class is put into service. This - * annotation must be supported on all classes that support dependency - * injection. The method annotated with PostConstruct must be - * invoked even if the class does not request any resources to be injected. Only - * one method in a given class can be annotated with this annotation. The method - * on which the PostConstruct annotation is applied must fulfill - * all of the following criteria: - *

    - *
  • The method must not have any parameters except in the case of - * interceptors in which case it takes an InvocationContext object - * as defined by the Interceptors specification.
  • - *
  • The method defined on an interceptor class or superclass of an - * interceptor class must have one of the following signatures: - *

    - * void <METHOD>(InvocationContext) - *

    - * Object <METHOD>(InvocationContext) throws Exception - *

    - * Note: A PostConstruct interceptor method must not throw application - * exceptions, but it may be declared to throw checked exceptions including the - * java.lang.Exception if the same interceptor method interposes on business or - * timeout methods in addition to lifecycle events. If a PostConstruct - * interceptor method returns a value, it is ignored by the container.

  • - *
  • The method defined on a non-interceptor class must have the following - * signature: - *

    - * void <METHOD>()

  • - *
  • The method on which the PostConstruct annotation is applied - * may be public, protected, package private or private.
  • - *
  • The method must not be static except for the application client.
  • - *
  • The method should not be final.
  • - *
  • If the method throws an unchecked exception the class must not be put - * into service except in the case where the exception is handled by an - * interceptor.
  • - *
- * - * @see javax.annotation.PreDestroy - * @see javax.annotation.Resource - * @since 1.6, Common Annotations 1.0 - */ -@Documented -@Retention(RUNTIME) -@Target(METHOD) -public @interface PostConstruct { -} diff --git a/src/main/java/javax/annotation/PreDestroy.java b/src/main/java/javax/annotation/PreDestroy.java deleted file mode 100755 index 823648a7..00000000 --- a/src/main/java/javax/annotation/PreDestroy.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2005-2018 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://oss.oracle.com/licenses/CDDL+GPL-1.1 - * or LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.annotation; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * The PreDestroy annotation is used on a method as a callback - * notification to signal that the instance is in the process of being removed - * by the container. The method annotated with PreDestroy is - * typically used to release resources that it has been holding. This annotation - * must be supported by all container-managed objects that support the use of - * the PostConstruct annotation except the Java EE application - * client. The method on which the PreDestroy annotation is applied - * must fulfill all of the following criteria: - *
    - *
  • The method must not have any parameters except in the case of - * interceptors in which case it takes an InvocationContext object - * as defined by the Interceptors specification.
  • - *
  • The method defined on an interceptor class or superclass of an - * interceptor class must have one of the following signatures: - *

    - * void <METHOD>(InvocationContext) - *

    - * Object <METHOD>(InvocationContext) throws Exception - *

    - * Note: A PreDestroy interceptor method must not throw application - * exceptions, but it may be declared to throw checked exceptions including the - * java.lang.Exception if the same interceptor method interposes on business or - * timeout methods in addition to lifecycle events. If a PreDestroy interceptor - * method returns a value, it is ignored by the container.

  • - *
  • The method defined on a non-interceptor class must have the following - * signature: - *

    - * void <METHOD>()

  • - *
  • The method on which PreDestroy is applied may be public, protected, - * package private or private.
  • - *
  • The method must not be static.
  • - *
  • The method should not be final.
  • - *
  • If the method throws an unchecked exception it is ignored by the - * container.
  • - *
- * - * @see javax.annotation.PostConstruct - * @see javax.annotation.Resource - * @since 1.6, Common Annotations 1.0 - */ - -@Documented -@Retention(RUNTIME) -@Target(METHOD) -public @interface PreDestroy { -} diff --git a/src/main/java/javax/annotation/Priority.java b/src/main/java/javax/annotation/Priority.java deleted file mode 100755 index ef483f17..00000000 --- a/src/main/java/javax/annotation/Priority.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2005-2018 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://oss.oracle.com/licenses/CDDL+GPL-1.1 - * or LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.annotation; - -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * The Priority annotation can be applied to classes or parameters - * to indicate in what order they should be used. The effect of using the - * Priority annotation in any particular instance is defined by - * other specifications that define the use of a specific class. - *

- * For example, the Interceptors specification defines the use of priorities on - * interceptors to control the order in which interceptors are called. - *

- *

- * Priority values should generally be non-negative, with negative values - * reserved for special meanings such as "undefined" or "not specified". A - * specification that defines use of the Priority annotation may - * define the range of allowed priorities and any priority values with special - * meaning. - *

- * - * @since Common Annotations 1.2 - */ -@Target({ TYPE, PARAMETER }) -@Retention(RUNTIME) -@Documented -public @interface Priority { - /** - * The priority value. - */ - int value(); -} diff --git a/src/main/java/javax/annotation/PropertyKey.java b/src/main/java/javax/annotation/PropertyKey.java deleted file mode 100755 index 807ba8f7..00000000 --- a/src/main/java/javax/annotation/PropertyKey.java +++ /dev/null @@ -1,15 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.annotation.meta.TypeQualifier; -import javax.annotation.meta.When; - -@Documented -@TypeQualifier -@Retention(RetentionPolicy.RUNTIME) -public @interface PropertyKey { - When when() default When.ALWAYS; -} diff --git a/src/main/java/javax/annotation/RegEx.java b/src/main/java/javax/annotation/RegEx.java deleted file mode 100755 index 9c916b75..00000000 --- a/src/main/java/javax/annotation/RegEx.java +++ /dev/null @@ -1,44 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - -import javax.annotation.meta.TypeQualifierNickname; -import javax.annotation.meta.TypeQualifierValidator; -import javax.annotation.meta.When; - -/** - * This qualifier is used to denote String values that should be a Regular - * expression. - *

- * When this annotation is applied to a method it applies to the method return - * value. - */ -@Documented -@Syntax("RegEx") -@TypeQualifierNickname -@Retention(RetentionPolicy.RUNTIME) -public @interface RegEx { - When when() default When.ALWAYS; - - static class Checker implements TypeQualifierValidator { - - public When forConstantValue(RegEx annotation, Object value) { - if (!(value instanceof String)) - return When.NEVER; - - try { - Pattern.compile((String) value); - } catch (PatternSyntaxException e) { - return When.NEVER; - } - return When.ALWAYS; - - } - - } - -} diff --git a/src/main/java/javax/annotation/Resource.java b/src/main/java/javax/annotation/Resource.java deleted file mode 100755 index ca409e3a..00000000 --- a/src/main/java/javax/annotation/Resource.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2005-2018 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://oss.oracle.com/licenses/CDDL+GPL-1.1 - * or LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.annotation; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Repeatable; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * The Resource annotation marks a resource that is needed by the - * application. This annotation may be applied to an application component - * class, or to fields or methods of the component class. When the annotation is - * applied to a field or method, the container will inject an instance of the - * requested resource into the application component when the component is - * initialized. If the annotation is applied to the component class, the - * annotation declares a resource that the application will look up at runtime. - *

- * Even though this annotation is not marked Inherited, deployment - * tools are required to examine all superclasses of any component class to - * discover all uses of this annotation in all superclasses. All such annotation - * instances specify resources that are needed by the application component. - * Note that this annotation may appear on private fields and methods of - * superclasses; the container is required to perform injection in these cases - * as well. - *

- * - * @since 1.6, Common Annotations 1.0 - */ -@Target({ TYPE, FIELD, METHOD }) -@Retention(RUNTIME) -@Repeatable(Resources.class) -public @interface Resource { - /** - * The JNDI name of the resource. For field annotations, the default is the - * field name. For method annotations, the default is the JavaBeans property - * name corresponding to the method. For class annotations, there is no default - * and this must be specified. - */ - String name() default ""; - - /** - * The name of the resource that the reference points to. It can link to any - * compatible resource using the global JNDI names. - * - * @since 1.7, Common Annotations 1.1 - */ - - String lookup() default ""; - - /** - * The Java type of the resource. For field annotations, the default is the type - * of the field. For method annotations, the default is the type of the - * JavaBeans property. For class annotations, there is no default and this must - * be specified. - */ - Class type() default java.lang.Object.class; - - /** - * The two possible authentication types for a resource. - */ - enum AuthenticationType { - CONTAINER, APPLICATION - } - - /** - * The authentication type to use for this resource. This may be specified for - * resources representing a connection factory of any supported type, and must - * not be specified for resources of other types. - */ - AuthenticationType authenticationType() default AuthenticationType.CONTAINER; - - /** - * Indicates whether this resource can be shared between this component and - * other components. This may be specified for resources representing a - * connection factory of any supported type, and must not be specified for - * resources of other types. - */ - boolean shareable() default true; - - /** - * A product-specific name that this resource should be mapped to. The - * mappedName element provides for mapping the resource reference - * to the name of a resource known to the applicaiton server. The mapped name - * could be of any form. - *

- * Application servers are not required to support any particular form or type - * of mapped name, nor the ability to use mapped names. The mapped name is - * product-dependent and often installation-dependent. No use of a mapped name - * is portable. - *

- */ - String mappedName() default ""; - - /** - * Description of this resource. The description is expected to be in the - * default language of the system on which the application is deployed. The - * description can be presented to the Deployer to help in choosing the correct - * resource. - */ - String description() default ""; -} diff --git a/src/main/java/javax/annotation/Resources.java b/src/main/java/javax/annotation/Resources.java deleted file mode 100755 index e142ab5f..00000000 --- a/src/main/java/javax/annotation/Resources.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2005-2018 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://oss.oracle.com/licenses/CDDL+GPL-1.1 - * or LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.annotation; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * This class is used to allow multiple resources declarations. - * - * @see javax.annotation.Resource - * @since 1.6, Common Annotations 1.0 - */ - -@Documented -@Retention(RUNTIME) -@Target(TYPE) -public @interface Resources { - /** - * Array used for multiple resource declarations. - */ - Resource[] value(); -} diff --git a/src/main/java/javax/annotation/Signed.java b/src/main/java/javax/annotation/Signed.java deleted file mode 100755 index dd970a3a..00000000 --- a/src/main/java/javax/annotation/Signed.java +++ /dev/null @@ -1,19 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.annotation.meta.TypeQualifierNickname; -import javax.annotation.meta.When; - -/** - * Used to annotate a value of unknown sign. - */ -@Documented -@TypeQualifierNickname -@Nonnegative(when = When.UNKNOWN) -@Retention(RetentionPolicy.RUNTIME) -public @interface Signed { - -} diff --git a/src/main/java/javax/annotation/Syntax.java b/src/main/java/javax/annotation/Syntax.java deleted file mode 100755 index 55ff5edf..00000000 --- a/src/main/java/javax/annotation/Syntax.java +++ /dev/null @@ -1,45 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.annotation.meta.TypeQualifier; -import javax.annotation.meta.When; - -/** - * This annotation a value that is of a particular syntax, such as Java syntax - * or regular expression syntax. This can be used to provide syntax checking of - * constant values at compile time, run time checking at runtime, and can assist - * IDEs in deciding how to interpret String constants (e.g., should a - * refactoring that renames method {@code x()} to {@code y()} update the String - * constant {@code "x()"}). - */ -@Documented -@TypeQualifier(applicableTo = CharSequence.class) -@Retention(RetentionPolicy.RUNTIME) -public @interface Syntax { - /** - * Value indicating the particular syntax denoted by this annotation. Different - * tools will recognize different syntaxes, but some proposed canonical values - * are: - *
    - *
  • "Java" - *
  • "RegEx" - *
  • "JavaScript" - *
  • "Ruby" - *
  • "Groovy" - *
  • "SQL" - *
  • "FormatString" - *
- *

- * Syntax names can be followed by a colon and a list of key value pairs, - * separated by commas. For example, "SQL:dialect=Oracle,version=2.3". Tools - * should ignore any keys they don't recognize. - * - * @return a name indicating the particular syntax. - */ - String value(); - - When when() default When.ALWAYS; -} diff --git a/src/main/java/javax/annotation/Tainted.java b/src/main/java/javax/annotation/Tainted.java deleted file mode 100755 index 20eab6ce..00000000 --- a/src/main/java/javax/annotation/Tainted.java +++ /dev/null @@ -1,28 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.annotation.meta.TypeQualifierNickname; -import javax.annotation.meta.When; - -/** - * This annotation is used to denote String values that are tainted, i.e. may - * come from untrusted sources without proper validation. - *

- * For example, this annotation should be used on the String value which - * represents raw input received from the web form. - *

- * When this annotation is applied to a method it applies to the method return - * value. - * - * @see Untainted - */ -@Documented -@TypeQualifierNickname -@Untainted(when = When.MAYBE) -@Retention(RetentionPolicy.RUNTIME) -public @interface Tainted { - -} diff --git a/src/main/java/javax/annotation/Untainted.java b/src/main/java/javax/annotation/Untainted.java deleted file mode 100755 index 2b397099..00000000 --- a/src/main/java/javax/annotation/Untainted.java +++ /dev/null @@ -1,27 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.annotation.meta.TypeQualifier; -import javax.annotation.meta.When; - -/** - * This annotation is used to denote String values that are untainted, i.e. - * properly validated. - *

- * For example, this annotation should be used on the String value which - * represents SQL query to be passed to database engine. - *

- * When this annotation is applied to a method it applies to the method return - * value. - * - * @see Tainted - */ -@Documented -@TypeQualifier -@Retention(RetentionPolicy.RUNTIME) -public @interface Untainted { - When when() default When.ALWAYS; -} diff --git a/src/main/java/javax/annotation/WillClose.java b/src/main/java/javax/annotation/WillClose.java deleted file mode 100755 index 370bc92a..00000000 --- a/src/main/java/javax/annotation/WillClose.java +++ /dev/null @@ -1,18 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Used to annotate a method parameter to indicate that this method will close - * the resource. - * - * @see WillCloseWhenClosed - * @see WillNotClose - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -public @interface WillClose { - -} diff --git a/src/main/java/javax/annotation/WillCloseWhenClosed.java b/src/main/java/javax/annotation/WillCloseWhenClosed.java deleted file mode 100755 index 31d1512f..00000000 --- a/src/main/java/javax/annotation/WillCloseWhenClosed.java +++ /dev/null @@ -1,18 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Used to annotate a constructor/factory parameter to indicate that returned - * object (X) will close the resource when X is closed. - * - * @see WillClose - * @see WillNotClose - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -public @interface WillCloseWhenClosed { - -} diff --git a/src/main/java/javax/annotation/WillNotClose.java b/src/main/java/javax/annotation/WillNotClose.java deleted file mode 100755 index bc3faa0a..00000000 --- a/src/main/java/javax/annotation/WillNotClose.java +++ /dev/null @@ -1,18 +0,0 @@ -package javax.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Used to annotate a method parameter to indicate that this method will not - * close the resource. - * - * @see WillClose - * @see WillCloseWhenClosed - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -public @interface WillNotClose { - -} diff --git a/src/main/java/javax/annotation/concurrent/GuardedBy.java b/src/main/java/javax/annotation/concurrent/GuardedBy.java deleted file mode 100755 index c1071d26..00000000 --- a/src/main/java/javax/annotation/concurrent/GuardedBy.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2005 Brian Goetz - * Released under the Creative Commons Attribution License - * (http://creativecommons.org/licenses/by/2.5) - * Official home: http://www.jcip.net - */ -package javax.annotation.concurrent; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * The field or method to which this annotation is applied can only be accessed - * when holding a particular lock, which may be a built-in (synchronization) - * lock, or may be an explicit {@link java.util.concurrent.locks.Lock}. - *

- * The argument determines which lock guards the annotated field or method: - *

    - *
  • this : The string literal "this" means that this field is guarded by the - * class in which it is defined. - *
  • class-name.this : For inner classes, it may be necessary to disambiguate - * 'this'; the class-name.this designation allows you to specify which 'this' - * reference is intended - *
  • itself : For reference fields only; the object to which the field refers. - *
  • field-name : The lock object is referenced by the (instance or static) - * field specified by field-name. - *
  • class-name.field-name : The lock object is reference by the static field - * specified by class-name.field-name. - *
  • method-name() : The lock object is returned by calling the named nil-ary - * method. - *
  • class-name.class : The Class object for the specified class should be - * used as the lock object. - *
- */ -@Target({ ElementType.FIELD, ElementType.METHOD }) -@Retention(RetentionPolicy.CLASS) -public @interface GuardedBy { - String value(); -} diff --git a/src/main/java/javax/annotation/concurrent/Immutable.java b/src/main/java/javax/annotation/concurrent/Immutable.java deleted file mode 100755 index e884da40..00000000 --- a/src/main/java/javax/annotation/concurrent/Immutable.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2005 Brian Goetz - * Released under the Creative Commons Attribution License - * (http://creativecommons.org/licenses/by/2.5) - * Official home: http://www.jcip.net - */ -package javax.annotation.concurrent; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * The class to which this annotation is applied is immutable. This means that - * its state cannot be seen to change by callers. Of necessity this means that - * all public fields are final, and that all public final reference fields refer - * to other immutable objects, and that methods do not publish references to any - * internal state which is mutable by implementation even if not by design. - * Immutable objects may still have internal mutable state for purposes of - * performance optimization; some state variables may be lazily computed, so - * long as they are computed from immutable state and that callers cannot tell - * the difference. - *

- * Immutable objects are inherently thread-safe; they may be passed between - * threads or published without synchronization. - */ -@Documented -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.CLASS) -public @interface Immutable { -} diff --git a/src/main/java/javax/annotation/concurrent/NotThreadSafe.java b/src/main/java/javax/annotation/concurrent/NotThreadSafe.java deleted file mode 100755 index f55c3d42..00000000 --- a/src/main/java/javax/annotation/concurrent/NotThreadSafe.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2005 Brian Goetz - * Released under the Creative Commons Attribution License - * (http://creativecommons.org/licenses/by/2.5) - * Official home: http://www.jcip.net - */ -package javax.annotation.concurrent; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * The class to which this annotation is applied is not thread-safe. This - * annotation primarily exists for clarifying the non-thread-safety of a class - * that might otherwise be assumed to be thread-safe, despite the fact that it - * is a bad idea to assume a class is thread-safe without good reason. - * - * @see ThreadSafe - */ -@Documented -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.CLASS) -public @interface NotThreadSafe { -} diff --git a/src/main/java/javax/annotation/concurrent/ThreadSafe.java b/src/main/java/javax/annotation/concurrent/ThreadSafe.java deleted file mode 100755 index 84ce4b93..00000000 --- a/src/main/java/javax/annotation/concurrent/ThreadSafe.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2005 Brian Goetz - * Released under the Creative Commons Attribution License - * (http://creativecommons.org/licenses/by/2.5) - * Official home: http://www.jcip.net - */ -package javax.annotation.concurrent; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * The class to which this annotation is applied is thread-safe. This means that - * no sequences of accesses (reads and writes to public fields, calls to public - * methods) may put the object into an invalid state, regardless of the - * interleaving of those actions by the runtime, and without requiring any - * additional synchronization or coordination on the part of the caller. - * - * @see NotThreadSafe - */ -@Documented -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.CLASS) -public @interface ThreadSafe { -} diff --git a/src/main/java/javax/annotation/meta/Exclusive.java b/src/main/java/javax/annotation/meta/Exclusive.java deleted file mode 100755 index 63d2d414..00000000 --- a/src/main/java/javax/annotation/meta/Exclusive.java +++ /dev/null @@ -1,30 +0,0 @@ -package javax.annotation.meta; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * This annotation can be applied to the value() element of an annotation that - * is annotated as a TypeQualifier. - * - *

- * For example, the following defines a type qualifier such that if you know a - * value is {@literal @Foo(1)}, then the value cannot be {@literal @Foo(2)} or - * {{@literal @Foo(3)}. - * - *

- * @TypeQualifier
- * @interface Foo {
- * 	@Exclusive
- * 	int value();
- * }
- * 
- * - */ - -@Documented -@Retention(RetentionPolicy.RUNTIME) -public @interface Exclusive { - -} diff --git a/src/main/java/javax/annotation/meta/Exhaustive.java b/src/main/java/javax/annotation/meta/Exhaustive.java deleted file mode 100755 index c83eeaa1..00000000 --- a/src/main/java/javax/annotation/meta/Exhaustive.java +++ /dev/null @@ -1,40 +0,0 @@ -package javax.annotation.meta; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * This annotation can be applied to the value() element of an annotation that - * is annotated as a TypeQualifier. This is only appropriate if the value field - * returns a value that is an Enumeration. - * - *

- * Applications of the type qualifier with different values are exclusive, and - * the enumeration is an exhaustive list of the possible values. - * - *

- * For example, the following defines a type qualifier such that if you know a - * value is neither {@literal @Foo(Color.Red)} or {@literal @Foo(Color.Blue)}, - * then the value must be {@literal @Foo(Color.Green)}. And if you know it is - * {@literal @Foo(Color.Green)}, you know it cannot be - * {@literal @Foo(Color.Red)} or {@literal @Foo(Color.Blue)} - * - *

- * @TypeQualifier
- * @interface Foo {
- * 	enum Color {
- * 		RED, BLUE, GREEN
- * 	};
- * 
- * 	@Exhaustive
- * 	Color value();
- * }
- * 
- */ - -@Documented -@Retention(RetentionPolicy.RUNTIME) -public @interface Exhaustive { - -} diff --git a/src/main/java/javax/annotation/meta/TypeQualifier.java b/src/main/java/javax/annotation/meta/TypeQualifier.java deleted file mode 100755 index 6451d7db..00000000 --- a/src/main/java/javax/annotation/meta/TypeQualifier.java +++ /dev/null @@ -1,28 +0,0 @@ -package javax.annotation.meta; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * This qualifier is applied to an annotation to denote that the annotation - * should be treated as a type qualifier. - */ -@Documented -@Target(ElementType.ANNOTATION_TYPE) -@Retention(RetentionPolicy.RUNTIME) -public @interface TypeQualifier { - - /** - * Describes the kinds of values the qualifier can be applied to. If a numeric - * class is provided (e.g., Number.class or Integer.class) then the annotation - * can also be applied to the corresponding primitive numeric types. - * - * @return a class object which denotes the type of the values the original - * annotation can be applied to. - */ - Class applicableTo() default Object.class; - -} diff --git a/src/main/java/javax/annotation/meta/TypeQualifierDefault.java b/src/main/java/javax/annotation/meta/TypeQualifierDefault.java deleted file mode 100755 index aec6869f..00000000 --- a/src/main/java/javax/annotation/meta/TypeQualifierDefault.java +++ /dev/null @@ -1,20 +0,0 @@ -package javax.annotation.meta; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * This qualifier is applied to an annotation to denote that the annotation - * defines a default type qualifier that is visible within the scope of the - * element it is applied to. - */ - -@Documented -@Target(ElementType.ANNOTATION_TYPE) -@Retention(RetentionPolicy.RUNTIME) -public @interface TypeQualifierDefault { - ElementType[] value() default {}; -} diff --git a/src/main/java/javax/annotation/meta/TypeQualifierNickname.java b/src/main/java/javax/annotation/meta/TypeQualifierNickname.java deleted file mode 100755 index 863dc1ed..00000000 --- a/src/main/java/javax/annotation/meta/TypeQualifierNickname.java +++ /dev/null @@ -1,30 +0,0 @@ -package javax.annotation.meta; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Target; - -/** - * This annotation is applied to a annotation, and marks the annotation as being - * a qualifier nickname. Applying a nickname annotation X to a element Y should - * be interpreted as having the same meaning as applying all of annotations of X - * (other than QualifierNickname) to Y. - * - *

- * Thus, you might define a qualifier SocialSecurityNumber as follows: - *

- * - *
- * @Documented
- * @TypeQualifierNickname
- * @Pattern("[0-9]{3}-[0-9]{2}-[0-9]{4}")
- * @Retention(RetentionPolicy.RUNTIME)
- * public @interface SocialSecurityNumber {
- * }
- * 
- */ -@Documented -@Target(ElementType.ANNOTATION_TYPE) -public @interface TypeQualifierNickname { - -} diff --git a/src/main/java/javax/annotation/meta/TypeQualifierValidator.java b/src/main/java/javax/annotation/meta/TypeQualifierValidator.java deleted file mode 100755 index d96a46b6..00000000 --- a/src/main/java/javax/annotation/meta/TypeQualifierValidator.java +++ /dev/null @@ -1,18 +0,0 @@ -package javax.annotation.meta; - -import java.lang.annotation.Annotation; - -import javax.annotation.Nonnull; - -public interface TypeQualifierValidator { - /** - * Given a type qualifier, check to see if a known specific constant value is an - * instance of the set of values denoted by the qualifier. - * - * @param annotation the type qualifier - * @param value the value to check - * @return a value indicating whether or not the value is an member of the - * values denoted by the type qualifier - */ - public @Nonnull When forConstantValue(@Nonnull A annotation, Object value); -} diff --git a/src/main/java/javax/annotation/meta/When.java b/src/main/java/javax/annotation/meta/When.java deleted file mode 100755 index 8abb4dd1..00000000 --- a/src/main/java/javax/annotation/meta/When.java +++ /dev/null @@ -1,23 +0,0 @@ -package javax.annotation.meta; - -/** - * Used to describe the relationship between a qualifier T and the set of values - * S possible on an annotated element. - * - * In particular, an issues should be reported if an ALWAYS or MAYBE value is - * used where a NEVER value is required, or if a NEVER or MAYBE value is used - * where an ALWAYS value is required. - * - * - */ -public enum When { - /** S is a subset of T */ - ALWAYS, - /** nothing definitive is known about the relation between S and T */ - UNKNOWN, - /** S intersection T is non empty and S - T is nonempty */ - MAYBE, - /** S intersection T is empty */ - NEVER; - -} diff --git a/src/main/java/javax/annotation/package.html b/src/main/java/javax/annotation/package.html deleted file mode 100755 index 42f60d90..00000000 --- a/src/main/java/javax/annotation/package.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - This package defines the common annotations. - - diff --git a/src/main/java/javax/annotation/security/DeclareRoles.java b/src/main/java/javax/annotation/security/DeclareRoles.java deleted file mode 100755 index 40c7515a..00000000 --- a/src/main/java/javax/annotation/security/DeclareRoles.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2005-2018 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://oss.oracle.com/licenses/CDDL+GPL-1.1 - * or LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.annotation.security; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * Used by application to declare security roles. It can be specified on a - * class. The value of the DeclareRoles annotation is a list of - * security role names. - * - * @since Common Annotations 1.0 - */ -@Documented -@Retention(RUNTIME) -@Target(TYPE) -public @interface DeclareRoles { - /** - * List of security role names. - */ - String[] value(); -} diff --git a/src/main/java/javax/annotation/security/DenyAll.java b/src/main/java/javax/annotation/security/DenyAll.java deleted file mode 100755 index a7e0f90f..00000000 --- a/src/main/java/javax/annotation/security/DenyAll.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2005-2018 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://oss.oracle.com/licenses/CDDL+GPL-1.1 - * or LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.annotation.security; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * Specifies that no security roles are allowed to invoke the specified - * method(s). - * - * @see javax.annotation.security.RolesAllowed - * @see javax.annotation.security.PermitAll - * @since Common Annotations 1.0 - */ -@Documented -@Retention(RUNTIME) -@Target({ TYPE, METHOD }) -public @interface DenyAll { -} diff --git a/src/main/java/javax/annotation/security/PermitAll.java b/src/main/java/javax/annotation/security/PermitAll.java deleted file mode 100755 index 9528268f..00000000 --- a/src/main/java/javax/annotation/security/PermitAll.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2005-2018 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://oss.oracle.com/licenses/CDDL+GPL-1.1 - * or LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.annotation.security; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * Specifies that all security roles are allowed to invoke the specified - * method(s) — i.e., that the specified method(s) are "unchecked". It can - * be specified on a class or on methods. Specifying it on the class means that - * it applies to all methods of the class. If specified at the method level, it - * only affects that method. If the RolesAllowed annotation is - * specified at the class level and this annotation is applied at the method - * level, the PermitAll annotation overrides the - * RolesAllowed annotation for the specified method. - * - * @see javax.annotation.security.RolesAllowed - * @see javax.annotation.security.DenyAll - * - * @since Common Annotations 1.0 - */ -@Documented -@Retention(RUNTIME) -@Target({ TYPE, METHOD }) -public @interface PermitAll { -} diff --git a/src/main/java/javax/annotation/security/RolesAllowed.java b/src/main/java/javax/annotation/security/RolesAllowed.java deleted file mode 100755 index 9a559491..00000000 --- a/src/main/java/javax/annotation/security/RolesAllowed.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2005-2018 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://oss.oracle.com/licenses/CDDL+GPL-1.1 - * or LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.annotation.security; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * Specifies the list of security roles permitted to access method(s) in an - * application. The value of the RolesAllowed annotation is a list - * of security role names. This annotation can be specified on a class or on - * method(s). Specifying it at a class level means that it applies to all the - * methods in the class. Specifying it on a method means that it is applicable - * to that method only. If applied at both the class and methods level, the - * method value overrides the class value if the two conflict. - * - * @since Common Annotations 1.0 - */ -@Documented -@Retention(RUNTIME) -@Target({ TYPE, METHOD }) -public @interface RolesAllowed { - /** - * List of roles that are permitted access. - */ - String[] value(); -} diff --git a/src/main/java/javax/annotation/security/RunAs.java b/src/main/java/javax/annotation/security/RunAs.java deleted file mode 100755 index 936e8735..00000000 --- a/src/main/java/javax/annotation/security/RunAs.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2005-2018 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://oss.oracle.com/licenses/CDDL+GPL-1.1 - * or LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.annotation.security; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * Defines the identity of the application during execution. This allows - * developers to execute an application under a particular role. The role must - * map to the user / group information in the container's security realm. Its - * value is the name of a security role. - * - * @since Common Annotations 1.0 - */ -@Documented -@Retention(RUNTIME) -@Target(TYPE) -public @interface RunAs { - /** - * Name of a security role. - */ - String value(); -} diff --git a/src/main/java/javax/annotation/security/package.html b/src/main/java/javax/annotation/security/package.html deleted file mode 100755 index 6300a135..00000000 --- a/src/main/java/javax/annotation/security/package.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - This package contains the security common annotations. - - diff --git a/src/main/java/javax/annotation/sql/DataSourceDefinition.java b/src/main/java/javax/annotation/sql/DataSourceDefinition.java deleted file mode 100755 index 3a579f7f..00000000 --- a/src/main/java/javax/annotation/sql/DataSourceDefinition.java +++ /dev/null @@ -1,355 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2009-2018 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://oss.oracle.com/licenses/CDDL+GPL-1.1 - * or LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.annotation.sql; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Repeatable; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation used to define a container DataSource to be - * registered with JNDI. The DataSource may be configured by - * setting the annotation elements for commonly used DataSource - * properties. Additional standard and vendor-specific properties may be - * specified using the properties element. - *

- * - * The data source will be registered under the name specified in the - * name element. It may be defined to be in any valid Java EE - * namespace, which will determine the accessibility of the data source from - * other components. - *

- * A JDBC driver implementation class of the appropriate type, either - * DataSource, ConnectionPoolDataSource, or - * XADataSource, must be indicated by the className - * element. The availability of the driver class will be assumed at runtime. - *

- * DataSource properties should not be specified more than once. If the url - * annotation element contains a DataSource property that was also specified - * using the corresponding annotation element or was specified in the properties - * annotation element, the precedence order is undefined and implementation - * specific: - *

- * - *

- *   @DataSourceDefinition(name="java:global/MyApp/MyDataSource",
- *      className="org.apache.derby.jdbc.ClientDataSource",
- *      url="jdbc:derby://localhost:1527/myDB;user=bill",
- *      user="lance",
- *      password="secret",
- *      databaseName="testDB",
- *      serverName="luckydog"
- *   )// DO NOT DO THIS!!!
- * 
- *

- * In the above example, the databaseName, user and - * serverName properties were specified as part of the - * url property and using the corresponding annotation elements. - * This should be avoided. - *

- * If the properties annotation element is used and contains a - * DataSource property that was also specified using the corresponding - * annotation element, the annotation element value takes precedence. For - * example: - *

- * - *

- *   @DataSourceDefinition(name="java:global/MyApp/MyDataSource",
- *      className="org.apache.derby.jdbc.ClientDataSource",
- *      user="lance",
- *      password="secret",
- *      databaseName="testDB",
- *      serverName="luckydog",
- *       properties= {"databaseName=myDB", "databaseProp=doThis"}
- *   )// DO NOT DO THIS!!!
- * 
- *

- * This would result in the following values being used when configuring the - * DataSource: - *

    - *
  • serverName=luckydog
  • - *
  • portNumber=1527
  • - *
  • databaseName=testDB
  • - *
  • user=lance
  • - *
  • password=secret
  • - *
  • databaseProp=doThis
  • - *
- *

- * Vendors are not required to support properties that do not normally apply to - * a specific data source type. For example, specifying the - * transactional property to be true but supplying a - * value for className that implements a data source class other - * than XADataSource may not be supported. - *

- * Vendor-specific properties may be combined with or used to override standard - * data source properties defined using this annotation. - *

- * DataSource properties that are specified and are not supported - * in a given configuration or cannot be mapped to a vendor specific - * configuration property may be ignored. - *

- * Examples:
- * - *

- *   @DataSourceDefinition(name="java:global/MyApp/MyDataSource",
- *      className="com.foobar.MyDataSource",
- *      portNumber=6689,
- *      serverName="myserver.com",
- *      user="lance",
- *      password="secret"
- *   )
- * 
- * 
- *

- * Using a URL:
- * - *

- *  @DataSourceDefinition(name="java:global/MyApp/MyDataSource",
- *    className="org.apache.derby.jdbc.ClientDataSource",
- *    url="jdbc:derby://localhost:1527/myDB",
- *    user="lance",
- *    password="secret"
- * )
- * 
- *

- * An example lookup of the DataSource from an EJB: - * - *

- * @Stateless
- * public class MyStatelessEJB {
- *   @Resource(lookup="java:global/MyApp/myDataSource")
- *    DataSource myDB;
- *      ...
- * }
- * 
- *

- * - * @see javax.sql.DataSource - * @see javax.sql.XADataSource - * @see javax.sql.ConnectionPoolDataSource - * @since Common Annotations 1.1 - */ -@Target({ ElementType.TYPE }) -@Retention(RetentionPolicy.RUNTIME) -@Repeatable(DataSourceDefinitions.class) -public @interface DataSourceDefinition { - - /** - * JNDI name by which the data source will be registered. - * - * @since 1.1 - */ - String name(); - - /** - * Name of a DataSource class that implements javax.sql.DataSource - * or javax.sql.XADataSource or - * javax.sql.ConnectionPoolDataSource. - * - * @since 1.1 - */ - String className(); - - /** - * Description of this data source - * - * @since 1.1 - */ - String description() default ""; - - /** - * A JDBC URL. If the url annotation element contains a DataSource - * property that was also specified using the corresponding annotation element, - * the precedence order is undefined and implementation specific. - * - * @since 1.1 - */ - String url() default ""; - - /** - * User name to use for connection authentication. - * - * @since 1.1 - */ - String user() default ""; - - /** - * Password to use for connection authentication. - * - * @since 1.1 - */ - String password() default ""; - - /** - * Name of a database on a server. - * - * @since 1.1 - */ - String databaseName() default ""; - - /** - * Port number where a server is listening for requests. - * - * @since 1.1 - */ - int portNumber() default -1; - - /** - * Database server name. - * - * @since 1.1 - */ - String serverName() default "localhost"; - - /** - * Isolation level for connections. The Isolation level must be one of the - * following: - *

- *

    - *
  • Connection.TRANSACTION_NONE, - *
  • Connection.TRANSACTION_READ_ UNCOMMITTED, - *
  • Connection.TRANSACTION_READ_COMMITTED, - *
  • Connection.TRANSACTION_REPEATABLE_READ, - *
  • Connection.TRANSACTION_SERIALIZABLE - *
- *

- * Default is vendor-specific. - * - * @since 1.1 - */ - int isolationLevel() default -1; - - /** - * Set to false if connections should not participate in - * transactions. - *

- * Default is to enlist in a transaction when one is active or becomes active. - * - * @since 1.1 - */ - boolean transactional() default true; - - /** - * Number of connections that should be created when a connection pool is - * initialized. - *

- * Default is vendor-specific - * - * @since 1.1 - */ - int initialPoolSize() default -1; - - /** - * Maximum number of connections that should be concurrently allocated for a - * connection pool. - *

- * Default is vendor-specific. - * - * @since 1.1 - */ - int maxPoolSize() default -1; - - /** - * Minimum number of connections that should be allocated for a connection pool. - *

- * Default is vendor-specific. - * - * @since 1.1 - */ - int minPoolSize() default -1; - - /** - * The number of seconds that a physical connection should remain unused in the - * pool before the connection is closed for a connection pool. - *

- * Default is vendor-specific - * - * @since 1.1 - */ - int maxIdleTime() default -1; - - /** - * The total number of statements that a connection pool should keep open. A - * value of 0 indicates that the caching of statements is disabled for a - * connection pool. - *

- * Default is vendor-specific - * - * @since 1.1 - */ - int maxStatements() default -1; - - /** - * Used to specify vendor-specific properties and less commonly used - * DataSource properties such as: - *

- *

    - *
  • dataSourceName - *
  • networkProtocol - *
  • propertyCycle - *
  • roleName - *
- *

- * Properties are specified using the format: propertyName=propertyValue - * with one property per array element. - *

- * If a DataSource property is specified in the properties element - * and the annotation element for the property is also specified, the annotation - * element value takes precedence. - * - * @since 1.1 - */ - String[] properties() default {}; - - /** - * Sets the maximum time in seconds that this data source will wait while - * attempting to connect to a database. A value of zero specifies that the - * timeout is the default system timeout if there is one; otherwise, it - * specifies that there is no timeout. - *

- * Default is vendor-specific. - * - * @since 1.1 - */ - int loginTimeout() default 0; -} diff --git a/src/main/java/javax/annotation/sql/DataSourceDefinitions.java b/src/main/java/javax/annotation/sql/DataSourceDefinitions.java deleted file mode 100755 index e94eed94..00000000 --- a/src/main/java/javax/annotation/sql/DataSourceDefinitions.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2009-2018 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://oss.oracle.com/licenses/CDDL+GPL-1.1 - * or LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.annotation.sql; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Declares one or more DataSourceDefinition annotations. - * - * @see javax.annotation.sql.DataSourceDefinition - * @since Common Annotations 1.1 - */ -@Target({ ElementType.TYPE }) -@Retention(RetentionPolicy.RUNTIME) -public @interface DataSourceDefinitions { - DataSourceDefinition[] value(); - -} diff --git a/src/main/java/jdk_internal/bidi/Annotation.java b/src/main/java/jdk_internal/bidi/Annotation.java index b0b6ed71..4a12557b 100755 --- a/src/main/java/jdk_internal/bidi/Annotation.java +++ b/src/main/java/jdk_internal/bidi/Annotation.java @@ -1,88 +1,88 @@ -/* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk_internal.bidi; - -/** - * An Annotation object is used as a wrapper for a text attribute value if the - * attribute has annotation characteristics. These characteristics are: - *

    - *
  • The text range that the attribute is applied to is critical to the - * semantics of the range. That means, the attribute cannot be applied to - * subranges of the text range that it applies to, and, if two adjacent text - * ranges have the same value for this attribute, the attribute still cannot be - * applied to the combined range as a whole with this value. - *
  • The attribute or its value usually do no longer apply if the underlying - * text is changed. - *
- * - * An example is grammatical information attached to a sentence: For the - * previous sentence, you can say that "an example" is the subject, but you - * cannot say the same about "an", "example", or "exam". When the text is - * changed, the grammatical information typically becomes invalid. Another - * example is Japanese reading information (yomi). - * - *

- * Wrapping the attribute value into an Annotation object guarantees that - * adjacent text runs don't get merged even if the attribute values are equal, - * and indicates to text containers that the attribute should be discarded if - * the underlying text is modified. - * - * @see AttributedCharacterIterator - * @since 1.2 - */ - -public class Annotation { - - /** - * Constructs an annotation record with the given value, which may be null. - * - * @param value the value of the attribute - */ - public Annotation(Object value) { - this.value = value; - } - - /** - * Returns the value of the attribute, which may be null. - * - * @return the value of the attribute - */ - public Object getValue() { - return value; - } - - /** - * Returns the String representation of this Annotation. - * - * @return the {@code String} representation of this {@code Annotation} - */ - public String toString() { - return getClass().getName() + "[value=" + value + "]"; - } - - private Object value; - -}; +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk_internal.bidi; + +/** + * An Annotation object is used as a wrapper for a text attribute value if the + * attribute has annotation characteristics. These characteristics are: + *

    + *
  • The text range that the attribute is applied to is critical to the + * semantics of the range. That means, the attribute cannot be applied to + * subranges of the text range that it applies to, and, if two adjacent text + * ranges have the same value for this attribute, the attribute still cannot be + * applied to the combined range as a whole with this value. + *
  • The attribute or its value usually do no longer apply if the underlying + * text is changed. + *
+ * + * An example is grammatical information attached to a sentence: For the + * previous sentence, you can say that "an example" is the subject, but you + * cannot say the same about "an", "example", or "exam". When the text is + * changed, the grammatical information typically becomes invalid. Another + * example is Japanese reading information (yomi). + * + *

+ * Wrapping the attribute value into an Annotation object guarantees that + * adjacent text runs don't get merged even if the attribute values are equal, + * and indicates to text containers that the attribute should be discarded if + * the underlying text is modified. + * + * @see AttributedCharacterIterator + * @since 1.2 + */ + +public class Annotation { + + /** + * Constructs an annotation record with the given value, which may be null. + * + * @param value the value of the attribute + */ + public Annotation(Object value) { + this.value = value; + } + + /** + * Returns the value of the attribute, which may be null. + * + * @return the value of the attribute + */ + public Object getValue() { + return value; + } + + /** + * Returns the String representation of this Annotation. + * + * @return the {@code String} representation of this {@code Annotation} + */ + public String toString() { + return getClass().getName() + "[value=" + value + "]"; + } + + private Object value; + +}; diff --git a/src/main/java/jdk_internal/bidi/AttributedCharacterIterator.java b/src/main/java/jdk_internal/bidi/AttributedCharacterIterator.java index 00223d36..4bdfd03e 100755 --- a/src/main/java/jdk_internal/bidi/AttributedCharacterIterator.java +++ b/src/main/java/jdk_internal/bidi/AttributedCharacterIterator.java @@ -1,299 +1,299 @@ -/* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk_internal.bidi; - -import java.io.InvalidObjectException; -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * An {@code AttributedCharacterIterator} allows iteration through both text and - * related attribute information. - * - *

- * An attribute is a key/value pair, identified by the key. No two attributes on - * a given character can have the same key. - * - *

- * The values for an attribute are immutable, or must not be mutated by clients - * or storage. They are always passed by reference, and not cloned. - * - *

- * A run with respect to an attribute is a maximum text range for - * which: - *

    - *
  • the attribute is undefined or {@code null} for the entire range, or - *
  • the attribute value is defined and has the same non-{@code null} value - * for the entire range. - *
- * - *

- * A run with respect to a set of attributes is a maximum text range - * for which this condition is met for each member attribute. - * - *

- * When getting a run with no explicit attributes specified (i.e., calling - * {@link #getRunStart()} and {@link #getRunLimit()}), any contiguous text - * segments having the same attributes (the same set of attribute/value pairs) - * are treated as separate runs if the attributes have been given to those text - * segments separately. - * - *

- * The returned indexes are limited to the range of the iterator. - * - *

- * The returned attribute information is limited to runs that contain the - * current character. - * - *

- * Attribute keys are instances of {@link AttributedCharacterIterator.Attribute} - * and its subclasses, such as {@link java.awt.font.TextAttribute}. - * - * @see AttributedCharacterIterator.Attribute - * @see java.awt.font.TextAttribute - * @see AttributedString - * @see Annotation - * @since 1.2 - */ - -public interface AttributedCharacterIterator extends CharacterIterator { - - /** - * Defines attribute keys that are used to identify text attributes. These keys - * are used in {@code AttributedCharacterIterator} and {@code AttributedString}. - * - * @see AttributedCharacterIterator - * @see AttributedString - * @since 1.2 - */ - - public static class Attribute implements Serializable { - - /** - * The name of this {@code Attribute}. The name is used primarily by - * {@code readResolve} to look up the corresponding predefined instance when - * deserializing an instance. - * - * @serial - */ - private String name; - - // table of all instances in this class, used by readResolve - private static final Map instanceMap = new HashMap<>(7); - - /** - * Constructs an {@code Attribute} with the given name. - * - * @param name the name of {@code Attribute} - */ - protected Attribute(String name) { - this.name = name; - if (this.getClass() == Attribute.class) { - instanceMap.put(name, this); - } - } - - /** - * Compares two objects for equality. This version only returns true for - * {@code x.equals(y)} if {@code x} and {@code y} refer to the same object, and - * guarantees this for all subclasses. - */ - public final boolean equals(Object obj) { - return super.equals(obj); - } - - /** - * Returns a hash code value for the object. This version is identical to the - * one in {@code Object}, but is also final. - */ - public final int hashCode() { - return super.hashCode(); - } - - /** - * Returns a string representation of the object. This version returns the - * concatenation of class name, {@code "("}, a name identifying the attribute - * and {@code ")"}. - */ - public String toString() { - return getClass().getName() + "(" + name + ")"; - } - - /** - * Returns the name of the attribute. - * - * @return the name of {@code Attribute} - */ - protected String getName() { - return name; - } - - /** - * Resolves instances being deserialized to the predefined constants. - * - * @return the resolved {@code Attribute} object - * @throws InvalidObjectException if the object to resolve is not an instance of - * {@code Attribute} - */ - protected Object readResolve() throws InvalidObjectException { - if (this.getClass() != Attribute.class) { - throw new InvalidObjectException("subclass didn't correctly implement readResolve"); - } - - Attribute instance = instanceMap.get(getName()); - if (instance != null) { - return instance; - } else { - throw new InvalidObjectException("unknown attribute name"); - } - } - - /** - * Attribute key for the language of some text. - *

- * Values are instances of {@link java.util.Locale Locale}. - * - * @see java.util.Locale - */ - public static final Attribute LANGUAGE = new Attribute("language"); - - /** - * Attribute key for the reading of some text. In languages where the written - * form and the pronunciation of a word are only loosely related (such as - * Japanese), it is often necessary to store the reading (pronunciation) along - * with the written form. - *

- * Values are instances of {@link Annotation} holding instances of - * {@link String}. - * - * @see Annotation - * @see java.lang.String - */ - public static final Attribute READING = new Attribute("reading"); - - /** - * Attribute key for input method segments. Input methods often break up text - * into segments, which usually correspond to words. - *

- * Values are instances of {@link Annotation} holding a {@code null} reference. - * - * @see Annotation - */ - public static final Attribute INPUT_METHOD_SEGMENT = new Attribute("input_method_segment"); - - // make sure the serial version doesn't change between compiler versions - private static final long serialVersionUID = -9142742483513960612L; - - }; - - /** - * Returns the index of the first character of the run with respect to all - * attributes containing the current character. - * - *

- * Any contiguous text segments having the same attributes (the same set of - * attribute/value pairs) are treated as separate runs if the attributes have - * been given to those text segments separately. - * - * @return the index of the first character of the run - */ - public int getRunStart(); - - /** - * Returns the index of the first character of the run with respect to the given - * {@code attribute} containing the current character. - * - * @param attribute the desired attribute. - * @return the index of the first character of the run - */ - public int getRunStart(Attribute attribute); - - /** - * Returns the index of the first character of the run with respect to the given - * {@code attributes} containing the current character. - * - * @param attributes a set of the desired attributes. - * @return the index of the first character of the run - */ - public int getRunStart(Set attributes); - - /** - * Returns the index of the first character following the run with respect to - * all attributes containing the current character. - * - *

- * Any contiguous text segments having the same attributes (the same set of - * attribute/value pairs) are treated as separate runs if the attributes have - * been given to those text segments separately. - * - * @return the index of the first character following the run - */ - public int getRunLimit(); - - /** - * Returns the index of the first character following the run with respect to - * the given {@code attribute} containing the current character. - * - * @param attribute the desired attribute - * @return the index of the first character following the run - */ - public int getRunLimit(Attribute attribute); - - /** - * Returns the index of the first character following the run with respect to - * the given {@code attributes} containing the current character. - * - * @param attributes a set of the desired attributes - * @return the index of the first character following the run - */ - public int getRunLimit(Set attributes); - - /** - * Returns a map with the attributes defined on the current character. - * - * @return a map with the attributes defined on the current character - */ - public Map getAttributes(); - - /** - * Returns the value of the named {@code attribute} for the current character. - * Returns {@code null} if the {@code attribute} is not defined. - * - * @param attribute the desired attribute - * @return the value of the named {@code attribute} or {@code null} - */ - public Object getAttribute(Attribute attribute); - - /** - * Returns the keys of all attributes defined on the iterator's text range. The - * set is empty if no attributes are defined. - * - * @return the keys of all attributes - */ - public Set getAllAttributeKeys(); -}; +/* + * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk_internal.bidi; + +import java.io.InvalidObjectException; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * An {@code AttributedCharacterIterator} allows iteration through both text and + * related attribute information. + * + *

+ * An attribute is a key/value pair, identified by the key. No two attributes on + * a given character can have the same key. + * + *

+ * The values for an attribute are immutable, or must not be mutated by clients + * or storage. They are always passed by reference, and not cloned. + * + *

+ * A run with respect to an attribute is a maximum text range for + * which: + *

    + *
  • the attribute is undefined or {@code null} for the entire range, or + *
  • the attribute value is defined and has the same non-{@code null} value + * for the entire range. + *
+ * + *

+ * A run with respect to a set of attributes is a maximum text range + * for which this condition is met for each member attribute. + * + *

+ * When getting a run with no explicit attributes specified (i.e., calling + * {@link #getRunStart()} and {@link #getRunLimit()}), any contiguous text + * segments having the same attributes (the same set of attribute/value pairs) + * are treated as separate runs if the attributes have been given to those text + * segments separately. + * + *

+ * The returned indexes are limited to the range of the iterator. + * + *

+ * The returned attribute information is limited to runs that contain the + * current character. + * + *

+ * Attribute keys are instances of {@link AttributedCharacterIterator.Attribute} + * and its subclasses, such as {@link java.awt.font.TextAttribute}. + * + * @see AttributedCharacterIterator.Attribute + * @see java.awt.font.TextAttribute + * @see AttributedString + * @see Annotation + * @since 1.2 + */ + +public interface AttributedCharacterIterator extends CharacterIterator { + + /** + * Defines attribute keys that are used to identify text attributes. These keys + * are used in {@code AttributedCharacterIterator} and {@code AttributedString}. + * + * @see AttributedCharacterIterator + * @see AttributedString + * @since 1.2 + */ + + public static class Attribute implements Serializable { + + /** + * The name of this {@code Attribute}. The name is used primarily by + * {@code readResolve} to look up the corresponding predefined instance when + * deserializing an instance. + * + * @serial + */ + private String name; + + // table of all instances in this class, used by readResolve + private static final Map instanceMap = new HashMap<>(7); + + /** + * Constructs an {@code Attribute} with the given name. + * + * @param name the name of {@code Attribute} + */ + protected Attribute(String name) { + this.name = name; + if (this.getClass() == Attribute.class) { + instanceMap.put(name, this); + } + } + + /** + * Compares two objects for equality. This version only returns true for + * {@code x.equals(y)} if {@code x} and {@code y} refer to the same object, and + * guarantees this for all subclasses. + */ + public final boolean equals(Object obj) { + return super.equals(obj); + } + + /** + * Returns a hash code value for the object. This version is identical to the + * one in {@code Object}, but is also final. + */ + public final int hashCode() { + return super.hashCode(); + } + + /** + * Returns a string representation of the object. This version returns the + * concatenation of class name, {@code "("}, a name identifying the attribute + * and {@code ")"}. + */ + public String toString() { + return getClass().getName() + "(" + name + ")"; + } + + /** + * Returns the name of the attribute. + * + * @return the name of {@code Attribute} + */ + protected String getName() { + return name; + } + + /** + * Resolves instances being deserialized to the predefined constants. + * + * @return the resolved {@code Attribute} object + * @throws InvalidObjectException if the object to resolve is not an instance of + * {@code Attribute} + */ + protected Object readResolve() throws InvalidObjectException { + if (this.getClass() != Attribute.class) { + throw new InvalidObjectException("subclass didn't correctly implement readResolve"); + } + + Attribute instance = instanceMap.get(getName()); + if (instance != null) { + return instance; + } else { + throw new InvalidObjectException("unknown attribute name"); + } + } + + /** + * Attribute key for the language of some text. + *

+ * Values are instances of {@link java.util.Locale Locale}. + * + * @see java.util.Locale + */ + public static final Attribute LANGUAGE = new Attribute("language"); + + /** + * Attribute key for the reading of some text. In languages where the written + * form and the pronunciation of a word are only loosely related (such as + * Japanese), it is often necessary to store the reading (pronunciation) along + * with the written form. + *

+ * Values are instances of {@link Annotation} holding instances of + * {@link String}. + * + * @see Annotation + * @see java.lang.String + */ + public static final Attribute READING = new Attribute("reading"); + + /** + * Attribute key for input method segments. Input methods often break up text + * into segments, which usually correspond to words. + *

+ * Values are instances of {@link Annotation} holding a {@code null} reference. + * + * @see Annotation + */ + public static final Attribute INPUT_METHOD_SEGMENT = new Attribute("input_method_segment"); + + // make sure the serial version doesn't change between compiler versions + private static final long serialVersionUID = -9142742483513960612L; + + }; + + /** + * Returns the index of the first character of the run with respect to all + * attributes containing the current character. + * + *

+ * Any contiguous text segments having the same attributes (the same set of + * attribute/value pairs) are treated as separate runs if the attributes have + * been given to those text segments separately. + * + * @return the index of the first character of the run + */ + public int getRunStart(); + + /** + * Returns the index of the first character of the run with respect to the given + * {@code attribute} containing the current character. + * + * @param attribute the desired attribute. + * @return the index of the first character of the run + */ + public int getRunStart(Attribute attribute); + + /** + * Returns the index of the first character of the run with respect to the given + * {@code attributes} containing the current character. + * + * @param attributes a set of the desired attributes. + * @return the index of the first character of the run + */ + public int getRunStart(Set attributes); + + /** + * Returns the index of the first character following the run with respect to + * all attributes containing the current character. + * + *

+ * Any contiguous text segments having the same attributes (the same set of + * attribute/value pairs) are treated as separate runs if the attributes have + * been given to those text segments separately. + * + * @return the index of the first character following the run + */ + public int getRunLimit(); + + /** + * Returns the index of the first character following the run with respect to + * the given {@code attribute} containing the current character. + * + * @param attribute the desired attribute + * @return the index of the first character following the run + */ + public int getRunLimit(Attribute attribute); + + /** + * Returns the index of the first character following the run with respect to + * the given {@code attributes} containing the current character. + * + * @param attributes a set of the desired attributes + * @return the index of the first character following the run + */ + public int getRunLimit(Set attributes); + + /** + * Returns a map with the attributes defined on the current character. + * + * @return a map with the attributes defined on the current character + */ + public Map getAttributes(); + + /** + * Returns the value of the named {@code attribute} for the current character. + * Returns {@code null} if the {@code attribute} is not defined. + * + * @param attribute the desired attribute + * @return the value of the named {@code attribute} or {@code null} + */ + public Object getAttribute(Attribute attribute); + + /** + * Returns the keys of all attributes defined on the iterator's text range. The + * set is empty if no attributes are defined. + * + * @return the keys of all attributes + */ + public Set getAllAttributeKeys(); +}; diff --git a/src/main/java/jdk_internal/bidi/AttributedString.java b/src/main/java/jdk_internal/bidi/AttributedString.java index cdabbc70..1f98f1e1 100755 --- a/src/main/java/jdk_internal/bidi/AttributedString.java +++ b/src/main/java/jdk_internal/bidi/AttributedString.java @@ -1,1109 +1,1109 @@ -/* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk_internal.bidi; - -import java.util.*; - -import jdk_internal.bidi.AttributedCharacterIterator.Attribute; - -/** - * An AttributedString holds text and related attribute information. It may be - * used as the actual data storage in some cases where a text reader wants to - * access attributed text through the AttributedCharacterIterator interface. - * - *

- * An attribute is a key/value pair, identified by the key. No two attributes on - * a given character can have the same key. - * - *

- * The values for an attribute are immutable, or must not be mutated by clients - * or storage. They are always passed by reference, and not cloned. - * - * @see AttributedCharacterIterator - * @see Annotation - * @since 1.2 - */ - -public class AttributedString { - // field holding the text - String text; - - // Fields holding run attribute information. - // Run attributes are organized by run. - // Arrays are always of equal lengths (the current capacity). - // Since there are no vectors of int, we have to use arrays. - private static final int INITIAL_CAPACITY = 10; - int runCount; // actual number of runs, <= current capacity - int[] runStarts; // start index for each run - Vector[] runAttributes; // vector of attribute keys for each run - Vector[] runAttributeValues; // parallel vector of attribute values for each run - - /** - * Constructs an AttributedString instance with the given - * AttributedCharacterIterators. - * - * @param iterators AttributedCharacterIterators to construct AttributedString - * from. - * @throws NullPointerException if iterators is null - */ - AttributedString(AttributedCharacterIterator[] iterators) { - if (iterators == null) { - throw new NullPointerException("Iterators must not be null"); - } - if (iterators.length == 0) { - text = ""; - } else { - // Build the String contents - StringBuffer buffer = new StringBuffer(); - for (int counter = 0; counter < iterators.length; counter++) { - appendContents(buffer, iterators[counter]); - } - - text = buffer.toString(); - - if (!text.isEmpty()) { - // Determine the runs, creating a new run when the attributes - // differ. - int offset = 0; - Map last = null; - - for (int counter = 0; counter < iterators.length; counter++) { - AttributedCharacterIterator iterator = iterators[counter]; - int start = iterator.getBeginIndex(); - int end = iterator.getEndIndex(); - int index = start; - - while (index < end) { - iterator.setIndex(index); - - Map attrs = iterator.getAttributes(); - - if (mapsDiffer(last, attrs)) { - setAttributes(attrs, index - start + offset); - } - last = attrs; - index = iterator.getRunLimit(); - } - offset += (end - start); - } - } - } - } - - /** - * Constructs an AttributedString instance with the given text. - * - * @param text The text for this attributed string. - * @throws NullPointerException if {@code text} is null. - */ - public AttributedString(String text) { - if (text == null) { - throw new NullPointerException(); - } - this.text = text; - } - - /** - * Constructs an AttributedString instance with the given text and attributes. - * - * @param text The text for this attributed string. - * @param attributes The attributes that apply to the entire string. - * @throws NullPointerException if {@code text} or {@code attributes} is - * null. - * @throws IllegalArgumentException if the text has length 0 and the attributes - * parameter is not an empty Map (attributes - * cannot be applied to a 0-length range). - */ - public AttributedString(String text, Map attributes) { - if (text == null || attributes == null) { - throw new NullPointerException(); - } - this.text = text; - - if (text.isEmpty()) { - if (attributes.isEmpty()) - return; - throw new IllegalArgumentException("Can't add attribute to 0-length text"); - } - - int attributeCount = attributes.size(); - if (attributeCount > 0) { - createRunAttributeDataVectors(); - Vector newRunAttributes = new Vector<>(attributeCount); - Vector newRunAttributeValues = new Vector<>(attributeCount); - runAttributes[0] = newRunAttributes; - runAttributeValues[0] = newRunAttributeValues; - - Iterator> iterator = attributes.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - newRunAttributes.addElement(entry.getKey()); - newRunAttributeValues.addElement(entry.getValue()); - } - } - } - - /** - * Constructs an AttributedString instance with the given attributed text - * represented by AttributedCharacterIterator. - * - * @param text The text for this attributed string. - * @throws NullPointerException if {@code text} is null. - */ - public AttributedString(AttributedCharacterIterator text) { - // If performance is critical, this constructor should be - // implemented here rather than invoking the constructor for a - // subrange. We can avoid some range checking in the loops. - this(text, text.getBeginIndex(), text.getEndIndex(), null); - } - - /** - * Constructs an AttributedString instance with the subrange of the given - * attributed text represented by AttributedCharacterIterator. If the given - * range produces an empty text, all attributes will be discarded. Note that any - * attributes wrapped by an Annotation object are discarded for a subrange of - * the original attribute range. - * - * @param text The text for this attributed string. - * @param beginIndex Index of the first character of the range. - * @param endIndex Index of the character following the last character of the - * range. - * @throws NullPointerException if {@code text} is null. - * @throws IllegalArgumentException if the subrange given by beginIndex and - * endIndex is out of the text range. - * @see java.text.Annotation - */ - public AttributedString(AttributedCharacterIterator text, int beginIndex, int endIndex) { - this(text, beginIndex, endIndex, null); - } - - /** - * Constructs an AttributedString instance with the subrange of the given - * attributed text represented by AttributedCharacterIterator. Only attributes - * that match the given attributes will be incorporated into the instance. If - * the given range produces an empty text, all attributes will be discarded. - * Note that any attributes wrapped by an Annotation object are discarded for a - * subrange of the original attribute range. - * - * @param text The text for this attributed string. - * @param beginIndex Index of the first character of the range. - * @param endIndex Index of the character following the last character of the - * range. - * @param attributes Specifies attributes to be extracted from the text. If null - * is specified, all available attributes will be used. - * @throws NullPointerException if {@code text} is null. - * @throws IllegalArgumentException if the subrange given by beginIndex and - * endIndex is out of the text range. - * @see java.text.Annotation - */ - public AttributedString(AttributedCharacterIterator text, int beginIndex, int endIndex, Attribute[] attributes) { - if (text == null) { - throw new NullPointerException(); - } - - // Validate the given subrange - int textBeginIndex = text.getBeginIndex(); - int textEndIndex = text.getEndIndex(); - if (beginIndex < textBeginIndex || endIndex > textEndIndex || beginIndex > endIndex) - throw new IllegalArgumentException("Invalid substring range"); - - // Copy the given string - StringBuilder textBuilder = new StringBuilder(); - text.setIndex(beginIndex); - for (char c = text.current(); text.getIndex() < endIndex; c = text.next()) - textBuilder.append(c); - this.text = textBuilder.toString(); - - if (beginIndex == endIndex) - return; - - // Select attribute keys to be taken care of - HashSet keys = new HashSet<>(); - if (attributes == null) { - keys.addAll(text.getAllAttributeKeys()); - } else { - for (int i = 0; i < attributes.length; i++) - keys.add(attributes[i]); - keys.retainAll(text.getAllAttributeKeys()); - } - if (keys.isEmpty()) - return; - - // Get and set attribute runs for each attribute name. Need to - // scan from the top of the text so that we can discard any - // Annotation that is no longer applied to a subset text segment. - Iterator itr = keys.iterator(); - while (itr.hasNext()) { - Attribute attributeKey = itr.next(); - text.setIndex(textBeginIndex); - while (text.getIndex() < endIndex) { - int start = text.getRunStart(attributeKey); - int limit = text.getRunLimit(attributeKey); - Object value = text.getAttribute(attributeKey); - - if (value != null) { - if (value instanceof Annotation) { - if (start >= beginIndex && limit <= endIndex) { - addAttribute(attributeKey, value, start - beginIndex, limit - beginIndex); - } else { - if (limit > endIndex) - break; - } - } else { - // if the run is beyond the given (subset) range, we - // don't need to process further. - if (start >= endIndex) - break; - if (limit > beginIndex) { - // attribute is applied to any subrange - if (start < beginIndex) - start = beginIndex; - if (limit > endIndex) - limit = endIndex; - if (start != limit) { - addAttribute(attributeKey, value, start - beginIndex, limit - beginIndex); - } - } - } - } - text.setIndex(limit); - } - } - } - - /** - * Adds an attribute to the entire string. - * - * @param attribute the attribute key - * @param value the value of the attribute; may be null - * @throws NullPointerException if {@code attribute} is null. - * @throws IllegalArgumentException if the AttributedString has length 0 - * (attributes cannot be applied to a 0-length - * range). - */ - public void addAttribute(Attribute attribute, Object value) { - - if (attribute == null) { - throw new NullPointerException(); - } - - int len = length(); - if (len == 0) { - throw new IllegalArgumentException("Can't add attribute to 0-length text"); - } - - addAttributeImpl(attribute, value, 0, len); - } - - /** - * Adds an attribute to a subrange of the string. - * - * @param attribute the attribute key - * @param value The value of the attribute. May be null. - * @param beginIndex Index of the first character of the range. - * @param endIndex Index of the character following the last character of the - * range. - * @throws NullPointerException if {@code attribute} is null. - * @throws IllegalArgumentException if beginIndex is less than 0, endIndex is - * greater than the length of the string, or - * beginIndex and endIndex together don't - * define a non-empty subrange of the string. - */ - public void addAttribute(Attribute attribute, Object value, int beginIndex, int endIndex) { - - if (attribute == null) { - throw new NullPointerException(); - } - - if (beginIndex < 0 || endIndex > length() || beginIndex >= endIndex) { - throw new IllegalArgumentException("Invalid substring range"); - } - - addAttributeImpl(attribute, value, beginIndex, endIndex); - } - - /** - * Adds a set of attributes to a subrange of the string. - * - * @param attributes The attributes to be added to the string. - * @param beginIndex Index of the first character of the range. - * @param endIndex Index of the character following the last character of the - * range. - * @throws NullPointerException if {@code attributes} is null. - * @throws IllegalArgumentException if beginIndex is less than 0, endIndex is - * greater than the length of the string, or - * beginIndex and endIndex together don't - * define a non-empty subrange of the string - * and the attributes parameter is not an empty - * Map. - */ - public void addAttributes(Map attributes, int beginIndex, int endIndex) { - if (attributes == null) { - throw new NullPointerException(); - } - - if (beginIndex < 0 || endIndex > length() || beginIndex > endIndex) { - throw new IllegalArgumentException("Invalid substring range"); - } - if (beginIndex == endIndex) { - if (attributes.isEmpty()) - return; - throw new IllegalArgumentException("Can't add attribute to 0-length text"); - } - - // make sure we have run attribute data vectors - if (runCount == 0) { - createRunAttributeDataVectors(); - } - - // break up runs if necessary - int beginRunIndex = ensureRunBreak(beginIndex); - int endRunIndex = ensureRunBreak(endIndex); - - Iterator> iterator = attributes.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - addAttributeRunData(entry.getKey(), entry.getValue(), beginRunIndex, endRunIndex); - } - } - - private synchronized void addAttributeImpl(Attribute attribute, Object value, int beginIndex, int endIndex) { - - // make sure we have run attribute data vectors - if (runCount == 0) { - createRunAttributeDataVectors(); - } - - // break up runs if necessary - int beginRunIndex = ensureRunBreak(beginIndex); - int endRunIndex = ensureRunBreak(endIndex); - - addAttributeRunData(attribute, value, beginRunIndex, endRunIndex); - } - - private final void createRunAttributeDataVectors() { - // use temporary variables so things remain consistent in case of an exception - int[] newRunStarts = new int[INITIAL_CAPACITY]; - - @SuppressWarnings("unchecked") - Vector[] newRunAttributes = (Vector[]) new Vector[INITIAL_CAPACITY]; - - @SuppressWarnings("unchecked") - Vector[] newRunAttributeValues = (Vector[]) new Vector[INITIAL_CAPACITY]; - - runStarts = newRunStarts; - runAttributes = newRunAttributes; - runAttributeValues = newRunAttributeValues; - runCount = 1; // assume initial run starting at index 0 - } - - // ensure there's a run break at offset, return the index of the run - private final int ensureRunBreak(int offset) { - return ensureRunBreak(offset, true); - } - - /** - * Ensures there is a run break at offset, returning the index of the run. If - * this results in splitting a run, two things can happen: - *
    - *
  • If copyAttrs is true, the attributes from the existing run will be placed - * in both of the newly created runs. - *
  • If copyAttrs is false, the attributes from the existing run will NOT be - * copied to the run to the right (>= offset) of the break, but will exist on - * the run to the left (< offset). - *
- */ - private final int ensureRunBreak(int offset, boolean copyAttrs) { - if (offset == length()) { - return runCount; - } - - // search for the run index where this offset should be - int runIndex = 0; - while (runIndex < runCount && runStarts[runIndex] < offset) { - runIndex++; - } - - // if the offset is at a run start already, we're done - if (runIndex < runCount && runStarts[runIndex] == offset) { - return runIndex; - } - - // we'll have to break up a run - // first, make sure we have enough space in our arrays - int currentCapacity = runStarts.length; - if (runCount == currentCapacity) { - // We need to resize - we grow capacity by 25%. - int newCapacity = currentCapacity + (currentCapacity >> 2); - - // use temporary variables so things remain consistent in case of an exception - int[] newRunStarts = Arrays.copyOf(runStarts, newCapacity); - Vector[] newRunAttributes = Arrays.copyOf(runAttributes, newCapacity); - Vector[] newRunAttributeValues = Arrays.copyOf(runAttributeValues, newCapacity); - - runStarts = newRunStarts; - runAttributes = newRunAttributes; - runAttributeValues = newRunAttributeValues; - } - - // make copies of the attribute information of the old run that the new one used - // to be part of - // use temporary variables so things remain consistent in case of an exception - Vector newRunAttributes = null; - Vector newRunAttributeValues = null; - - if (copyAttrs) { - Vector oldRunAttributes = runAttributes[runIndex - 1]; - Vector oldRunAttributeValues = runAttributeValues[runIndex - 1]; - if (oldRunAttributes != null) { - newRunAttributes = new Vector<>(oldRunAttributes); - } - if (oldRunAttributeValues != null) { - newRunAttributeValues = new Vector<>(oldRunAttributeValues); - } - } - - // now actually break up the run - runCount++; - for (int i = runCount - 1; i > runIndex; i--) { - runStarts[i] = runStarts[i - 1]; - runAttributes[i] = runAttributes[i - 1]; - runAttributeValues[i] = runAttributeValues[i - 1]; - } - runStarts[runIndex] = offset; - runAttributes[runIndex] = newRunAttributes; - runAttributeValues[runIndex] = newRunAttributeValues; - - return runIndex; - } - - // add the attribute attribute/value to all runs where beginRunIndex <= runIndex - // < endRunIndex - private void addAttributeRunData(Attribute attribute, Object value, int beginRunIndex, int endRunIndex) { - - for (int i = beginRunIndex; i < endRunIndex; i++) { - int keyValueIndex = -1; // index of key and value in our vectors; assume we don't have an entry yet - if (runAttributes[i] == null) { - Vector newRunAttributes = new Vector<>(); - Vector newRunAttributeValues = new Vector<>(); - runAttributes[i] = newRunAttributes; - runAttributeValues[i] = newRunAttributeValues; - } else { - // check whether we have an entry already - keyValueIndex = runAttributes[i].indexOf(attribute); - } - - if (keyValueIndex == -1) { - // create new entry - int oldSize = runAttributes[i].size(); - runAttributes[i].addElement(attribute); - try { - runAttributeValues[i].addElement(value); - } catch (Exception e) { - runAttributes[i].setSize(oldSize); - runAttributeValues[i].setSize(oldSize); - } - } else { - // update existing entry - runAttributeValues[i].set(keyValueIndex, value); - } - } - } - - /** - * Creates an AttributedCharacterIterator instance that provides access to the - * entire contents of this string. - * - * @return An iterator providing access to the text and its attributes. - */ - public AttributedCharacterIterator getIterator() { - return getIterator(null, 0, length()); - } - - /** - * Creates an AttributedCharacterIterator instance that provides access to - * selected contents of this string. Information about attributes not listed in - * attributes that the implementor may have need not be made accessible through - * the iterator. If the list is null, all available attribute information should - * be made accessible. - * - * @param attributes a list of attributes that the client is interested in - * @return an iterator providing access to the entire text and its selected - * attributes - */ - public AttributedCharacterIterator getIterator(Attribute[] attributes) { - return getIterator(attributes, 0, length()); - } - - /** - * Creates an AttributedCharacterIterator instance that provides access to - * selected contents of this string. Information about attributes not listed in - * attributes that the implementor may have need not be made accessible through - * the iterator. If the list is null, all available attribute information should - * be made accessible. - * - * @param attributes a list of attributes that the client is interested in - * @param beginIndex the index of the first character - * @param endIndex the index of the character following the last character - * @return an iterator providing access to the text and its attributes - * @throws IllegalArgumentException if beginIndex is less than 0, endIndex is - * greater than the length of the string, or - * beginIndex is greater than endIndex. - */ - public AttributedCharacterIterator getIterator(Attribute[] attributes, int beginIndex, int endIndex) { - return new AttributedStringIterator(attributes, beginIndex, endIndex); - } - - // all (with the exception of length) reading operations are private, - // since AttributedString instances are accessed through iterators. - - // length is package private so that CharacterIteratorFieldDelegate can - // access it without creating an AttributedCharacterIterator. - int length() { - return text.length(); - } - - private char charAt(int index) { - return text.charAt(index); - } - - private synchronized Object getAttribute(Attribute attribute, int runIndex) { - Vector currentRunAttributes = runAttributes[runIndex]; - Vector currentRunAttributeValues = runAttributeValues[runIndex]; - if (currentRunAttributes == null) { - return null; - } - int attributeIndex = currentRunAttributes.indexOf(attribute); - if (attributeIndex != -1) { - return currentRunAttributeValues.elementAt(attributeIndex); - } else { - return null; - } - } - - // gets an attribute value, but returns an annotation only if it's range does - // not extend outside the range beginIndex..endIndex - private Object getAttributeCheckRange(Attribute attribute, int runIndex, int beginIndex, int endIndex) { - Object value = getAttribute(attribute, runIndex); - if (value instanceof Annotation) { - // need to check whether the annotation's range extends outside the iterator's - // range - if (beginIndex > 0) { - int currIndex = runIndex; - int runStart = runStarts[currIndex]; - while (runStart >= beginIndex && valuesMatch(value, getAttribute(attribute, currIndex - 1))) { - currIndex--; - runStart = runStarts[currIndex]; - } - if (runStart < beginIndex) { - // annotation's range starts before iterator's range - return null; - } - } - int textLength = length(); - if (endIndex < textLength) { - int currIndex = runIndex; - int runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1] : textLength; - while (runLimit <= endIndex && valuesMatch(value, getAttribute(attribute, currIndex + 1))) { - currIndex++; - runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1] : textLength; - } - if (runLimit > endIndex) { - // annotation's range ends after iterator's range - return null; - } - } - // annotation's range is subrange of iterator's range, - // so we can return the value - } - return value; - } - - // returns whether all specified attributes have equal values in the runs with - // the given indices - private boolean attributeValuesMatch(Set attributes, int runIndex1, int runIndex2) { - Iterator iterator = attributes.iterator(); - while (iterator.hasNext()) { - Attribute key = iterator.next(); - if (!valuesMatch(getAttribute(key, runIndex1), getAttribute(key, runIndex2))) { - return false; - } - } - return true; - } - - // returns whether the two objects are either both null or equal - private static final boolean valuesMatch(Object value1, Object value2) { - if (value1 == null) { - return value2 == null; - } else { - return value1.equals(value2); - } - } - - /** - * Appends the contents of the CharacterIterator iterator into the StringBuffer - * buf. - */ - private final void appendContents(StringBuffer buf, CharacterIterator iterator) { - int index = iterator.getBeginIndex(); - int end = iterator.getEndIndex(); - - while (index < end) { - iterator.setIndex(index++); - buf.append(iterator.current()); - } - } - - /** - * Sets the attributes for the range from offset to the next run break - * (typically the end of the text) to the ones specified in attrs. This is only - * meant to be called from the constructor! - */ - private void setAttributes(Map attrs, int offset) { - if (runCount == 0) { - createRunAttributeDataVectors(); - } - - int index = ensureRunBreak(offset, false); - int size; - - if (attrs != null && (size = attrs.size()) > 0) { - Vector runAttrs = new Vector<>(size); - Vector runValues = new Vector<>(size); - Iterator> iterator = attrs.entrySet().iterator(); - - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - - runAttrs.add(entry.getKey()); - runValues.add(entry.getValue()); - } - runAttributes[index] = runAttrs; - runAttributeValues[index] = runValues; - } - } - - /** - * Returns true if the attributes specified in last and attrs differ. - */ - private static boolean mapsDiffer(Map last, Map attrs) { - if (last == null) { - return (attrs != null && attrs.size() > 0); - } - return (!last.equals(attrs)); - } - - // the iterator class associated with this string class - - private final class AttributedStringIterator implements AttributedCharacterIterator { - - // note on synchronization: - // we don't synchronize on the iterator, assuming that an iterator is only used - // in one thread. - // we do synchronize access to the AttributedString however, since it's more - // likely to be shared between threads. - - // start and end index for our iteration - private int beginIndex; - private int endIndex; - - // attributes that our client is interested in - private Attribute[] relevantAttributes; - - // the current index for our iteration - // invariant: beginIndex <= currentIndex <= endIndex - private int currentIndex; - - // information about the run that includes currentIndex - private int currentRunIndex; - private int currentRunStart; - private int currentRunLimit; - - // constructor - AttributedStringIterator(Attribute[] attributes, int beginIndex, int endIndex) { - - if (beginIndex < 0 || beginIndex > endIndex || endIndex > length()) { - throw new IllegalArgumentException("Invalid substring range"); - } - - this.beginIndex = beginIndex; - this.endIndex = endIndex; - this.currentIndex = beginIndex; - updateRunInfo(); - if (attributes != null) { - relevantAttributes = attributes.clone(); - } - } - - // Object methods. See documentation in that class. - - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof AttributedStringIterator)) { - return false; - } - - AttributedStringIterator that = (AttributedStringIterator) obj; - - if (AttributedString.this != that.getString()) - return false; - if (currentIndex != that.currentIndex || beginIndex != that.beginIndex || endIndex != that.endIndex) - return false; - return true; - } - - public int hashCode() { - return text.hashCode() ^ currentIndex ^ beginIndex ^ endIndex; - } - - public Object clone() { - try { - AttributedStringIterator other = (AttributedStringIterator) super.clone(); - return other; - } catch (CloneNotSupportedException e) { - throw new InternalError(e); - } - } - - // CharacterIterator methods. See documentation in that interface. - - public char first() { - return internalSetIndex(beginIndex); - } - - public char last() { - if (endIndex == beginIndex) { - return internalSetIndex(endIndex); - } else { - return internalSetIndex(endIndex - 1); - } - } - - public char current() { - if (currentIndex == endIndex) { - return DONE; - } else { - return charAt(currentIndex); - } - } - - public char next() { - if (currentIndex < endIndex) { - return internalSetIndex(currentIndex + 1); - } else { - return DONE; - } - } - - public char previous() { - if (currentIndex > beginIndex) { - return internalSetIndex(currentIndex - 1); - } else { - return DONE; - } - } - - public char setIndex(int position) { - if (position < beginIndex || position > endIndex) - throw new IllegalArgumentException("Invalid index"); - return internalSetIndex(position); - } - - public int getBeginIndex() { - return beginIndex; - } - - public int getEndIndex() { - return endIndex; - } - - public int getIndex() { - return currentIndex; - } - - // AttributedCharacterIterator methods. See documentation in that interface. - - public int getRunStart() { - return currentRunStart; - } - - public int getRunStart(Attribute attribute) { - if (currentRunStart == beginIndex || currentRunIndex == -1) { - return currentRunStart; - } else { - Object value = getAttribute(attribute); - int runStart = currentRunStart; - int runIndex = currentRunIndex; - while (runStart > beginIndex - && valuesMatch(value, AttributedString.this.getAttribute(attribute, runIndex - 1))) { - runIndex--; - runStart = runStarts[runIndex]; - } - if (runStart < beginIndex) { - runStart = beginIndex; - } - return runStart; - } - } - - public int getRunStart(Set attributes) { - if (currentRunStart == beginIndex || currentRunIndex == -1) { - return currentRunStart; - } else { - int runStart = currentRunStart; - int runIndex = currentRunIndex; - while (runStart > beginIndex - && AttributedString.this.attributeValuesMatch(attributes, currentRunIndex, runIndex - 1)) { - runIndex--; - runStart = runStarts[runIndex]; - } - if (runStart < beginIndex) { - runStart = beginIndex; - } - return runStart; - } - } - - public int getRunLimit() { - return currentRunLimit; - } - - public int getRunLimit(Attribute attribute) { - if (currentRunLimit == endIndex || currentRunIndex == -1) { - return currentRunLimit; - } else { - Object value = getAttribute(attribute); - int runLimit = currentRunLimit; - int runIndex = currentRunIndex; - while (runLimit < endIndex - && valuesMatch(value, AttributedString.this.getAttribute(attribute, runIndex + 1))) { - runIndex++; - runLimit = runIndex < runCount - 1 ? runStarts[runIndex + 1] : endIndex; - } - if (runLimit > endIndex) { - runLimit = endIndex; - } - return runLimit; - } - } - - public int getRunLimit(Set attributes) { - if (currentRunLimit == endIndex || currentRunIndex == -1) { - return currentRunLimit; - } else { - int runLimit = currentRunLimit; - int runIndex = currentRunIndex; - while (runLimit < endIndex - && AttributedString.this.attributeValuesMatch(attributes, currentRunIndex, runIndex + 1)) { - runIndex++; - runLimit = runIndex < runCount - 1 ? runStarts[runIndex + 1] : endIndex; - } - if (runLimit > endIndex) { - runLimit = endIndex; - } - return runLimit; - } - } - - public Map getAttributes() { - if (runAttributes == null || currentRunIndex == -1 || runAttributes[currentRunIndex] == null) { - // ??? would be nice to return null, but current spec doesn't allow it - // returning Hashtable saves AttributeMap from dealing with emptiness - return new Hashtable<>(); - } - return new AttributeMap(currentRunIndex, beginIndex, endIndex); - } - - public Set getAllAttributeKeys() { - // ??? This should screen out attribute keys that aren't relevant to the client - if (runAttributes == null) { - // ??? would be nice to return null, but current spec doesn't allow it - // returning HashSet saves us from dealing with emptiness - return new HashSet<>(); - } - synchronized (AttributedString.this) { - // ??? should try to create this only once, then update if necessary, - // and give callers read-only view - Set keys = new HashSet<>(); - int i = 0; - while (i < runCount) { - if (runStarts[i] < endIndex && (i == runCount - 1 || runStarts[i + 1] > beginIndex)) { - Vector currentRunAttributes = runAttributes[i]; - if (currentRunAttributes != null) { - int j = currentRunAttributes.size(); - while (j-- > 0) { - keys.add(currentRunAttributes.get(j)); - } - } - } - i++; - } - return keys; - } - } - - public Object getAttribute(Attribute attribute) { - int runIndex = currentRunIndex; - if (runIndex < 0) { - return null; - } - return AttributedString.this.getAttributeCheckRange(attribute, runIndex, beginIndex, endIndex); - } - - // internally used methods - - private AttributedString getString() { - return AttributedString.this; - } - - // set the current index, update information about the current run if necessary, - // return the character at the current index - private char internalSetIndex(int position) { - currentIndex = position; - if (position < currentRunStart || position >= currentRunLimit) { - updateRunInfo(); - } - if (currentIndex == endIndex) { - return DONE; - } else { - return charAt(position); - } - } - - // update the information about the current run - private void updateRunInfo() { - if (currentIndex == endIndex) { - currentRunStart = currentRunLimit = endIndex; - currentRunIndex = -1; - } else { - synchronized (AttributedString.this) { - int runIndex = -1; - while (runIndex < runCount - 1 && runStarts[runIndex + 1] <= currentIndex) - runIndex++; - currentRunIndex = runIndex; - if (runIndex >= 0) { - currentRunStart = runStarts[runIndex]; - if (currentRunStart < beginIndex) - currentRunStart = beginIndex; - } else { - currentRunStart = beginIndex; - } - if (runIndex < runCount - 1) { - currentRunLimit = runStarts[runIndex + 1]; - if (currentRunLimit > endIndex) - currentRunLimit = endIndex; - } else { - currentRunLimit = endIndex; - } - } - } - } - - } - - // the map class associated with this string class, giving access to the - // attributes of one run - - private final class AttributeMap extends AbstractMap { - - int runIndex; - int beginIndex; - int endIndex; - - AttributeMap(int runIndex, int beginIndex, int endIndex) { - this.runIndex = runIndex; - this.beginIndex = beginIndex; - this.endIndex = endIndex; - } - - public Set> entrySet() { - HashSet> set = new HashSet<>(); - synchronized (AttributedString.this) { - int size = runAttributes[runIndex].size(); - for (int i = 0; i < size; i++) { - Attribute key = runAttributes[runIndex].get(i); - Object value = runAttributeValues[runIndex].get(i); - if (value instanceof Annotation) { - value = AttributedString.this.getAttributeCheckRange(key, runIndex, beginIndex, endIndex); - if (value == null) { - continue; - } - } - - Map.Entry entry = new AttributeEntry(key, value); - set.add(entry); - } - } - return set; - } - - public Object get(Object key) { - return AttributedString.this.getAttributeCheckRange((Attribute) key, runIndex, beginIndex, endIndex); - } - } -} - -class AttributeEntry implements Map.Entry { - - private Attribute key; - private Object value; - - AttributeEntry(Attribute key, Object value) { - this.key = key; - this.value = value; - } - - public boolean equals(Object o) { - if (!(o instanceof AttributeEntry)) { - return false; - } - AttributeEntry other = (AttributeEntry) o; - return other.key.equals(key) && Objects.equals(other.value, value); - } - - public Attribute getKey() { - return key; - } - - public Object getValue() { - return value; - } - - public Object setValue(Object newValue) { - throw new UnsupportedOperationException(); - } - - public int hashCode() { - return key.hashCode() ^ (value == null ? 0 : value.hashCode()); - } - - public String toString() { - return key.toString() + "=" + value.toString(); - } -} +/* + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk_internal.bidi; + +import java.util.*; + +import jdk_internal.bidi.AttributedCharacterIterator.Attribute; + +/** + * An AttributedString holds text and related attribute information. It may be + * used as the actual data storage in some cases where a text reader wants to + * access attributed text through the AttributedCharacterIterator interface. + * + *

+ * An attribute is a key/value pair, identified by the key. No two attributes on + * a given character can have the same key. + * + *

+ * The values for an attribute are immutable, or must not be mutated by clients + * or storage. They are always passed by reference, and not cloned. + * + * @see AttributedCharacterIterator + * @see Annotation + * @since 1.2 + */ + +public class AttributedString { + // field holding the text + String text; + + // Fields holding run attribute information. + // Run attributes are organized by run. + // Arrays are always of equal lengths (the current capacity). + // Since there are no vectors of int, we have to use arrays. + private static final int INITIAL_CAPACITY = 10; + int runCount; // actual number of runs, <= current capacity + int[] runStarts; // start index for each run + Vector[] runAttributes; // vector of attribute keys for each run + Vector[] runAttributeValues; // parallel vector of attribute values for each run + + /** + * Constructs an AttributedString instance with the given + * AttributedCharacterIterators. + * + * @param iterators AttributedCharacterIterators to construct AttributedString + * from. + * @throws NullPointerException if iterators is null + */ + AttributedString(AttributedCharacterIterator[] iterators) { + if (iterators == null) { + throw new NullPointerException("Iterators must not be null"); + } + if (iterators.length == 0) { + text = ""; + } else { + // Build the String contents + StringBuffer buffer = new StringBuffer(); + for (int counter = 0; counter < iterators.length; counter++) { + appendContents(buffer, iterators[counter]); + } + + text = buffer.toString(); + + if (!text.isEmpty()) { + // Determine the runs, creating a new run when the attributes + // differ. + int offset = 0; + Map last = null; + + for (int counter = 0; counter < iterators.length; counter++) { + AttributedCharacterIterator iterator = iterators[counter]; + int start = iterator.getBeginIndex(); + int end = iterator.getEndIndex(); + int index = start; + + while (index < end) { + iterator.setIndex(index); + + Map attrs = iterator.getAttributes(); + + if (mapsDiffer(last, attrs)) { + setAttributes(attrs, index - start + offset); + } + last = attrs; + index = iterator.getRunLimit(); + } + offset += (end - start); + } + } + } + } + + /** + * Constructs an AttributedString instance with the given text. + * + * @param text The text for this attributed string. + * @throws NullPointerException if {@code text} is null. + */ + public AttributedString(String text) { + if (text == null) { + throw new NullPointerException(); + } + this.text = text; + } + + /** + * Constructs an AttributedString instance with the given text and attributes. + * + * @param text The text for this attributed string. + * @param attributes The attributes that apply to the entire string. + * @throws NullPointerException if {@code text} or {@code attributes} is + * null. + * @throws IllegalArgumentException if the text has length 0 and the attributes + * parameter is not an empty Map (attributes + * cannot be applied to a 0-length range). + */ + public AttributedString(String text, Map attributes) { + if (text == null || attributes == null) { + throw new NullPointerException(); + } + this.text = text; + + if (text.isEmpty()) { + if (attributes.isEmpty()) + return; + throw new IllegalArgumentException("Can't add attribute to 0-length text"); + } + + int attributeCount = attributes.size(); + if (attributeCount > 0) { + createRunAttributeDataVectors(); + Vector newRunAttributes = new Vector<>(attributeCount); + Vector newRunAttributeValues = new Vector<>(attributeCount); + runAttributes[0] = newRunAttributes; + runAttributeValues[0] = newRunAttributeValues; + + Iterator> iterator = attributes.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + newRunAttributes.addElement(entry.getKey()); + newRunAttributeValues.addElement(entry.getValue()); + } + } + } + + /** + * Constructs an AttributedString instance with the given attributed text + * represented by AttributedCharacterIterator. + * + * @param text The text for this attributed string. + * @throws NullPointerException if {@code text} is null. + */ + public AttributedString(AttributedCharacterIterator text) { + // If performance is critical, this constructor should be + // implemented here rather than invoking the constructor for a + // subrange. We can avoid some range checking in the loops. + this(text, text.getBeginIndex(), text.getEndIndex(), null); + } + + /** + * Constructs an AttributedString instance with the subrange of the given + * attributed text represented by AttributedCharacterIterator. If the given + * range produces an empty text, all attributes will be discarded. Note that any + * attributes wrapped by an Annotation object are discarded for a subrange of + * the original attribute range. + * + * @param text The text for this attributed string. + * @param beginIndex Index of the first character of the range. + * @param endIndex Index of the character following the last character of the + * range. + * @throws NullPointerException if {@code text} is null. + * @throws IllegalArgumentException if the subrange given by beginIndex and + * endIndex is out of the text range. + * @see java.text.Annotation + */ + public AttributedString(AttributedCharacterIterator text, int beginIndex, int endIndex) { + this(text, beginIndex, endIndex, null); + } + + /** + * Constructs an AttributedString instance with the subrange of the given + * attributed text represented by AttributedCharacterIterator. Only attributes + * that match the given attributes will be incorporated into the instance. If + * the given range produces an empty text, all attributes will be discarded. + * Note that any attributes wrapped by an Annotation object are discarded for a + * subrange of the original attribute range. + * + * @param text The text for this attributed string. + * @param beginIndex Index of the first character of the range. + * @param endIndex Index of the character following the last character of the + * range. + * @param attributes Specifies attributes to be extracted from the text. If null + * is specified, all available attributes will be used. + * @throws NullPointerException if {@code text} is null. + * @throws IllegalArgumentException if the subrange given by beginIndex and + * endIndex is out of the text range. + * @see java.text.Annotation + */ + public AttributedString(AttributedCharacterIterator text, int beginIndex, int endIndex, Attribute[] attributes) { + if (text == null) { + throw new NullPointerException(); + } + + // Validate the given subrange + int textBeginIndex = text.getBeginIndex(); + int textEndIndex = text.getEndIndex(); + if (beginIndex < textBeginIndex || endIndex > textEndIndex || beginIndex > endIndex) + throw new IllegalArgumentException("Invalid substring range"); + + // Copy the given string + StringBuilder textBuilder = new StringBuilder(); + text.setIndex(beginIndex); + for (char c = text.current(); text.getIndex() < endIndex; c = text.next()) + textBuilder.append(c); + this.text = textBuilder.toString(); + + if (beginIndex == endIndex) + return; + + // Select attribute keys to be taken care of + HashSet keys = new HashSet<>(); + if (attributes == null) { + keys.addAll(text.getAllAttributeKeys()); + } else { + for (int i = 0; i < attributes.length; i++) + keys.add(attributes[i]); + keys.retainAll(text.getAllAttributeKeys()); + } + if (keys.isEmpty()) + return; + + // Get and set attribute runs for each attribute name. Need to + // scan from the top of the text so that we can discard any + // Annotation that is no longer applied to a subset text segment. + Iterator itr = keys.iterator(); + while (itr.hasNext()) { + Attribute attributeKey = itr.next(); + text.setIndex(textBeginIndex); + while (text.getIndex() < endIndex) { + int start = text.getRunStart(attributeKey); + int limit = text.getRunLimit(attributeKey); + Object value = text.getAttribute(attributeKey); + + if (value != null) { + if (value instanceof Annotation) { + if (start >= beginIndex && limit <= endIndex) { + addAttribute(attributeKey, value, start - beginIndex, limit - beginIndex); + } else { + if (limit > endIndex) + break; + } + } else { + // if the run is beyond the given (subset) range, we + // don't need to process further. + if (start >= endIndex) + break; + if (limit > beginIndex) { + // attribute is applied to any subrange + if (start < beginIndex) + start = beginIndex; + if (limit > endIndex) + limit = endIndex; + if (start != limit) { + addAttribute(attributeKey, value, start - beginIndex, limit - beginIndex); + } + } + } + } + text.setIndex(limit); + } + } + } + + /** + * Adds an attribute to the entire string. + * + * @param attribute the attribute key + * @param value the value of the attribute; may be null + * @throws NullPointerException if {@code attribute} is null. + * @throws IllegalArgumentException if the AttributedString has length 0 + * (attributes cannot be applied to a 0-length + * range). + */ + public void addAttribute(Attribute attribute, Object value) { + + if (attribute == null) { + throw new NullPointerException(); + } + + int len = length(); + if (len == 0) { + throw new IllegalArgumentException("Can't add attribute to 0-length text"); + } + + addAttributeImpl(attribute, value, 0, len); + } + + /** + * Adds an attribute to a subrange of the string. + * + * @param attribute the attribute key + * @param value The value of the attribute. May be null. + * @param beginIndex Index of the first character of the range. + * @param endIndex Index of the character following the last character of the + * range. + * @throws NullPointerException if {@code attribute} is null. + * @throws IllegalArgumentException if beginIndex is less than 0, endIndex is + * greater than the length of the string, or + * beginIndex and endIndex together don't + * define a non-empty subrange of the string. + */ + public void addAttribute(Attribute attribute, Object value, int beginIndex, int endIndex) { + + if (attribute == null) { + throw new NullPointerException(); + } + + if (beginIndex < 0 || endIndex > length() || beginIndex >= endIndex) { + throw new IllegalArgumentException("Invalid substring range"); + } + + addAttributeImpl(attribute, value, beginIndex, endIndex); + } + + /** + * Adds a set of attributes to a subrange of the string. + * + * @param attributes The attributes to be added to the string. + * @param beginIndex Index of the first character of the range. + * @param endIndex Index of the character following the last character of the + * range. + * @throws NullPointerException if {@code attributes} is null. + * @throws IllegalArgumentException if beginIndex is less than 0, endIndex is + * greater than the length of the string, or + * beginIndex and endIndex together don't + * define a non-empty subrange of the string + * and the attributes parameter is not an empty + * Map. + */ + public void addAttributes(Map attributes, int beginIndex, int endIndex) { + if (attributes == null) { + throw new NullPointerException(); + } + + if (beginIndex < 0 || endIndex > length() || beginIndex > endIndex) { + throw new IllegalArgumentException("Invalid substring range"); + } + if (beginIndex == endIndex) { + if (attributes.isEmpty()) + return; + throw new IllegalArgumentException("Can't add attribute to 0-length text"); + } + + // make sure we have run attribute data vectors + if (runCount == 0) { + createRunAttributeDataVectors(); + } + + // break up runs if necessary + int beginRunIndex = ensureRunBreak(beginIndex); + int endRunIndex = ensureRunBreak(endIndex); + + Iterator> iterator = attributes.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + addAttributeRunData(entry.getKey(), entry.getValue(), beginRunIndex, endRunIndex); + } + } + + private synchronized void addAttributeImpl(Attribute attribute, Object value, int beginIndex, int endIndex) { + + // make sure we have run attribute data vectors + if (runCount == 0) { + createRunAttributeDataVectors(); + } + + // break up runs if necessary + int beginRunIndex = ensureRunBreak(beginIndex); + int endRunIndex = ensureRunBreak(endIndex); + + addAttributeRunData(attribute, value, beginRunIndex, endRunIndex); + } + + private final void createRunAttributeDataVectors() { + // use temporary variables so things remain consistent in case of an exception + int[] newRunStarts = new int[INITIAL_CAPACITY]; + + @SuppressWarnings("unchecked") + Vector[] newRunAttributes = (Vector[]) new Vector[INITIAL_CAPACITY]; + + @SuppressWarnings("unchecked") + Vector[] newRunAttributeValues = (Vector[]) new Vector[INITIAL_CAPACITY]; + + runStarts = newRunStarts; + runAttributes = newRunAttributes; + runAttributeValues = newRunAttributeValues; + runCount = 1; // assume initial run starting at index 0 + } + + // ensure there's a run break at offset, return the index of the run + private final int ensureRunBreak(int offset) { + return ensureRunBreak(offset, true); + } + + /** + * Ensures there is a run break at offset, returning the index of the run. If + * this results in splitting a run, two things can happen: + *
    + *
  • If copyAttrs is true, the attributes from the existing run will be placed + * in both of the newly created runs. + *
  • If copyAttrs is false, the attributes from the existing run will NOT be + * copied to the run to the right (>= offset) of the break, but will exist on + * the run to the left (< offset). + *
+ */ + private final int ensureRunBreak(int offset, boolean copyAttrs) { + if (offset == length()) { + return runCount; + } + + // search for the run index where this offset should be + int runIndex = 0; + while (runIndex < runCount && runStarts[runIndex] < offset) { + runIndex++; + } + + // if the offset is at a run start already, we're done + if (runIndex < runCount && runStarts[runIndex] == offset) { + return runIndex; + } + + // we'll have to break up a run + // first, make sure we have enough space in our arrays + int currentCapacity = runStarts.length; + if (runCount == currentCapacity) { + // We need to resize - we grow capacity by 25%. + int newCapacity = currentCapacity + (currentCapacity >> 2); + + // use temporary variables so things remain consistent in case of an exception + int[] newRunStarts = Arrays.copyOf(runStarts, newCapacity); + Vector[] newRunAttributes = Arrays.copyOf(runAttributes, newCapacity); + Vector[] newRunAttributeValues = Arrays.copyOf(runAttributeValues, newCapacity); + + runStarts = newRunStarts; + runAttributes = newRunAttributes; + runAttributeValues = newRunAttributeValues; + } + + // make copies of the attribute information of the old run that the new one used + // to be part of + // use temporary variables so things remain consistent in case of an exception + Vector newRunAttributes = null; + Vector newRunAttributeValues = null; + + if (copyAttrs) { + Vector oldRunAttributes = runAttributes[runIndex - 1]; + Vector oldRunAttributeValues = runAttributeValues[runIndex - 1]; + if (oldRunAttributes != null) { + newRunAttributes = new Vector<>(oldRunAttributes); + } + if (oldRunAttributeValues != null) { + newRunAttributeValues = new Vector<>(oldRunAttributeValues); + } + } + + // now actually break up the run + runCount++; + for (int i = runCount - 1; i > runIndex; i--) { + runStarts[i] = runStarts[i - 1]; + runAttributes[i] = runAttributes[i - 1]; + runAttributeValues[i] = runAttributeValues[i - 1]; + } + runStarts[runIndex] = offset; + runAttributes[runIndex] = newRunAttributes; + runAttributeValues[runIndex] = newRunAttributeValues; + + return runIndex; + } + + // add the attribute attribute/value to all runs where beginRunIndex <= runIndex + // < endRunIndex + private void addAttributeRunData(Attribute attribute, Object value, int beginRunIndex, int endRunIndex) { + + for (int i = beginRunIndex; i < endRunIndex; i++) { + int keyValueIndex = -1; // index of key and value in our vectors; assume we don't have an entry yet + if (runAttributes[i] == null) { + Vector newRunAttributes = new Vector<>(); + Vector newRunAttributeValues = new Vector<>(); + runAttributes[i] = newRunAttributes; + runAttributeValues[i] = newRunAttributeValues; + } else { + // check whether we have an entry already + keyValueIndex = runAttributes[i].indexOf(attribute); + } + + if (keyValueIndex == -1) { + // create new entry + int oldSize = runAttributes[i].size(); + runAttributes[i].addElement(attribute); + try { + runAttributeValues[i].addElement(value); + } catch (Exception e) { + runAttributes[i].setSize(oldSize); + runAttributeValues[i].setSize(oldSize); + } + } else { + // update existing entry + runAttributeValues[i].set(keyValueIndex, value); + } + } + } + + /** + * Creates an AttributedCharacterIterator instance that provides access to the + * entire contents of this string. + * + * @return An iterator providing access to the text and its attributes. + */ + public AttributedCharacterIterator getIterator() { + return getIterator(null, 0, length()); + } + + /** + * Creates an AttributedCharacterIterator instance that provides access to + * selected contents of this string. Information about attributes not listed in + * attributes that the implementor may have need not be made accessible through + * the iterator. If the list is null, all available attribute information should + * be made accessible. + * + * @param attributes a list of attributes that the client is interested in + * @return an iterator providing access to the entire text and its selected + * attributes + */ + public AttributedCharacterIterator getIterator(Attribute[] attributes) { + return getIterator(attributes, 0, length()); + } + + /** + * Creates an AttributedCharacterIterator instance that provides access to + * selected contents of this string. Information about attributes not listed in + * attributes that the implementor may have need not be made accessible through + * the iterator. If the list is null, all available attribute information should + * be made accessible. + * + * @param attributes a list of attributes that the client is interested in + * @param beginIndex the index of the first character + * @param endIndex the index of the character following the last character + * @return an iterator providing access to the text and its attributes + * @throws IllegalArgumentException if beginIndex is less than 0, endIndex is + * greater than the length of the string, or + * beginIndex is greater than endIndex. + */ + public AttributedCharacterIterator getIterator(Attribute[] attributes, int beginIndex, int endIndex) { + return new AttributedStringIterator(attributes, beginIndex, endIndex); + } + + // all (with the exception of length) reading operations are private, + // since AttributedString instances are accessed through iterators. + + // length is package private so that CharacterIteratorFieldDelegate can + // access it without creating an AttributedCharacterIterator. + int length() { + return text.length(); + } + + private char charAt(int index) { + return text.charAt(index); + } + + private synchronized Object getAttribute(Attribute attribute, int runIndex) { + Vector currentRunAttributes = runAttributes[runIndex]; + Vector currentRunAttributeValues = runAttributeValues[runIndex]; + if (currentRunAttributes == null) { + return null; + } + int attributeIndex = currentRunAttributes.indexOf(attribute); + if (attributeIndex != -1) { + return currentRunAttributeValues.elementAt(attributeIndex); + } else { + return null; + } + } + + // gets an attribute value, but returns an annotation only if it's range does + // not extend outside the range beginIndex..endIndex + private Object getAttributeCheckRange(Attribute attribute, int runIndex, int beginIndex, int endIndex) { + Object value = getAttribute(attribute, runIndex); + if (value instanceof Annotation) { + // need to check whether the annotation's range extends outside the iterator's + // range + if (beginIndex > 0) { + int currIndex = runIndex; + int runStart = runStarts[currIndex]; + while (runStart >= beginIndex && valuesMatch(value, getAttribute(attribute, currIndex - 1))) { + currIndex--; + runStart = runStarts[currIndex]; + } + if (runStart < beginIndex) { + // annotation's range starts before iterator's range + return null; + } + } + int textLength = length(); + if (endIndex < textLength) { + int currIndex = runIndex; + int runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1] : textLength; + while (runLimit <= endIndex && valuesMatch(value, getAttribute(attribute, currIndex + 1))) { + currIndex++; + runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1] : textLength; + } + if (runLimit > endIndex) { + // annotation's range ends after iterator's range + return null; + } + } + // annotation's range is subrange of iterator's range, + // so we can return the value + } + return value; + } + + // returns whether all specified attributes have equal values in the runs with + // the given indices + private boolean attributeValuesMatch(Set attributes, int runIndex1, int runIndex2) { + Iterator iterator = attributes.iterator(); + while (iterator.hasNext()) { + Attribute key = iterator.next(); + if (!valuesMatch(getAttribute(key, runIndex1), getAttribute(key, runIndex2))) { + return false; + } + } + return true; + } + + // returns whether the two objects are either both null or equal + private static final boolean valuesMatch(Object value1, Object value2) { + if (value1 == null) { + return value2 == null; + } else { + return value1.equals(value2); + } + } + + /** + * Appends the contents of the CharacterIterator iterator into the StringBuffer + * buf. + */ + private final void appendContents(StringBuffer buf, CharacterIterator iterator) { + int index = iterator.getBeginIndex(); + int end = iterator.getEndIndex(); + + while (index < end) { + iterator.setIndex(index++); + buf.append(iterator.current()); + } + } + + /** + * Sets the attributes for the range from offset to the next run break + * (typically the end of the text) to the ones specified in attrs. This is only + * meant to be called from the constructor! + */ + private void setAttributes(Map attrs, int offset) { + if (runCount == 0) { + createRunAttributeDataVectors(); + } + + int index = ensureRunBreak(offset, false); + int size; + + if (attrs != null && (size = attrs.size()) > 0) { + Vector runAttrs = new Vector<>(size); + Vector runValues = new Vector<>(size); + Iterator> iterator = attrs.entrySet().iterator(); + + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + + runAttrs.add(entry.getKey()); + runValues.add(entry.getValue()); + } + runAttributes[index] = runAttrs; + runAttributeValues[index] = runValues; + } + } + + /** + * Returns true if the attributes specified in last and attrs differ. + */ + private static boolean mapsDiffer(Map last, Map attrs) { + if (last == null) { + return (attrs != null && attrs.size() > 0); + } + return (!last.equals(attrs)); + } + + // the iterator class associated with this string class + + private final class AttributedStringIterator implements AttributedCharacterIterator { + + // note on synchronization: + // we don't synchronize on the iterator, assuming that an iterator is only used + // in one thread. + // we do synchronize access to the AttributedString however, since it's more + // likely to be shared between threads. + + // start and end index for our iteration + private int beginIndex; + private int endIndex; + + // attributes that our client is interested in + private Attribute[] relevantAttributes; + + // the current index for our iteration + // invariant: beginIndex <= currentIndex <= endIndex + private int currentIndex; + + // information about the run that includes currentIndex + private int currentRunIndex; + private int currentRunStart; + private int currentRunLimit; + + // constructor + AttributedStringIterator(Attribute[] attributes, int beginIndex, int endIndex) { + + if (beginIndex < 0 || beginIndex > endIndex || endIndex > length()) { + throw new IllegalArgumentException("Invalid substring range"); + } + + this.beginIndex = beginIndex; + this.endIndex = endIndex; + this.currentIndex = beginIndex; + updateRunInfo(); + if (attributes != null) { + relevantAttributes = attributes.clone(); + } + } + + // Object methods. See documentation in that class. + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof AttributedStringIterator)) { + return false; + } + + AttributedStringIterator that = (AttributedStringIterator) obj; + + if (AttributedString.this != that.getString()) + return false; + if (currentIndex != that.currentIndex || beginIndex != that.beginIndex || endIndex != that.endIndex) + return false; + return true; + } + + public int hashCode() { + return text.hashCode() ^ currentIndex ^ beginIndex ^ endIndex; + } + + public Object clone() { + try { + AttributedStringIterator other = (AttributedStringIterator) super.clone(); + return other; + } catch (CloneNotSupportedException e) { + throw new InternalError(e); + } + } + + // CharacterIterator methods. See documentation in that interface. + + public char first() { + return internalSetIndex(beginIndex); + } + + public char last() { + if (endIndex == beginIndex) { + return internalSetIndex(endIndex); + } else { + return internalSetIndex(endIndex - 1); + } + } + + public char current() { + if (currentIndex == endIndex) { + return DONE; + } else { + return charAt(currentIndex); + } + } + + public char next() { + if (currentIndex < endIndex) { + return internalSetIndex(currentIndex + 1); + } else { + return DONE; + } + } + + public char previous() { + if (currentIndex > beginIndex) { + return internalSetIndex(currentIndex - 1); + } else { + return DONE; + } + } + + public char setIndex(int position) { + if (position < beginIndex || position > endIndex) + throw new IllegalArgumentException("Invalid index"); + return internalSetIndex(position); + } + + public int getBeginIndex() { + return beginIndex; + } + + public int getEndIndex() { + return endIndex; + } + + public int getIndex() { + return currentIndex; + } + + // AttributedCharacterIterator methods. See documentation in that interface. + + public int getRunStart() { + return currentRunStart; + } + + public int getRunStart(Attribute attribute) { + if (currentRunStart == beginIndex || currentRunIndex == -1) { + return currentRunStart; + } else { + Object value = getAttribute(attribute); + int runStart = currentRunStart; + int runIndex = currentRunIndex; + while (runStart > beginIndex + && valuesMatch(value, AttributedString.this.getAttribute(attribute, runIndex - 1))) { + runIndex--; + runStart = runStarts[runIndex]; + } + if (runStart < beginIndex) { + runStart = beginIndex; + } + return runStart; + } + } + + public int getRunStart(Set attributes) { + if (currentRunStart == beginIndex || currentRunIndex == -1) { + return currentRunStart; + } else { + int runStart = currentRunStart; + int runIndex = currentRunIndex; + while (runStart > beginIndex + && AttributedString.this.attributeValuesMatch(attributes, currentRunIndex, runIndex - 1)) { + runIndex--; + runStart = runStarts[runIndex]; + } + if (runStart < beginIndex) { + runStart = beginIndex; + } + return runStart; + } + } + + public int getRunLimit() { + return currentRunLimit; + } + + public int getRunLimit(Attribute attribute) { + if (currentRunLimit == endIndex || currentRunIndex == -1) { + return currentRunLimit; + } else { + Object value = getAttribute(attribute); + int runLimit = currentRunLimit; + int runIndex = currentRunIndex; + while (runLimit < endIndex + && valuesMatch(value, AttributedString.this.getAttribute(attribute, runIndex + 1))) { + runIndex++; + runLimit = runIndex < runCount - 1 ? runStarts[runIndex + 1] : endIndex; + } + if (runLimit > endIndex) { + runLimit = endIndex; + } + return runLimit; + } + } + + public int getRunLimit(Set attributes) { + if (currentRunLimit == endIndex || currentRunIndex == -1) { + return currentRunLimit; + } else { + int runLimit = currentRunLimit; + int runIndex = currentRunIndex; + while (runLimit < endIndex + && AttributedString.this.attributeValuesMatch(attributes, currentRunIndex, runIndex + 1)) { + runIndex++; + runLimit = runIndex < runCount - 1 ? runStarts[runIndex + 1] : endIndex; + } + if (runLimit > endIndex) { + runLimit = endIndex; + } + return runLimit; + } + } + + public Map getAttributes() { + if (runAttributes == null || currentRunIndex == -1 || runAttributes[currentRunIndex] == null) { + // ??? would be nice to return null, but current spec doesn't allow it + // returning Hashtable saves AttributeMap from dealing with emptiness + return new Hashtable<>(); + } + return new AttributeMap(currentRunIndex, beginIndex, endIndex); + } + + public Set getAllAttributeKeys() { + // ??? This should screen out attribute keys that aren't relevant to the client + if (runAttributes == null) { + // ??? would be nice to return null, but current spec doesn't allow it + // returning HashSet saves us from dealing with emptiness + return new HashSet<>(); + } + synchronized (AttributedString.this) { + // ??? should try to create this only once, then update if necessary, + // and give callers read-only view + Set keys = new HashSet<>(); + int i = 0; + while (i < runCount) { + if (runStarts[i] < endIndex && (i == runCount - 1 || runStarts[i + 1] > beginIndex)) { + Vector currentRunAttributes = runAttributes[i]; + if (currentRunAttributes != null) { + int j = currentRunAttributes.size(); + while (j-- > 0) { + keys.add(currentRunAttributes.get(j)); + } + } + } + i++; + } + return keys; + } + } + + public Object getAttribute(Attribute attribute) { + int runIndex = currentRunIndex; + if (runIndex < 0) { + return null; + } + return AttributedString.this.getAttributeCheckRange(attribute, runIndex, beginIndex, endIndex); + } + + // internally used methods + + private AttributedString getString() { + return AttributedString.this; + } + + // set the current index, update information about the current run if necessary, + // return the character at the current index + private char internalSetIndex(int position) { + currentIndex = position; + if (position < currentRunStart || position >= currentRunLimit) { + updateRunInfo(); + } + if (currentIndex == endIndex) { + return DONE; + } else { + return charAt(position); + } + } + + // update the information about the current run + private void updateRunInfo() { + if (currentIndex == endIndex) { + currentRunStart = currentRunLimit = endIndex; + currentRunIndex = -1; + } else { + synchronized (AttributedString.this) { + int runIndex = -1; + while (runIndex < runCount - 1 && runStarts[runIndex + 1] <= currentIndex) + runIndex++; + currentRunIndex = runIndex; + if (runIndex >= 0) { + currentRunStart = runStarts[runIndex]; + if (currentRunStart < beginIndex) + currentRunStart = beginIndex; + } else { + currentRunStart = beginIndex; + } + if (runIndex < runCount - 1) { + currentRunLimit = runStarts[runIndex + 1]; + if (currentRunLimit > endIndex) + currentRunLimit = endIndex; + } else { + currentRunLimit = endIndex; + } + } + } + } + + } + + // the map class associated with this string class, giving access to the + // attributes of one run + + private final class AttributeMap extends AbstractMap { + + int runIndex; + int beginIndex; + int endIndex; + + AttributeMap(int runIndex, int beginIndex, int endIndex) { + this.runIndex = runIndex; + this.beginIndex = beginIndex; + this.endIndex = endIndex; + } + + public Set> entrySet() { + HashSet> set = new HashSet<>(); + synchronized (AttributedString.this) { + int size = runAttributes[runIndex].size(); + for (int i = 0; i < size; i++) { + Attribute key = runAttributes[runIndex].get(i); + Object value = runAttributeValues[runIndex].get(i); + if (value instanceof Annotation) { + value = AttributedString.this.getAttributeCheckRange(key, runIndex, beginIndex, endIndex); + if (value == null) { + continue; + } + } + + Map.Entry entry = new AttributeEntry(key, value); + set.add(entry); + } + } + return set; + } + + public Object get(Object key) { + return AttributedString.this.getAttributeCheckRange((Attribute) key, runIndex, beginIndex, endIndex); + } + } +} + +class AttributeEntry implements Map.Entry { + + private Attribute key; + private Object value; + + AttributeEntry(Attribute key, Object value) { + this.key = key; + this.value = value; + } + + public boolean equals(Object o) { + if (!(o instanceof AttributeEntry)) { + return false; + } + AttributeEntry other = (AttributeEntry) o; + return other.key.equals(key) && Objects.equals(other.value, value); + } + + public Attribute getKey() { + return key; + } + + public Object getValue() { + return value; + } + + public Object setValue(Object newValue) { + throw new UnsupportedOperationException(); + } + + public int hashCode() { + return key.hashCode() ^ (value == null ? 0 : value.hashCode()); + } + + public String toString() { + return key.toString() + "=" + value.toString(); + } +} diff --git a/src/main/java/jdk_internal/bidi/Bidi.java b/src/main/java/jdk_internal/bidi/Bidi.java index 5ea9ee48..1ad568fb 100755 --- a/src/main/java/jdk_internal/bidi/Bidi.java +++ b/src/main/java/jdk_internal/bidi/Bidi.java @@ -1,362 +1,362 @@ -/* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * (C) Copyright IBM Corp. 1999-2003 - All Rights Reserved - * - * The original version of this source code and documentation is - * copyrighted and owned by IBM. These materials are provided - * under terms of a License Agreement between IBM and Sun. - * This technology is protected by multiple US and International - * patents. This notice and attribution to IBM may not be removed. - */ - -package jdk_internal.bidi; - -import jdk_internal.icu.text.BidiBase; - -/** - * This class implements the Unicode Bidirectional Algorithm. - *

- * A Bidi object provides information on the bidirectional reordering of the - * text used to create it. This is required, for example, to properly display - * Arabic or Hebrew text. These languages are inherently mixed directional, as - * they order numbers from left-to-right while ordering most other text from - * right-to-left. - *

- * Once created, a Bidi object can be queried to see if the text it represents - * is all left-to-right or all right-to-left. Such objects are very lightweight - * and this text is relatively easy to process. - *

- * If there are multiple runs of text, information about the runs can be - * accessed by indexing to get the start, limit, and level of a run. The level - * represents both the direction and the 'nesting level' of a directional run. - * Odd levels are right-to-left, while even levels are left-to-right. So for - * example level 0 represents left-to-right text, while level 1 represents - * right-to-left text, and level 2 represents left-to-right text embedded in a - * right-to-left run. - * - * @since 1.4 - */ -public final class Bidi { - - /** Constant indicating base direction is left-to-right. */ - public static final int DIRECTION_LEFT_TO_RIGHT = 0; - - /** Constant indicating base direction is right-to-left. */ - public static final int DIRECTION_RIGHT_TO_LEFT = 1; - - /** - * 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, the base direction - * is left-to-right. - */ - public static final int DIRECTION_DEFAULT_LEFT_TO_RIGHT = -2; - - /** - * 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, the base direction - * is right-to-left. - */ - public static final int DIRECTION_DEFAULT_RIGHT_TO_LEFT = -1; - - private BidiBase bidiBase; - - /** - * Create Bidi from the given paragraph of text and base direction. - * - * @param paragraph a paragraph of text - * @param flags a collection of flags that control the algorithm. The - * algorithm understands the flags DIRECTION_LEFT_TO_RIGHT, - * DIRECTION_RIGHT_TO_LEFT, DIRECTION_DEFAULT_LEFT_TO_RIGHT, - * and DIRECTION_DEFAULT_RIGHT_TO_LEFT. Other values are - * reserved. - */ - public Bidi(String paragraph, int flags) { - if (paragraph == null) { - throw new IllegalArgumentException("paragraph is null"); - } - - bidiBase = new BidiBase(paragraph.toCharArray(), 0, null, 0, paragraph.length(), flags); - } - - /** - * Create Bidi from the given paragraph of text. - *

- * The RUN_DIRECTION attribute in the text, if present, determines the base - * direction (left-to-right or right-to-left). If not present, the base - * direction is computes using the Unicode Bidirectional Algorithm, defaulting - * to left-to-right if there are no strong directional characters in the text. - * This attribute, if present, must be applied to all the text in the paragraph. - *

- * The BIDI_EMBEDDING attribute in the text, if present, represents embedding - * level information. Negative values from -1 to -62 indicate overrides at the - * absolute value of the level. Positive values from 1 to 62 indicate - * embeddings. Where values are zero or not defined, the base embedding level as - * determined by the base direction is assumed. - *

- * The NUMERIC_SHAPING attribute in the text, if present, converts European - * digits to other decimal digits before running the bidi algorithm. This - * attribute, if present, must be applied to all the text in the paragraph. - * - * @param paragraph a paragraph of text with optional character and paragraph - * attribute information - * - * @see java.awt.font.TextAttribute#BIDI_EMBEDDING - * @see java.awt.font.TextAttribute#NUMERIC_SHAPING - * @see java.awt.font.TextAttribute#RUN_DIRECTION - */ - public Bidi(AttributedCharacterIterator paragraph) { - if (paragraph == null) { - throw new IllegalArgumentException("paragraph is null"); - } - - bidiBase = new BidiBase(0, 0); - bidiBase.setPara(paragraph); - } - - /** - * Create Bidi from the given text, embedding, and direction information. The - * embeddings array may be null. If present, the values represent embedding - * level information. Negative values from -1 to -61 indicate overrides at the - * absolute value of the level. Positive values from 1 to 61 indicate - * embeddings. Where values are zero, the base embedding level as determined by - * the base direction is assumed. - * - * @param text an array containing the paragraph of text to process. - * @param textStart the index into the text array of the start of the - * paragraph. - * @param embeddings an array containing embedding values for each - * character in the paragraph. This can be null, in which - * case it is assumed that there is no external embedding - * information. - * @param embStart the index into the embedding array of the start of the - * paragraph. - * @param paragraphLength the length of the paragraph in the text and embeddings - * arrays. - * @param flags a collection of flags that control the algorithm. The - * algorithm understands the flags - * DIRECTION_LEFT_TO_RIGHT, DIRECTION_RIGHT_TO_LEFT, - * DIRECTION_DEFAULT_LEFT_TO_RIGHT, and - * DIRECTION_DEFAULT_RIGHT_TO_LEFT. Other values are - * reserved. - */ - public Bidi(char[] text, int textStart, byte[] embeddings, int embStart, int paragraphLength, int flags) { - if (text == null) { - throw new IllegalArgumentException("text is null"); - } - if (paragraphLength < 0) { - throw new IllegalArgumentException("bad length: " + paragraphLength); - } - if (textStart < 0 || paragraphLength > text.length - textStart) { - throw new IllegalArgumentException( - "bad range: " + textStart + " length: " + paragraphLength + " for text of length: " + text.length); - } - if (embeddings != null && (embStart < 0 || paragraphLength > embeddings.length - embStart)) { - throw new IllegalArgumentException("bad range: " + embStart + " length: " + paragraphLength - + " for embeddings of length: " + text.length); - } - - bidiBase = new BidiBase(text, textStart, embeddings, embStart, paragraphLength, flags); - } - - /** - * Create a Bidi object representing the bidi information on a line of text - * within the paragraph represented by the current Bidi. This call is not - * required if the entire paragraph fits on one line. - * - * @param lineStart the offset from the start of the paragraph to the start of - * the line. - * @param lineLimit the offset from the start of the paragraph to the limit of - * the line. - * @return a {@code Bidi} object - */ - public Bidi createLineBidi(int lineStart, int lineLimit) { - AttributedString astr = new AttributedString(""); - Bidi newBidi = new Bidi(astr.getIterator()); - - return bidiBase.setLine(this, bidiBase, newBidi, newBidi.bidiBase, lineStart, lineLimit); - } - - /** - * Return true if the line is not left-to-right or right-to-left. This means it - * either has mixed runs of left-to-right and right-to-left text, or the base - * direction differs from the direction of the only run of text. - * - * @return true if the line is not left-to-right or right-to-left. - */ - public boolean isMixed() { - return bidiBase.isMixed(); - } - - /** - * Return true if the line is all left-to-right text and the base direction is - * left-to-right. - * - * @return true if the line is all left-to-right text and the base direction is - * left-to-right - */ - public boolean isLeftToRight() { - return bidiBase.isLeftToRight(); - } - - /** - * Return true if the line is all right-to-left text, and the base direction is - * right-to-left. - * - * @return true if the line is all right-to-left text, and the base direction is - * right-to-left - */ - public boolean isRightToLeft() { - return bidiBase.isRightToLeft(); - } - - /** - * Return the length of text in the line. - * - * @return the length of text in the line - */ - public int getLength() { - return bidiBase.getLength(); - } - - /** - * Return true if the base direction is left-to-right. - * - * @return true if the base direction is left-to-right - */ - public boolean baseIsLeftToRight() { - return bidiBase.baseIsLeftToRight(); - } - - /** - * Return the base level (0 if left-to-right, 1 if right-to-left). - * - * @return the base level - */ - public int getBaseLevel() { - return bidiBase.getParaLevel(); - } - - /** - * Return the resolved level of the character at offset. If offset is - * {@literal <} 0 or ≥ the length of the line, return the base direction - * level. - * - * @param offset the index of the character for which to return the level - * @return the resolved level of the character at offset - */ - public int getLevelAt(int offset) { - return bidiBase.getLevelAt(offset); - } - - /** - * Return the number of level runs. - * - * @return the number of level runs - */ - public int getRunCount() { - return bidiBase.countRuns(); - } - - /** - * Return the level of the nth logical run in this line. - * - * @param run the index of the run, between 0 and {@code getRunCount()} - * @return the level of the run - */ - public int getRunLevel(int run) { - return bidiBase.getRunLevel(run); - } - - /** - * Return the index of the character at the start of the nth logical run in this - * line, as an offset from the start of the line. - * - * @param run the index of the run, between 0 and {@code getRunCount()} - * @return the start of the run - */ - public int getRunStart(int run) { - return bidiBase.getRunStart(run); - } - - /** - * Return the index of the character past the end of the nth logical run in this - * line, as an offset from the start of the line. For example, this will return - * the length of the line for the last run on the line. - * - * @param run the index of the run, between 0 and {@code getRunCount()} - * @return limit the limit of the run - */ - public int getRunLimit(int run) { - return bidiBase.getRunLimit(run); - } - - /** - * Return true if the specified text requires bidi analysis. If this returns - * false, the text will display left-to-right. Clients can then avoid - * constructing a Bidi object. Text in the Arabic Presentation Forms area of - * Unicode is presumed to already be shaped and ordered for display, and so will - * not cause this function to return true. - * - * @param text the text containing the characters to test - * @param start the start of the range of characters to test - * @param limit the limit of the range of characters to test - * @return true if the range of characters requires bidi analysis - */ - public static boolean requiresBidi(char[] text, int start, int limit) { - return BidiBase.requiresBidi(text, start, limit); - } - - /** - * Reorder the objects in the array into visual order based on their levels. - * This is a utility function to use when you have a collection of objects - * representing runs of text in logical order, each run containing text at a - * single level. The elements at {@code index} from {@code objectStart} up to - * {@code objectStart + count} in the objects array will be reordered into - * visual order assuming each run of text has the level indicated by the - * corresponding element in the levels array (at - * {@code index - objectStart + levelStart}). - * - * @param levels an array representing the bidi level of each object - * @param levelStart the start position in the levels array - * @param objects the array of objects to be reordered into visual order - * @param objectStart the start position in the objects array - * @param count the number of objects to reorder - */ - public static void reorderVisually(byte[] levels, int levelStart, Object[] objects, int objectStart, int count) { - BidiBase.reorderVisually(levels, levelStart, objects, objectStart, count); - } - - /** - * Display the bidi internal state, used in debugging. - */ - public String toString() { - return bidiBase.toString(); - } - -} +/* + * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * (C) Copyright IBM Corp. 1999-2003 - All Rights Reserved + * + * The original version of this source code and documentation is + * copyrighted and owned by IBM. These materials are provided + * under terms of a License Agreement between IBM and Sun. + * This technology is protected by multiple US and International + * patents. This notice and attribution to IBM may not be removed. + */ + +package jdk_internal.bidi; + +import jdk_internal.icu.text.BidiBase; + +/** + * This class implements the Unicode Bidirectional Algorithm. + *

+ * A Bidi object provides information on the bidirectional reordering of the + * text used to create it. This is required, for example, to properly display + * Arabic or Hebrew text. These languages are inherently mixed directional, as + * they order numbers from left-to-right while ordering most other text from + * right-to-left. + *

+ * Once created, a Bidi object can be queried to see if the text it represents + * is all left-to-right or all right-to-left. Such objects are very lightweight + * and this text is relatively easy to process. + *

+ * If there are multiple runs of text, information about the runs can be + * accessed by indexing to get the start, limit, and level of a run. The level + * represents both the direction and the 'nesting level' of a directional run. + * Odd levels are right-to-left, while even levels are left-to-right. So for + * example level 0 represents left-to-right text, while level 1 represents + * right-to-left text, and level 2 represents left-to-right text embedded in a + * right-to-left run. + * + * @since 1.4 + */ +public final class Bidi { + + /** Constant indicating base direction is left-to-right. */ + public static final int DIRECTION_LEFT_TO_RIGHT = 0; + + /** Constant indicating base direction is right-to-left. */ + public static final int DIRECTION_RIGHT_TO_LEFT = 1; + + /** + * 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, the base direction + * is left-to-right. + */ + public static final int DIRECTION_DEFAULT_LEFT_TO_RIGHT = -2; + + /** + * 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, the base direction + * is right-to-left. + */ + public static final int DIRECTION_DEFAULT_RIGHT_TO_LEFT = -1; + + private BidiBase bidiBase; + + /** + * Create Bidi from the given paragraph of text and base direction. + * + * @param paragraph a paragraph of text + * @param flags a collection of flags that control the algorithm. The + * algorithm understands the flags DIRECTION_LEFT_TO_RIGHT, + * DIRECTION_RIGHT_TO_LEFT, DIRECTION_DEFAULT_LEFT_TO_RIGHT, + * and DIRECTION_DEFAULT_RIGHT_TO_LEFT. Other values are + * reserved. + */ + public Bidi(String paragraph, int flags) { + if (paragraph == null) { + throw new IllegalArgumentException("paragraph is null"); + } + + bidiBase = new BidiBase(paragraph.toCharArray(), 0, null, 0, paragraph.length(), flags); + } + + /** + * Create Bidi from the given paragraph of text. + *

+ * The RUN_DIRECTION attribute in the text, if present, determines the base + * direction (left-to-right or right-to-left). If not present, the base + * direction is computes using the Unicode Bidirectional Algorithm, defaulting + * to left-to-right if there are no strong directional characters in the text. + * This attribute, if present, must be applied to all the text in the paragraph. + *

+ * The BIDI_EMBEDDING attribute in the text, if present, represents embedding + * level information. Negative values from -1 to -62 indicate overrides at the + * absolute value of the level. Positive values from 1 to 62 indicate + * embeddings. Where values are zero or not defined, the base embedding level as + * determined by the base direction is assumed. + *

+ * The NUMERIC_SHAPING attribute in the text, if present, converts European + * digits to other decimal digits before running the bidi algorithm. This + * attribute, if present, must be applied to all the text in the paragraph. + * + * @param paragraph a paragraph of text with optional character and paragraph + * attribute information + * + * @see java.awt.font.TextAttribute#BIDI_EMBEDDING + * @see java.awt.font.TextAttribute#NUMERIC_SHAPING + * @see java.awt.font.TextAttribute#RUN_DIRECTION + */ + public Bidi(AttributedCharacterIterator paragraph) { + if (paragraph == null) { + throw new IllegalArgumentException("paragraph is null"); + } + + bidiBase = new BidiBase(0, 0); + bidiBase.setPara(paragraph); + } + + /** + * Create Bidi from the given text, embedding, and direction information. The + * embeddings array may be null. If present, the values represent embedding + * level information. Negative values from -1 to -61 indicate overrides at the + * absolute value of the level. Positive values from 1 to 61 indicate + * embeddings. Where values are zero, the base embedding level as determined by + * the base direction is assumed. + * + * @param text an array containing the paragraph of text to process. + * @param textStart the index into the text array of the start of the + * paragraph. + * @param embeddings an array containing embedding values for each + * character in the paragraph. This can be null, in which + * case it is assumed that there is no external embedding + * information. + * @param embStart the index into the embedding array of the start of the + * paragraph. + * @param paragraphLength the length of the paragraph in the text and embeddings + * arrays. + * @param flags a collection of flags that control the algorithm. The + * algorithm understands the flags + * DIRECTION_LEFT_TO_RIGHT, DIRECTION_RIGHT_TO_LEFT, + * DIRECTION_DEFAULT_LEFT_TO_RIGHT, and + * DIRECTION_DEFAULT_RIGHT_TO_LEFT. Other values are + * reserved. + */ + public Bidi(char[] text, int textStart, byte[] embeddings, int embStart, int paragraphLength, int flags) { + if (text == null) { + throw new IllegalArgumentException("text is null"); + } + if (paragraphLength < 0) { + throw new IllegalArgumentException("bad length: " + paragraphLength); + } + if (textStart < 0 || paragraphLength > text.length - textStart) { + throw new IllegalArgumentException( + "bad range: " + textStart + " length: " + paragraphLength + " for text of length: " + text.length); + } + if (embeddings != null && (embStart < 0 || paragraphLength > embeddings.length - embStart)) { + throw new IllegalArgumentException("bad range: " + embStart + " length: " + paragraphLength + + " for embeddings of length: " + text.length); + } + + bidiBase = new BidiBase(text, textStart, embeddings, embStart, paragraphLength, flags); + } + + /** + * Create a Bidi object representing the bidi information on a line of text + * within the paragraph represented by the current Bidi. This call is not + * required if the entire paragraph fits on one line. + * + * @param lineStart the offset from the start of the paragraph to the start of + * the line. + * @param lineLimit the offset from the start of the paragraph to the limit of + * the line. + * @return a {@code Bidi} object + */ + public Bidi createLineBidi(int lineStart, int lineLimit) { + AttributedString astr = new AttributedString(""); + Bidi newBidi = new Bidi(astr.getIterator()); + + return bidiBase.setLine(this, bidiBase, newBidi, newBidi.bidiBase, lineStart, lineLimit); + } + + /** + * Return true if the line is not left-to-right or right-to-left. This means it + * either has mixed runs of left-to-right and right-to-left text, or the base + * direction differs from the direction of the only run of text. + * + * @return true if the line is not left-to-right or right-to-left. + */ + public boolean isMixed() { + return bidiBase.isMixed(); + } + + /** + * Return true if the line is all left-to-right text and the base direction is + * left-to-right. + * + * @return true if the line is all left-to-right text and the base direction is + * left-to-right + */ + public boolean isLeftToRight() { + return bidiBase.isLeftToRight(); + } + + /** + * Return true if the line is all right-to-left text, and the base direction is + * right-to-left. + * + * @return true if the line is all right-to-left text, and the base direction is + * right-to-left + */ + public boolean isRightToLeft() { + return bidiBase.isRightToLeft(); + } + + /** + * Return the length of text in the line. + * + * @return the length of text in the line + */ + public int getLength() { + return bidiBase.getLength(); + } + + /** + * Return true if the base direction is left-to-right. + * + * @return true if the base direction is left-to-right + */ + public boolean baseIsLeftToRight() { + return bidiBase.baseIsLeftToRight(); + } + + /** + * Return the base level (0 if left-to-right, 1 if right-to-left). + * + * @return the base level + */ + public int getBaseLevel() { + return bidiBase.getParaLevel(); + } + + /** + * Return the resolved level of the character at offset. If offset is + * {@literal <} 0 or ≥ the length of the line, return the base direction + * level. + * + * @param offset the index of the character for which to return the level + * @return the resolved level of the character at offset + */ + public int getLevelAt(int offset) { + return bidiBase.getLevelAt(offset); + } + + /** + * Return the number of level runs. + * + * @return the number of level runs + */ + public int getRunCount() { + return bidiBase.countRuns(); + } + + /** + * Return the level of the nth logical run in this line. + * + * @param run the index of the run, between 0 and {@code getRunCount()} + * @return the level of the run + */ + public int getRunLevel(int run) { + return bidiBase.getRunLevel(run); + } + + /** + * Return the index of the character at the start of the nth logical run in this + * line, as an offset from the start of the line. + * + * @param run the index of the run, between 0 and {@code getRunCount()} + * @return the start of the run + */ + public int getRunStart(int run) { + return bidiBase.getRunStart(run); + } + + /** + * Return the index of the character past the end of the nth logical run in this + * line, as an offset from the start of the line. For example, this will return + * the length of the line for the last run on the line. + * + * @param run the index of the run, between 0 and {@code getRunCount()} + * @return limit the limit of the run + */ + public int getRunLimit(int run) { + return bidiBase.getRunLimit(run); + } + + /** + * Return true if the specified text requires bidi analysis. If this returns + * false, the text will display left-to-right. Clients can then avoid + * constructing a Bidi object. Text in the Arabic Presentation Forms area of + * Unicode is presumed to already be shaped and ordered for display, and so will + * not cause this function to return true. + * + * @param text the text containing the characters to test + * @param start the start of the range of characters to test + * @param limit the limit of the range of characters to test + * @return true if the range of characters requires bidi analysis + */ + public static boolean requiresBidi(char[] text, int start, int limit) { + return BidiBase.requiresBidi(text, start, limit); + } + + /** + * Reorder the objects in the array into visual order based on their levels. + * This is a utility function to use when you have a collection of objects + * representing runs of text in logical order, each run containing text at a + * single level. The elements at {@code index} from {@code objectStart} up to + * {@code objectStart + count} in the objects array will be reordered into + * visual order assuming each run of text has the level indicated by the + * corresponding element in the levels array (at + * {@code index - objectStart + levelStart}). + * + * @param levels an array representing the bidi level of each object + * @param levelStart the start position in the levels array + * @param objects the array of objects to be reordered into visual order + * @param objectStart the start position in the objects array + * @param count the number of objects to reorder + */ + public static void reorderVisually(byte[] levels, int levelStart, Object[] objects, int objectStart, int count) { + BidiBase.reorderVisually(levels, levelStart, objects, objectStart, count); + } + + /** + * Display the bidi internal state, used in debugging. + */ + public String toString() { + return bidiBase.toString(); + } + +} diff --git a/src/main/java/jdk_internal/bidi/CharacterIterator.java b/src/main/java/jdk_internal/bidi/CharacterIterator.java index 68312844..b30cf7f2 100755 --- a/src/main/java/jdk_internal/bidi/CharacterIterator.java +++ b/src/main/java/jdk_internal/bidi/CharacterIterator.java @@ -1,203 +1,203 @@ -/* - * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved - * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved - * - * The original version of this source code and documentation - * is copyrighted and owned by Taligent, Inc., a wholly-owned - * subsidiary of IBM. These materials are provided under terms - * of a License Agreement between Taligent and Sun. This technology - * is protected by multiple US and International patents. - * - * This notice and attribution to Taligent may not be removed. - * Taligent is a registered trademark of Taligent, Inc. - * - */ - -package jdk_internal.bidi; - -/** - * This interface defines a protocol for bidirectional iteration over text. The - * iterator iterates over a bounded sequence of characters. Characters are - * indexed with values beginning with the value returned by getBeginIndex() and - * continuing through the value returned by getEndIndex()-1. - *

- * Iterators maintain a current character index, whose valid range is from - * getBeginIndex() to getEndIndex(); the value getEndIndex() is included to - * allow handling of zero-length text ranges and for historical reasons. The - * current index can be retrieved by calling getIndex() and set directly by - * calling setIndex(), first(), and last(). - *

- * The methods previous() and next() are used for iteration. They return DONE if - * they would move outside the range from getBeginIndex() to getEndIndex() -1, - * signaling that the iterator has reached the end of the sequence. DONE is also - * returned by other methods to indicate that the current index is outside this - * range. - * - *

- * Examples: - *

- * - * Traverse the text from start to finish - * - *

{@code
- * public void traverseForward(CharacterIterator iter) {
- * 	for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
- * 		processChar(c);
- * 	}
- * }
- * }
- * - * Traverse the text backwards, from end to start - * - *
{@code
- * public void traverseBackward(CharacterIterator iter) {
- * 	for (char c = iter.last(); c != CharacterIterator.DONE; c = iter.previous()) {
- * 		processChar(c);
- * 	}
- * }
- * }
- * - * Traverse both forward and backward from a given position in the text. Calls - * to notBoundary() in this example represents some additional stopping - * criteria. - * - *
{@code
- * public void traverseOut(CharacterIterator iter, int pos) {
- * 	for (char c = iter.setIndex(pos); c != CharacterIterator.DONE && notBoundary(c); c = iter.next()) {
- * 	}
- * 	int end = iter.getIndex();
- * 	for (char c = iter.setIndex(pos); c != CharacterIterator.DONE && notBoundary(c); c = iter.previous()) {
- * 	}
- * 	int start = iter.getIndex();
- * 	processSection(start, end);
- * }
- * }
- * - * @since 1.1 - * @see StringCharacterIterator - * @see AttributedCharacterIterator - */ - -public interface CharacterIterator extends Cloneable { - - /** - * Constant that is returned when the iterator has reached either the end or the - * beginning of the text. The value is '\\uFFFF', the "not a character" value - * which should not occur in any valid Unicode string. - */ - public static final char DONE = '\uFFFF'; - - /** - * Sets the position to getBeginIndex() and returns the character at that - * position. - * - * @return the first character in the text, or DONE if the text is empty - * @see #getBeginIndex() - */ - public char first(); - - /** - * Sets the position to getEndIndex()-1 (getEndIndex() if the text is empty) and - * returns the character at that position. - * - * @return the last character in the text, or DONE if the text is empty - * @see #getEndIndex() - */ - public char last(); - - /** - * Gets the character at the current position (as returned by getIndex()). - * - * @return the character at the current position or DONE if the current position - * is off the end of the text. - * @see #getIndex() - */ - public char current(); - - /** - * Increments the iterator's index by one and returns the character at the new - * index. If the resulting index is greater or equal to getEndIndex(), the - * current index is reset to getEndIndex() and a value of DONE is returned. - * - * @return the character at the new position or DONE if the new position is off - * the end of the text range. - */ - public char next(); - - /** - * Decrements the iterator's index by one and returns the character at the new - * index. If the current index is getBeginIndex(), the index remains at - * getBeginIndex() and a value of DONE is returned. - * - * @return the character at the new position or DONE if the current position is - * equal to getBeginIndex(). - */ - public char previous(); - - /** - * Sets the position to the specified position in the text and returns that - * character. - * - * @param position the position within the text. Valid values range from - * getBeginIndex() to getEndIndex(). An IllegalArgumentException - * is thrown if an invalid value is supplied. - * @return the character at the specified position or DONE if the specified - * position is equal to getEndIndex() - */ - public char setIndex(int position); - - /** - * Returns the start index of the text. - * - * @return the index at which the text begins. - */ - public int getBeginIndex(); - - /** - * Returns the end index of the text. This index is the index of the first - * character following the end of the text. - * - * @return the index after the last character in the text - */ - public int getEndIndex(); - - /** - * Returns the current index. - * - * @return the current index. - */ - public int getIndex(); - - /** - * Create a copy of this iterator - * - * @return A copy of this - */ - public Object clone(); - -} +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved + * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved + * + * The original version of this source code and documentation + * is copyrighted and owned by Taligent, Inc., a wholly-owned + * subsidiary of IBM. These materials are provided under terms + * of a License Agreement between Taligent and Sun. This technology + * is protected by multiple US and International patents. + * + * This notice and attribution to Taligent may not be removed. + * Taligent is a registered trademark of Taligent, Inc. + * + */ + +package jdk_internal.bidi; + +/** + * This interface defines a protocol for bidirectional iteration over text. The + * iterator iterates over a bounded sequence of characters. Characters are + * indexed with values beginning with the value returned by getBeginIndex() and + * continuing through the value returned by getEndIndex()-1. + *

+ * Iterators maintain a current character index, whose valid range is from + * getBeginIndex() to getEndIndex(); the value getEndIndex() is included to + * allow handling of zero-length text ranges and for historical reasons. The + * current index can be retrieved by calling getIndex() and set directly by + * calling setIndex(), first(), and last(). + *

+ * The methods previous() and next() are used for iteration. They return DONE if + * they would move outside the range from getBeginIndex() to getEndIndex() -1, + * signaling that the iterator has reached the end of the sequence. DONE is also + * returned by other methods to indicate that the current index is outside this + * range. + * + *

+ * Examples: + *

+ * + * Traverse the text from start to finish + * + *

{@code
+ * public void traverseForward(CharacterIterator iter) {
+ * 	for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
+ * 		processChar(c);
+ * 	}
+ * }
+ * }
+ * + * Traverse the text backwards, from end to start + * + *
{@code
+ * public void traverseBackward(CharacterIterator iter) {
+ * 	for (char c = iter.last(); c != CharacterIterator.DONE; c = iter.previous()) {
+ * 		processChar(c);
+ * 	}
+ * }
+ * }
+ * + * Traverse both forward and backward from a given position in the text. Calls + * to notBoundary() in this example represents some additional stopping + * criteria. + * + *
{@code
+ * public void traverseOut(CharacterIterator iter, int pos) {
+ * 	for (char c = iter.setIndex(pos); c != CharacterIterator.DONE && notBoundary(c); c = iter.next()) {
+ * 	}
+ * 	int end = iter.getIndex();
+ * 	for (char c = iter.setIndex(pos); c != CharacterIterator.DONE && notBoundary(c); c = iter.previous()) {
+ * 	}
+ * 	int start = iter.getIndex();
+ * 	processSection(start, end);
+ * }
+ * }
+ * + * @since 1.1 + * @see StringCharacterIterator + * @see AttributedCharacterIterator + */ + +public interface CharacterIterator extends Cloneable { + + /** + * Constant that is returned when the iterator has reached either the end or the + * beginning of the text. The value is '\\uFFFF', the "not a character" value + * which should not occur in any valid Unicode string. + */ + public static final char DONE = '\uFFFF'; + + /** + * Sets the position to getBeginIndex() and returns the character at that + * position. + * + * @return the first character in the text, or DONE if the text is empty + * @see #getBeginIndex() + */ + public char first(); + + /** + * Sets the position to getEndIndex()-1 (getEndIndex() if the text is empty) and + * returns the character at that position. + * + * @return the last character in the text, or DONE if the text is empty + * @see #getEndIndex() + */ + public char last(); + + /** + * Gets the character at the current position (as returned by getIndex()). + * + * @return the character at the current position or DONE if the current position + * is off the end of the text. + * @see #getIndex() + */ + public char current(); + + /** + * Increments the iterator's index by one and returns the character at the new + * index. If the resulting index is greater or equal to getEndIndex(), the + * current index is reset to getEndIndex() and a value of DONE is returned. + * + * @return the character at the new position or DONE if the new position is off + * the end of the text range. + */ + public char next(); + + /** + * Decrements the iterator's index by one and returns the character at the new + * index. If the current index is getBeginIndex(), the index remains at + * getBeginIndex() and a value of DONE is returned. + * + * @return the character at the new position or DONE if the current position is + * equal to getBeginIndex(). + */ + public char previous(); + + /** + * Sets the position to the specified position in the text and returns that + * character. + * + * @param position the position within the text. Valid values range from + * getBeginIndex() to getEndIndex(). An IllegalArgumentException + * is thrown if an invalid value is supplied. + * @return the character at the specified position or DONE if the specified + * position is equal to getEndIndex() + */ + public char setIndex(int position); + + /** + * Returns the start index of the text. + * + * @return the index at which the text begins. + */ + public int getBeginIndex(); + + /** + * Returns the end index of the text. This index is the index of the first + * character following the end of the text. + * + * @return the index after the last character in the text + */ + public int getEndIndex(); + + /** + * Returns the current index. + * + * @return the current index. + */ + public int getIndex(); + + /** + * Create a copy of this iterator + * + * @return A copy of this + */ + public Object clone(); + +} diff --git a/src/main/java/jdk_internal/bidi/Normalizer.java b/src/main/java/jdk_internal/bidi/Normalizer.java index ce51a813..7f8328e7 100755 --- a/src/main/java/jdk_internal/bidi/Normalizer.java +++ b/src/main/java/jdk_internal/bidi/Normalizer.java @@ -1,178 +1,178 @@ -/* - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ******************************************************************************* - * (C) Copyright IBM Corp. 1996-2005 - All Rights Reserved * - * * - * The original version of this source code and documentation is copyrighted * - * and owned by IBM, These materials are provided under terms of a License * - * Agreement between IBM and Sun. This technology is protected by multiple * - * US and International patents. This notice and attribution to IBM may not * - * to removed. * - ******************************************************************************* - */ - -package jdk_internal.bidi; - -import jdk_internal.icu.text.NormalizerBase; - -/** - * This class provides the method {@code normalize} which transforms Unicode - * text into an equivalent composed or decomposed form, allowing for easier - * sorting and searching of text. The {@code normalize} method 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): - * - *

- *      U+00C1    LATIN CAPITAL LETTER A WITH ACUTE
- * 
- * - * or as two separate characters (the "decomposed" form): - * - *
- *      U+0041    LATIN CAPITAL LETTER A
- *      U+0301    COMBINING ACUTE ACCENT
- * 
- * - * 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 as equivalent. 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: - * - *

- *      U+0066    LATIN SMALL LETTER F
- *      U+0066    LATIN SMALL LETTER F
- *      U+0069    LATIN SMALL LETTER I
- * 
- * - * or as the single character - * - *
- *      U+FB03    LATIN SMALL LIGATURE FFI
- * 
- * - * 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. - *

- * The {@code normalize} method 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, the {@code normalize} method rearranges accents - * into the proper canonical order, so that you do not have to worry about - * accent rearrangement on your own. - *

- * 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. - * - * @since 1.6 - */ -public final class Normalizer { - - private Normalizer() { - }; - - /** - * This enum provides constants of the four Unicode normalization forms that are - * described in Unicode - * Standard Annex #15 — Unicode Normalization Forms and two methods to - * access them. - * - * @since 1.6 - */ - public static enum Form { - - /** - * Canonical decomposition. - */ - NFD, - - /** - * Canonical decomposition, followed by canonical composition. - */ - NFC, - - /** - * Compatibility decomposition. - */ - NFKD, - - /** - * Compatibility decomposition, followed by canonical composition. - */ - NFKC - } - - /** - * Normalize a sequence of char values. The sequence will be normalized - * according to the specified normalization form. - * - * @param src The sequence of char values to normalize. - * @param form The normalization form; one of - * {@link java.text.Normalizer.Form#NFC}, - * {@link java.text.Normalizer.Form#NFD}, - * {@link java.text.Normalizer.Form#NFKC}, - * {@link java.text.Normalizer.Form#NFKD} - * @return The normalized String - * @throws NullPointerException If {@code src} or {@code form} is null. - */ - public static String normalize(CharSequence src, Form form) { - return NormalizerBase.normalize(src.toString(), form); - } - - /** - * Determines if the given sequence of char values is normalized. - * - * @param src The sequence of char values to be checked. - * @param form The normalization form; one of - * {@link java.text.Normalizer.Form#NFC}, - * {@link java.text.Normalizer.Form#NFD}, - * {@link java.text.Normalizer.Form#NFKC}, - * {@link java.text.Normalizer.Form#NFKD} - * @return true if the sequence of char values is normalized; false otherwise. - * @throws NullPointerException If {@code src} or {@code form} is null. - */ - public static boolean isNormalized(CharSequence src, Form form) { - return NormalizerBase.isNormalized(src.toString(), form); - } -} +/* + * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ******************************************************************************* + * (C) Copyright IBM Corp. 1996-2005 - All Rights Reserved * + * * + * The original version of this source code and documentation is copyrighted * + * and owned by IBM, These materials are provided under terms of a License * + * Agreement between IBM and Sun. This technology is protected by multiple * + * US and International patents. This notice and attribution to IBM may not * + * to removed. * + ******************************************************************************* + */ + +package jdk_internal.bidi; + +import jdk_internal.icu.text.NormalizerBase; + +/** + * This class provides the method {@code normalize} which transforms Unicode + * text into an equivalent composed or decomposed form, allowing for easier + * sorting and searching of text. The {@code normalize} method 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): + * + *

+ *      U+00C1    LATIN CAPITAL LETTER A WITH ACUTE
+ * 
+ * + * or as two separate characters (the "decomposed" form): + * + *
+ *      U+0041    LATIN CAPITAL LETTER A
+ *      U+0301    COMBINING ACUTE ACCENT
+ * 
+ * + * 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 as equivalent. 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: + * + *

+ *      U+0066    LATIN SMALL LETTER F
+ *      U+0066    LATIN SMALL LETTER F
+ *      U+0069    LATIN SMALL LETTER I
+ * 
+ * + * or as the single character + * + *
+ *      U+FB03    LATIN SMALL LIGATURE FFI
+ * 
+ * + * 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. + *

+ * The {@code normalize} method 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, the {@code normalize} method rearranges accents + * into the proper canonical order, so that you do not have to worry about + * accent rearrangement on your own. + *

+ * 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. + * + * @since 1.6 + */ +public final class Normalizer { + + private Normalizer() { + }; + + /** + * This enum provides constants of the four Unicode normalization forms that are + * described in Unicode + * Standard Annex #15 — Unicode Normalization Forms and two methods to + * access them. + * + * @since 1.6 + */ + public static enum Form { + + /** + * Canonical decomposition. + */ + NFD, + + /** + * Canonical decomposition, followed by canonical composition. + */ + NFC, + + /** + * Compatibility decomposition. + */ + NFKD, + + /** + * Compatibility decomposition, followed by canonical composition. + */ + NFKC + } + + /** + * Normalize a sequence of char values. The sequence will be normalized + * according to the specified normalization form. + * + * @param src The sequence of char values to normalize. + * @param form The normalization form; one of + * {@link java.text.Normalizer.Form#NFC}, + * {@link java.text.Normalizer.Form#NFD}, + * {@link java.text.Normalizer.Form#NFKC}, + * {@link java.text.Normalizer.Form#NFKD} + * @return The normalized String + * @throws NullPointerException If {@code src} or {@code form} is null. + */ + public static String normalize(CharSequence src, Form form) { + return NormalizerBase.normalize(src.toString(), form); + } + + /** + * Determines if the given sequence of char values is normalized. + * + * @param src The sequence of char values to be checked. + * @param form The normalization form; one of + * {@link java.text.Normalizer.Form#NFC}, + * {@link java.text.Normalizer.Form#NFD}, + * {@link java.text.Normalizer.Form#NFKC}, + * {@link java.text.Normalizer.Form#NFKD} + * @return true if the sequence of char values is normalized; false otherwise. + * @throws NullPointerException If {@code src} or {@code form} is null. + */ + public static boolean isNormalized(CharSequence src, Form form) { + return NormalizerBase.isNormalized(src.toString(), form); + } +} diff --git a/src/main/java/jdk_internal/bidi/NumericShaper.java b/src/main/java/jdk_internal/bidi/NumericShaper.java index a3f5f803..1a97bb68 100755 --- a/src/main/java/jdk_internal/bidi/NumericShaper.java +++ b/src/main/java/jdk_internal/bidi/NumericShaper.java @@ -1,1351 +1,1351 @@ -/* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk_internal.bidi; - -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.util.Arrays; -import java.util.Comparator; -import java.util.EnumSet; -import java.util.Set; - -/** - * The {@code NumericShaper} class is used to convert Latin-1 (European) digits - * to other Unicode decimal digits. Users of this class will primarily be people - * who wish to present data using national digit shapes, but find it more - * convenient to represent the data internally using Latin-1 (European) digits. - * This does not interpret the deprecated numeric shape selector character - * (U+206E). - *

- * Instances of {@code NumericShaper} are typically applied as attributes to - * text with the {@link TextAttribute#NUMERIC_SHAPING NUMERIC_SHAPING} attribute - * of the {@code TextAttribute} class. For example, this code snippet causes a - * {@code TextLayout} to shape European digits to Arabic in an Arabic - * context:
- *

- * - *
- * Map map = new HashMap();
- * map.put(TextAttribute.NUMERIC_SHAPING,
- *     NumericShaper.getContextualShaper(NumericShaper.ARABIC));
- * FontRenderContext frc = ...;
- * TextLayout layout = new TextLayout(text, map, frc);
- * layout.draw(g2d, x, y);
- * 
- * - *

- * It is also possible to perform numeric shaping explicitly using instances of - * {@code NumericShaper}, as this code snippet demonstrates:
- *
- * - *
- * char[] text = ...;
- * // shape all EUROPEAN digits (except zero) to ARABIC digits
- * NumericShaper shaper = NumericShaper.getShaper(NumericShaper.ARABIC);
- * shaper.shape(text, start, count);
- *
- * // shape European digits to ARABIC digits if preceding text is Arabic, or
- * // shape European digits to TAMIL digits if preceding text is Tamil, or
- * // leave European digits alone if there is no preceding text, or
- * // preceding text is neither Arabic nor Tamil
- * NumericShaper shaper =
- *     NumericShaper.getContextualShaper(NumericShaper.ARABIC |
- *                                         NumericShaper.TAMIL,
- *                                       NumericShaper.EUROPEAN);
- * shaper.shape(text, start, count);
- * 
- * - *
- * - *

- * Bit mask- and enum-based Unicode ranges - *

- * - *

- * This class supports two different programming interfaces to represent Unicode - * ranges for script-specific digits: bit mask-based ones, such as - * {@link #ARABIC NumericShaper.ARABIC}, and enum-based ones, such as - * {@link NumericShaper.Range#ARABIC}. Multiple ranges can be specified by ORing - * bit mask-based constants, such as:

- * - *
- * NumericShaper.ARABIC | NumericShaper.TAMIL
- * 
- * - *
or creating a {@code Set} with the {@link NumericShaper.Range} - * constants, such as:
- * - *
- * EnumSet.of(NumericShaper.Range.ARABIC, NumericShaper.Range.TAMIL)
- * 
- * - *
The enum-based ranges are a super set of the bit mask-based - * ones. - * - *

- * If the two interfaces are mixed (including serialization), Unicode range - * values are mapped to their counterparts where such mapping is possible, such - * as {@code NumericShaper.Range.ARABIC} from/to {@code NumericShaper.ARABIC}. - * If any unmappable range values are specified, such as - * {@code NumericShaper.Range.BALINESE}, those ranges are ignored. - * - *

- * Decimal Digits Precedence - *

- * - *

- * A Unicode range may have more than one set of decimal digits. If multiple - * decimal digits sets are specified for the same Unicode range, one of the sets - * will take precedence as follows. - * - * - * - * - * - * - * - * - * - * - * - *
NumericShaper constants precedence
Unicode Range - * {@code NumericShaper} Constants - * Precedence
Arabic - * {@link NumericShaper#ARABIC NumericShaper.ARABIC}
- * {@link NumericShaper#EASTERN_ARABIC NumericShaper.EASTERN_ARABIC} - *
{@link NumericShaper#EASTERN_ARABIC NumericShaper.EASTERN_ARABIC} - *
{@link NumericShaper.Range#ARABIC}
- * {@link NumericShaper.Range#EASTERN_ARABIC} - *
{@link NumericShaper.Range#EASTERN_ARABIC}
Tai Tham - * {@link NumericShaper.Range#TAI_THAM_HORA}
- * {@link NumericShaper.Range#TAI_THAM_THAM} - *
{@link NumericShaper.Range#TAI_THAM_THAM}
- * - * @since 1.4 - */ -public final class NumericShaper implements java.io.Serializable { - - /** - * A {@code NumericShaper.Range} represents a Unicode range of a script having - * its own decimal digits. For example, the {@link NumericShaper.Range#THAI} - * range has the Thai digits, THAI DIGIT ZERO (U+0E50) to THAI DIGIT NINE - * (U+0E59). - * - *

- * The {@code Range} enum replaces the traditional bit mask-based values (e.g., - * {@link NumericShaper#ARABIC}), and supports more Unicode ranges than the bit - * mask-based ones. For example, the following code using the bit mask: - *

- * - *
-	 * NumericShaper.getContextualShaper(NumericShaper.ARABIC | NumericShaper.TAMIL, NumericShaper.EUROPEAN);
-	 * 
- * - *
can be written using this enum as:
- * - *
-	 * NumericShaper.getContextualShaper(EnumSet.of(NumericShaper.Range.ARABIC, NumericShaper.Range.TAMIL),
-	 * 		NumericShaper.Range.EUROPEAN);
-	 * 
- * - *
- * - * @since 1.7 - */ - public static enum Range { - // The order of EUROPEAN to MOGOLIAN must be consistent - // with the bitmask-based constants. - /** - * The Latin (European) range with the Latin (ASCII) digits. - */ - EUROPEAN('\u0030', '\u0000', '\u0300'), - /** - * The Arabic range with the Arabic-Indic digits. - */ - ARABIC('\u0660', '\u0600', '\u0780'), - /** - * The Arabic range with the Eastern Arabic-Indic digits. - */ - EASTERN_ARABIC('\u06f0', '\u0600', '\u0780'), - /** - * The Devanagari range with the Devanagari digits. - */ - DEVANAGARI('\u0966', '\u0900', '\u0980'), - /** - * The Bengali range with the Bengali digits. - */ - BENGALI('\u09e6', '\u0980', '\u0a00'), - /** - * The Gurmukhi range with the Gurmukhi digits. - */ - GURMUKHI('\u0a66', '\u0a00', '\u0a80'), - /** - * The Gujarati range with the Gujarati digits. - */ - GUJARATI('\u0ae6', '\u0b00', '\u0b80'), - /** - * The Oriya range with the Oriya digits. - */ - ORIYA('\u0b66', '\u0b00', '\u0b80'), - /** - * The Tamil range with the Tamil digits. - */ - TAMIL('\u0be6', '\u0b80', '\u0c00'), - /** - * The Telugu range with the Telugu digits. - */ - TELUGU('\u0c66', '\u0c00', '\u0c80'), - /** - * The Kannada range with the Kannada digits. - */ - KANNADA('\u0ce6', '\u0c80', '\u0d00'), - /** - * The Malayalam range with the Malayalam digits. - */ - MALAYALAM('\u0d66', '\u0d00', '\u0d80'), - /** - * The Thai range with the Thai digits. - */ - THAI('\u0e50', '\u0e00', '\u0e80'), - /** - * The Lao range with the Lao digits. - */ - LAO('\u0ed0', '\u0e80', '\u0f00'), - /** - * The Tibetan range with the Tibetan digits. - */ - TIBETAN('\u0f20', '\u0f00', '\u1000'), - /** - * The Myanmar range with the Myanmar digits. - */ - MYANMAR('\u1040', '\u1000', '\u1080'), - /** - * The Ethiopic range with the Ethiopic digits. Ethiopic does not have a decimal - * digit 0 so Latin (European) 0 is used. - */ - ETHIOPIC('\u1369', '\u1200', '\u1380') { - @Override - char getNumericBase() { - return 1; - } - }, - /** - * The Khmer range with the Khmer digits. - */ - KHMER('\u17e0', '\u1780', '\u1800'), - /** - * The Mongolian range with the Mongolian digits. - */ - MONGOLIAN('\u1810', '\u1800', '\u1900'), - // The order of EUROPEAN to MOGOLIAN must be consistent - // with the bitmask-based constants. - - /** - * The N'Ko range with the N'Ko digits. - */ - NKO('\u07c0', '\u07c0', '\u0800'), - /** - * The Myanmar range with the Myanmar Shan digits. - */ - MYANMAR_SHAN('\u1090', '\u1000', '\u10a0'), - /** - * The Limbu range with the Limbu digits. - */ - LIMBU('\u1946', '\u1900', '\u1950'), - /** - * The New Tai Lue range with the New Tai Lue digits. - */ - NEW_TAI_LUE('\u19d0', '\u1980', '\u19e0'), - /** - * The Balinese range with the Balinese digits. - */ - BALINESE('\u1b50', '\u1b00', '\u1b80'), - /** - * The Sundanese range with the Sundanese digits. - */ - SUNDANESE('\u1bb0', '\u1b80', '\u1bc0'), - /** - * The Lepcha range with the Lepcha digits. - */ - LEPCHA('\u1c40', '\u1c00', '\u1c50'), - /** - * The Ol Chiki range with the Ol Chiki digits. - */ - OL_CHIKI('\u1c50', '\u1c50', '\u1c80'), - /** - * The Vai range with the Vai digits. - */ - VAI('\ua620', '\ua500', '\ua640'), - /** - * The Saurashtra range with the Saurashtra digits. - */ - SAURASHTRA('\ua8d0', '\ua880', '\ua8e0'), - /** - * The Kayah Li range with the Kayah Li digits. - */ - KAYAH_LI('\ua900', '\ua900', '\ua930'), - /** - * The Cham range with the Cham digits. - */ - CHAM('\uaa50', '\uaa00', '\uaa60'), - /** - * The Tai Tham Hora range with the Tai Tham Hora digits. - */ - TAI_THAM_HORA('\u1a80', '\u1a20', '\u1ab0'), - /** - * The Tai Tham Tham range with the Tai Tham Tham digits. - */ - TAI_THAM_THAM('\u1a90', '\u1a20', '\u1ab0'), - /** - * The Javanese range with the Javanese digits. - */ - JAVANESE('\ua9d0', '\ua980', '\ua9e0'), - /** - * The Meetei Mayek range with the Meetei Mayek digits. - */ - MEETEI_MAYEK('\uabf0', '\uabc0', '\uac00'), - /** - * The Sinhala range with the Sinhala digits. - * - * @since 9 - */ - SINHALA('\u0de6', '\u0d80', '\u0e00'), - /** - * The Myanmar Extended-B range with the Myanmar Tai Laing digits. - * - * @since 9 - */ - MYANMAR_TAI_LAING('\ua9f0', '\ua9e0', '\uaa00'); - - private static int toRangeIndex(Range script) { - int index = script.ordinal(); - return index < NUM_KEYS ? index : -1; - } - - private static Range indexToRange(int index) { - return index < NUM_KEYS ? Range.values()[index] : null; - } - - private static int toRangeMask(Set ranges) { - int m = 0; - for (Range range : ranges) { - int index = range.ordinal(); - if (index < NUM_KEYS) { - m |= 1 << index; - } - } - return m; - } - - private static Set maskToRangeSet(int mask) { - Set set = EnumSet.noneOf(Range.class); - Range[] a = Range.values(); - for (int i = 0; i < NUM_KEYS; i++) { - if ((mask & (1 << i)) != 0) { - set.add(a[i]); - } - } - return set; - } - - // base character of range digits - private final int base; - // Unicode range - private final int start, // inclusive - end; // exclusive - - private Range(int base, int start, int end) { - this.base = base - ('0' + getNumericBase()); - this.start = start; - this.end = end; - } - - private int getDigitBase() { - return base; - } - - char getNumericBase() { - return 0; - } - - private boolean inRange(int c) { - return start <= c && c < end; - } - } - - /** index of context for contextual shaping - values range from 0 to 18 */ - private int key; - - /** - * flag indicating whether to shape contextually (high bit) and which digit - * ranges to shape (bits 0-18) - */ - private int mask; - - /** - * The context {@code Range} for contextual shaping or the {@code - * Range} for non-contextual shaping. {@code null} for the bit mask-based API. - * - * @since 1.7 - */ - private Range shapingRange; - - /** - * {@code Set} indicating which Unicode ranges to shape. {@code null} for - * the bit mask-based API. - */ - private transient Set rangeSet; - - /** - * rangeSet.toArray() value. Sorted by Range.base when the number of elements is - * greater than BSEARCH_THRESHOLD. - */ - private transient Range[] rangeArray; - - /** - * If more than BSEARCH_THRESHOLD ranges are specified, binary search is used. - */ - private static final int BSEARCH_THRESHOLD = 3; - - /** - * Use serialVersionUID from JDK 1.7 for interoperability. - */ - private static final long serialVersionUID = -8022764705923730308L; - - /** - * Identifies the Latin-1 (European) and extended range, and Latin-1 (European) - * decimal base. - */ - public static final int EUROPEAN = 1 << 0; - - /** Identifies the ARABIC range and decimal base. */ - public static final int ARABIC = 1 << 1; - - /** Identifies the ARABIC range and ARABIC_EXTENDED decimal base. */ - public static final int EASTERN_ARABIC = 1 << 2; - - /** Identifies the DEVANAGARI range and decimal base. */ - public static final int DEVANAGARI = 1 << 3; - - /** Identifies the BENGALI range and decimal base. */ - public static final int BENGALI = 1 << 4; - - /** Identifies the GURMUKHI range and decimal base. */ - public static final int GURMUKHI = 1 << 5; - - /** Identifies the GUJARATI range and decimal base. */ - public static final int GUJARATI = 1 << 6; - - /** Identifies the ORIYA range and decimal base. */ - public static final int ORIYA = 1 << 7; - - /** Identifies the TAMIL range and decimal base. */ - // TAMIL DIGIT ZERO was added in Unicode 4.1 - public static final int TAMIL = 1 << 8; - - /** Identifies the TELUGU range and decimal base. */ - public static final int TELUGU = 1 << 9; - - /** Identifies the KANNADA range and decimal base. */ - public static final int KANNADA = 1 << 10; - - /** Identifies the MALAYALAM range and decimal base. */ - public static final int MALAYALAM = 1 << 11; - - /** Identifies the THAI range and decimal base. */ - public static final int THAI = 1 << 12; - - /** Identifies the LAO range and decimal base. */ - public static final int LAO = 1 << 13; - - /** Identifies the TIBETAN range and decimal base. */ - public static final int TIBETAN = 1 << 14; - - /** Identifies the MYANMAR range and decimal base. */ - public static final int MYANMAR = 1 << 15; - - /** Identifies the ETHIOPIC range and decimal base. */ - public static final int ETHIOPIC = 1 << 16; - - /** Identifies the KHMER range and decimal base. */ - public static final int KHMER = 1 << 17; - - /** Identifies the MONGOLIAN range and decimal base. */ - public static final int MONGOLIAN = 1 << 18; - - /** - * Identifies all ranges, for full contextual shaping. - * - *

- * This constant specifies all of the bit mask-based ranges. Use - * {@code EnumSet.allOf(NumericShaper.Range.class)} to specify all of the - * enum-based ranges. - */ - public static final int ALL_RANGES = 0x0007ffff; - - private static final int EUROPEAN_KEY = 0; - private static final int ARABIC_KEY = 1; - private static final int EASTERN_ARABIC_KEY = 2; - private static final int DEVANAGARI_KEY = 3; - private static final int BENGALI_KEY = 4; - private static final int GURMUKHI_KEY = 5; - private static final int GUJARATI_KEY = 6; - private static final int ORIYA_KEY = 7; - private static final int TAMIL_KEY = 8; - private static final int TELUGU_KEY = 9; - private static final int KANNADA_KEY = 10; - private static final int MALAYALAM_KEY = 11; - private static final int THAI_KEY = 12; - private static final int LAO_KEY = 13; - private static final int TIBETAN_KEY = 14; - private static final int MYANMAR_KEY = 15; - private static final int ETHIOPIC_KEY = 16; - private static final int KHMER_KEY = 17; - private static final int MONGOLIAN_KEY = 18; - - private static final int NUM_KEYS = MONGOLIAN_KEY + 1; // fixed - - private static final int CONTEXTUAL_MASK = 1 << 31; - - private static final char[] bases = { '\u0030' - '\u0030', // EUROPEAN - '\u0660' - '\u0030', // ARABIC-INDIC - '\u06f0' - '\u0030', // EXTENDED ARABIC-INDIC (EASTERN_ARABIC) - '\u0966' - '\u0030', // DEVANAGARI - '\u09e6' - '\u0030', // BENGALI - '\u0a66' - '\u0030', // GURMUKHI - '\u0ae6' - '\u0030', // GUJARATI - '\u0b66' - '\u0030', // ORIYA - '\u0be6' - '\u0030', // TAMIL - zero was added in Unicode 4.1 - '\u0c66' - '\u0030', // TELUGU - '\u0ce6' - '\u0030', // KANNADA - '\u0d66' - '\u0030', // MALAYALAM - '\u0e50' - '\u0030', // THAI - '\u0ed0' - '\u0030', // LAO - '\u0f20' - '\u0030', // TIBETAN - '\u1040' - '\u0030', // MYANMAR - '\u1369' - '\u0031', // ETHIOPIC - no zero - '\u17e0' - '\u0030', // KHMER - '\u1810' - '\u0030', // MONGOLIAN - }; - - // some ranges adjoin or overlap, rethink if we want to do a binary search on - // this - - private static final char[] contexts = { '\u0000', '\u0300', // 'EUROPEAN' (really latin-1 and extended) - '\u0600', '\u0780', // ARABIC - '\u0600', '\u0780', // EASTERN_ARABIC -- note overlap with arabic - '\u0900', '\u0980', // DEVANAGARI - '\u0980', '\u0a00', // BENGALI - '\u0a00', '\u0a80', // GURMUKHI - '\u0a80', '\u0b00', // GUJARATI - '\u0b00', '\u0b80', // ORIYA - '\u0b80', '\u0c00', // TAMIL - '\u0c00', '\u0c80', // TELUGU - '\u0c80', '\u0d00', // KANNADA - '\u0d00', '\u0d80', // MALAYALAM - '\u0e00', '\u0e80', // THAI - '\u0e80', '\u0f00', // LAO - '\u0f00', '\u1000', // TIBETAN - '\u1000', '\u1080', // MYANMAR - '\u1200', '\u1380', // ETHIOPIC - note missing zero - '\u1780', '\u1800', // KHMER - '\u1800', '\u1900', // MONGOLIAN - '\uffff', }; - - // assume most characters are near each other so probing the cache is - // infrequent, - // and a linear probe is ok. - - private static int ctCache = 0; - private static int ctCacheLimit = contexts.length - 2; - - // warning, synchronize access to this as it modifies state - private static int getContextKey(char c) { - if (c < contexts[ctCache]) { - while (ctCache > 0 && c < contexts[ctCache]) - --ctCache; - } else if (c >= contexts[ctCache + 1]) { - while (ctCache < ctCacheLimit && c >= contexts[ctCache + 1]) - ++ctCache; - } - - // if we're not in a known range, then return EUROPEAN as the range key - return (ctCache & 0x1) == 0 ? (ctCache / 2) : EUROPEAN_KEY; - } - - // cache for the NumericShaper.Range version - private transient volatile Range currentRange = Range.EUROPEAN; - - private Range rangeForCodePoint(final int codepoint) { - if (currentRange.inRange(codepoint)) { - return currentRange; - } - - final Range[] ranges = rangeArray; - if (ranges.length > BSEARCH_THRESHOLD) { - int lo = 0; - int hi = ranges.length - 1; - while (lo <= hi) { - int mid = (lo + hi) / 2; - Range range = ranges[mid]; - if (codepoint < range.start) { - hi = mid - 1; - } else if (codepoint >= range.end) { - lo = mid + 1; - } else { - currentRange = range; - return range; - } - } - } else { - for (int i = 0; i < ranges.length; i++) { - if (ranges[i].inRange(codepoint)) { - return ranges[i]; - } - } - } - return Range.EUROPEAN; - } - - /* - * A range table of strong directional characters (types L, R, AL). Even (left) - * indexes are starts of ranges of non-strong-directional (or undefined) - * characters, odd (right) indexes are starts of ranges of strong directional - * characters. - */ - private static int[] strongTable = { 0x0000, 0x0041, 0x005b, 0x0061, 0x007b, 0x00aa, 0x00ab, 0x00b5, 0x00b6, 0x00ba, - 0x00bb, 0x00c0, 0x00d7, 0x00d8, 0x00f7, 0x00f8, 0x02b9, 0x02bb, 0x02c2, 0x02d0, 0x02d2, 0x02e0, 0x02e5, - 0x02ee, 0x02ef, 0x0370, 0x0374, 0x0376, 0x0378, 0x037a, 0x037e, 0x037f, 0x0380, 0x0386, 0x0387, 0x0388, - 0x038b, 0x038c, 0x038d, 0x038e, 0x03a2, 0x03a3, 0x03f6, 0x03f7, 0x0483, 0x048a, 0x0530, 0x0531, 0x0557, - 0x0559, 0x058a, 0x0590, 0x0591, 0x05be, 0x05bf, 0x05c0, 0x05c1, 0x05c3, 0x05c4, 0x05c6, 0x05c7, 0x05c8, - 0x0600, 0x0608, 0x0609, 0x060b, 0x060c, 0x060d, 0x060e, 0x061b, 0x064b, 0x066d, 0x0670, 0x0671, 0x06d6, - 0x06e5, 0x06e7, 0x06ee, 0x06f0, 0x06fa, 0x0711, 0x0712, 0x0730, 0x074b, 0x07a6, 0x07b1, 0x07eb, 0x07f4, - 0x07f6, 0x07fa, 0x07fd, 0x07fe, 0x0816, 0x081a, 0x081b, 0x0824, 0x0825, 0x0828, 0x0829, 0x082e, 0x0859, - 0x085c, 0x08e3, 0x0903, 0x093a, 0x093b, 0x093c, 0x093d, 0x0941, 0x0949, 0x094d, 0x094e, 0x0951, 0x0958, - 0x0962, 0x0964, 0x0981, 0x0982, 0x0984, 0x0985, 0x098d, 0x098f, 0x0991, 0x0993, 0x09a9, 0x09aa, 0x09b1, - 0x09b2, 0x09b3, 0x09b6, 0x09ba, 0x09bd, 0x09c1, 0x09c7, 0x09c9, 0x09cb, 0x09cd, 0x09ce, 0x09cf, 0x09d7, - 0x09d8, 0x09dc, 0x09de, 0x09df, 0x09e2, 0x09e6, 0x09f2, 0x09f4, 0x09fb, 0x09fc, 0x09fe, 0x0a03, 0x0a04, - 0x0a05, 0x0a0b, 0x0a0f, 0x0a11, 0x0a13, 0x0a29, 0x0a2a, 0x0a31, 0x0a32, 0x0a34, 0x0a35, 0x0a37, 0x0a38, - 0x0a3a, 0x0a3e, 0x0a41, 0x0a59, 0x0a5d, 0x0a5e, 0x0a5f, 0x0a66, 0x0a70, 0x0a72, 0x0a75, 0x0a76, 0x0a73, - 0x0a83, 0x0a84, 0x0a85, 0x0a8e, 0x0a8f, 0x0a92, 0x0a93, 0x0aa9, 0x0aaa, 0x0ab1, 0x0ab2, 0x0ab4, 0x0ab5, - 0x0aba, 0x0abd, 0x0ac1, 0x0ac9, 0x0aca, 0x0acb, 0x0acd, 0x0ad0, 0x0ad1, 0x0ae0, 0x0ae2, 0x0ae6, 0x0af1, - 0x0af9, 0x0afa, 0x0b02, 0x0b04, 0x0b05, 0x0b0d, 0x0b0f, 0x0b11, 0x0b13, 0x0b29, 0x0b2a, 0x0b31, 0x0b32, - 0x0b34, 0x0b35, 0x0b3a, 0x0b3d, 0x0b3f, 0x0b40, 0x0b41, 0x0b47, 0x0b49, 0x0b4b, 0x0b4d, 0x0b57, 0x0b58, - 0x0b5c, 0x0b5e, 0x0b5f, 0x0b62, 0x0b66, 0x0b78, 0x0b83, 0x0b84, 0x0b85, 0x0b8b, 0x0b8e, 0x0b91, 0x0b92, - 0x0b96, 0x0b99, 0x0b9b, 0x0b9c, 0x0b9d, 0x0b9e, 0x0ba0, 0x0ba3, 0x0ba5, 0x0ba8, 0x0bab, 0x0bae, 0x0bba, - 0x0bbe, 0x0bc0, 0x0bc1, 0x0bc3, 0x0bc6, 0x0bc9, 0x0bca, 0x0bcd, 0x0bd0, 0x0bd1, 0x0bd7, 0x0bd8, 0x0be6, - 0x0bf3, 0x0c01, 0x0c04, 0x0c05, 0x0c0d, 0x0c0e, 0x0c11, 0x0c12, 0x0c29, 0x0c2a, 0x0c3a, 0x0c3d, 0x0c3e, - 0x0c41, 0x0c45, 0x0c58, 0x0c5b, 0x0c60, 0x0c62, 0x0c66, 0x0c70, 0x0c7f, 0x0c81, 0x0c82, 0x0c8d, 0x0c8e, - 0x0c91, 0x0c92, 0x0ca9, 0x0caa, 0x0cb4, 0x0cb5, 0x0cba, 0x0cbd, 0x0cc5, 0x0cc6, 0x0cc9, 0x0cca, 0x0ccc, - 0x0cd5, 0x0cd7, 0x0cde, 0x0cdf, 0x0ce0, 0x0ce2, 0x0ce6, 0x0cf0, 0x0cf1, 0x0cf3, 0x0d02, 0x0d04, 0x0d05, - 0x0d0d, 0x0d0e, 0x0d11, 0x0d12, 0x0d3b, 0x0d3d, 0x0d41, 0x0d46, 0x0d49, 0x0d4a, 0x0d4d, 0x0d4e, 0x0d62, - 0x0d66, 0x0d80, 0x0d82, 0x0d84, 0x0d85, 0x0d97, 0x0d9a, 0x0db2, 0x0db3, 0x0dbc, 0x0dbd, 0x0dbe, 0x0dc0, - 0x0dc7, 0x0dcf, 0x0dd2, 0x0dd8, 0x0de0, 0x0de6, 0x0df0, 0x0df2, 0x0df5, 0x0e01, 0x0e31, 0x0e32, 0x0e34, - 0x0e40, 0x0e47, 0x0e4f, 0x0e5c, 0x0e81, 0x0e83, 0x0e84, 0x0e85, 0x0e87, 0x0e89, 0x0e8a, 0x0e8b, 0x0e8d, - 0x0e8e, 0x0e94, 0x0e98, 0x0e99, 0x0ea0, 0x0ea1, 0x0ea4, 0x0ea5, 0x0ea6, 0x0ea7, 0x0ea8, 0x0eaa, 0x0eac, - 0x0ead, 0x0eb1, 0x0eb2, 0x0eb4, 0x0ebd, 0x0ebe, 0x0ec0, 0x0ec5, 0x0ec6, 0x0ec7, 0x0ed0, 0x0eda, 0x0edc, - 0x0ee0, 0x0f00, 0x0f18, 0x0f1a, 0x0f35, 0x0f36, 0x0f37, 0x0f38, 0x0f39, 0x0f3e, 0x0f48, 0x0f49, 0x0f6d, - 0x0f7f, 0x0f80, 0x0f85, 0x0f86, 0x0f88, 0x0f8d, 0x0fbe, 0x0fc6, 0x0fc7, 0x0fcd, 0x0fce, 0x0fdb, 0x1000, - 0x102d, 0x1031, 0x1032, 0x1038, 0x1039, 0x103b, 0x103d, 0x103f, 0x1058, 0x105a, 0x105e, 0x1061, 0x1071, - 0x1075, 0x1082, 0x1083, 0x1085, 0x1087, 0x108d, 0x108e, 0x109d, 0x109e, 0x10c6, 0x10c7, 0x10c8, 0x10cd, - 0x10ce, 0x10d0, 0x1249, 0x124a, 0x124e, 0x1250, 0x1257, 0x1258, 0x1259, 0x125a, 0x125e, 0x1260, 0x1289, - 0x128a, 0x128e, 0x1290, 0x12b1, 0x12b2, 0x12b6, 0x12b8, 0x12bf, 0x12c0, 0x12c1, 0x12c2, 0x12c6, 0x12c8, - 0x12d7, 0x12d8, 0x1311, 0x1312, 0x1316, 0x1318, 0x135b, 0x1360, 0x137d, 0x1380, 0x1390, 0x13a0, 0x13f6, - 0x13f8, 0x13fe, 0x1401, 0x1680, 0x1681, 0x169b, 0x16a0, 0x16f9, 0x1700, 0x170d, 0x170e, 0x1712, 0x1720, - 0x1732, 0x1735, 0x1737, 0x1740, 0x1752, 0x1760, 0x176d, 0x176e, 0x1771, 0x1780, 0x17b4, 0x17b6, 0x17b7, - 0x17be, 0x17c6, 0x17c7, 0x17c9, 0x17d4, 0x17db, 0x17dc, 0x17dd, 0x17e0, 0x17ea, 0x1810, 0x181a, 0x1820, - 0x1879, 0x1884, 0x1885, 0x1887, 0x18a9, 0x18aa, 0x18ab, 0x18b0, 0x18f6, 0x1900, 0x191f, 0x1923, 0x1927, - 0x1929, 0x192c, 0x1930, 0x1932, 0x1933, 0x1939, 0x1946, 0x196e, 0x1970, 0x1975, 0x1980, 0x19ac, 0x19b0, - 0x19ca, 0x19d0, 0x19db, 0x1a00, 0x1a17, 0x1a19, 0x1a1b, 0x1a1e, 0x1a56, 0x1a57, 0x1a58, 0x1a61, 0x1a62, - 0x1a63, 0x1a65, 0x1a6d, 0x1a73, 0x1a80, 0x1a8a, 0x1a90, 0x1a9a, 0x1aa0, 0x1aae, 0x1b04, 0x1b34, 0x1b35, - 0x1b36, 0x1b3b, 0x1b3c, 0x1b3d, 0x1b42, 0x1b43, 0x1b4c, 0x1b50, 0x1b6b, 0x1b74, 0x1b7d, 0x1b82, 0x1ba2, - 0x1ba6, 0x1ba8, 0x1baa, 0x1bab, 0x1bae, 0x1be6, 0x1be7, 0x1be8, 0x1bea, 0x1bed, 0x1bee, 0x1bef, 0x1bf2, - 0x1bf4, 0x1bfc, 0x1c2c, 0x1c34, 0x1c36, 0x1c3b, 0x1c4a, 0x1c4d, 0x1c89, 0x1c90, 0x1cbb, 0x1cbd, 0x1cc8, - 0x1cd3, 0x1cd4, 0x1ce1, 0x1ce2, 0x1ce9, 0x1ced, 0x1cee, 0x1cf4, 0x1cf5, 0x1cf8, 0x1d00, 0x1dc0, 0x1e00, - 0x1f16, 0x1f18, 0x1f1e, 0x1f20, 0x1f46, 0x1f48, 0x1f4e, 0x1f50, 0x1f58, 0x1f59, 0x1f5a, 0x1f5b, 0x1f5c, - 0x1f5d, 0x1f5e, 0x1f5f, 0x1f7e, 0x1f80, 0x1fb5, 0x1fb6, 0x1fbd, 0x1fbe, 0x1fbf, 0x1fc2, 0x1fc5, 0x1fc6, - 0x1fcd, 0x1fd0, 0x1fd4, 0x1fd6, 0x1fdc, 0x1fe0, 0x1fed, 0x1ff2, 0x1ff5, 0x1ff6, 0x1ffd, 0x200e, 0x2010, - 0x2071, 0x2072, 0x207f, 0x2080, 0x2090, 0x209d, 0x2102, 0x2103, 0x2107, 0x2108, 0x210a, 0x2114, 0x2115, - 0x2116, 0x2119, 0x211e, 0x2124, 0x2125, 0x2126, 0x2127, 0x2128, 0x2129, 0x212a, 0x212e, 0x212f, 0x213a, - 0x213c, 0x2140, 0x2145, 0x214a, 0x214e, 0x2150, 0x2160, 0x2189, 0x2336, 0x237b, 0x2395, 0x2396, 0x249c, - 0x24ea, 0x26ac, 0x26ad, 0x2800, 0x2900, 0x2c00, 0x2c2f, 0x2c30, 0x2c5f, 0x2c60, 0x2ce5, 0x2ceb, 0x2cef, - 0x2cf2, 0x2cf4, 0x2d00, 0x2d26, 0x2d27, 0x2d28, 0x2d2d, 0x2d2e, 0x2d30, 0x2d68, 0x2d6f, 0x2d71, 0x2d80, - 0x2d97, 0x2da0, 0x2da7, 0x2da8, 0x2daf, 0x2db0, 0x2db7, 0x2db8, 0x2dbf, 0x2dc0, 0x2dc7, 0x2dc8, 0x2dcf, - 0x2dd0, 0x2dd7, 0x2dd8, 0x2ddf, 0x3005, 0x3008, 0x3021, 0x302a, 0x302e, 0x3030, 0x3031, 0x3036, 0x3038, - 0x303d, 0x3041, 0x3097, 0x309d, 0x30a0, 0x30a1, 0x30fb, 0x30fc, 0x3100, 0x3105, 0x3130, 0x3131, 0x318f, - 0x3190, 0x31bb, 0x31f0, 0x321d, 0x3220, 0x3250, 0x3260, 0x327c, 0x327f, 0x32b1, 0x32c0, 0x32cc, 0x32d0, - 0x32ff, 0x3300, 0x3377, 0x337b, 0x33de, 0x33e0, 0x33ff, 0x3400, 0x4db6, 0x4e00, 0x9ff0, 0xa000, 0xa48d, - 0xa4d0, 0xa60d, 0xa610, 0xa62c, 0xa640, 0xa66f, 0xa680, 0xa69e, 0xa6a0, 0xa6f0, 0xa6f2, 0xa6f8, 0xa722, - 0xa788, 0xa789, 0xa7ba, 0xa7f7, 0xa802, 0xa803, 0xa806, 0xa807, 0xa80b, 0xa80c, 0xa825, 0xa827, 0xa828, - 0xa830, 0xa838, 0xa840, 0xa874, 0xa880, 0xa8c4, 0xa8ce, 0xa8da, 0xa8f2, 0xa8ff, 0xa900, 0xa926, 0xa92e, - 0xa947, 0xa952, 0xa954, 0xa95f, 0xa97d, 0xa983, 0xa9b3, 0xa9b4, 0xa9b6, 0xa9ba, 0xa9bc, 0xa9bd, 0xa9ce, - 0xa9cf, 0xa9da, 0xa9de, 0xa9e5, 0xa9e6, 0xa9ff, 0xaa00, 0xaa29, 0xaa2f, 0xaa31, 0xaa33, 0xaa35, 0xaa40, - 0xaa43, 0xaa44, 0xaa4c, 0xaa4d, 0xaa4e, 0xaa50, 0xaa5a, 0xaa5c, 0xaa7c, 0xaa7d, 0xaab0, 0xaab1, 0xaab2, - 0xaab5, 0xaab7, 0xaab9, 0xaabe, 0xaac0, 0xaac1, 0xaac2, 0xaac3, 0xaadb, 0xaaec, 0xaaee, 0xaaf6, 0xab01, - 0xab07, 0xab09, 0xab0f, 0xab11, 0xab17, 0xab20, 0xab27, 0xab28, 0xab2f, 0xab30, 0xab66, 0xab70, 0xabe5, - 0xabe6, 0xabe8, 0xabe9, 0xabed, 0xabf0, 0xabfa, 0xac00, 0xd7a4, 0xd7b0, 0xd7c7, 0xd7cb, 0xd7fc, 0xe000, - 0xfa6e, 0xfa70, 0xfada, 0xfb00, 0xfb07, 0xfb13, 0xfb18, 0xfb1d, 0xfb1e, 0xfb1f, 0xfb29, 0xfb2a, 0xfd3e, - 0xfd40, 0xfdd0, 0xfdf0, 0xfdfd, 0xfdfe, 0xfe00, 0xfe70, 0xfeff, 0xff21, 0xff3b, 0xff41, 0xff5b, 0xff66, - 0xffbf, 0xffc2, 0xffc8, 0xffca, 0xffd0, 0xffd2, 0xffd8, 0xffda, 0xffdd, 0x10000, 0x1000c, 0x1000d, 0x10027, - 0x10028, 0x1003b, 0x1003c, 0x1003e, 0x1003f, 0x1004e, 0x10050, 0x1005e, 0x10080, 0x100fb, 0x10100, 0x10101, - 0x10102, 0x10103, 0x10107, 0x10134, 0x10137, 0x10140, 0x1018d, 0x1018f, 0x101d0, 0x101fd, 0x10280, 0x1029d, - 0x102a0, 0x102d1, 0x10300, 0x10324, 0x1032d, 0x1034b, 0x10350, 0x10376, 0x10380, 0x1039e, 0x1039f, 0x103c4, - 0x103c8, 0x103d6, 0x10400, 0x1049e, 0x104a0, 0x104aa, 0x104d3, 0x104d4, 0x104d8, 0x104fc, 0x10500, 0x10528, - 0x10530, 0x10564, 0x1056f, 0x10570, 0x10600, 0x10737, 0x10740, 0x10756, 0x10760, 0x10768, 0x10800, 0x1091f, - 0x10920, 0x10a01, 0x10a04, 0x10a05, 0x10a07, 0x10a0c, 0x10a10, 0x10a38, 0x10a3b, 0x10a3f, 0x10a40, 0x10ae5, - 0x10ae7, 0x10b39, 0x10b40, 0x10d00, 0x10d40, 0x10e60, 0x10e7f, 0x10f30, 0x10f70, 0x11001, 0x11002, 0x11038, - 0x11047, 0x1104e, 0x11066, 0x11070, 0x11082, 0x110b3, 0x110b7, 0x110b9, 0x110bb, 0x110c2, 0x110cd, 0x110ce, - 0x110d0, 0x110e9, 0x110f0, 0x110fa, 0x11103, 0x11127, 0x1112c, 0x1112d, 0x11136, 0x11147, 0x11150, 0x11173, - 0x11174, 0x11177, 0x11182, 0x111b6, 0x111bf, 0x111c9, 0x111cd, 0x111ce, 0x111d0, 0x111e0, 0x111e1, 0x111f5, - 0x11200, 0x11212, 0x11213, 0x1122f, 0x11232, 0x11234, 0x11235, 0x11236, 0x11238, 0x1123e, 0x11280, 0x11287, - 0x11288, 0x11289, 0x1128a, 0x1128e, 0x1128f, 0x1129e, 0x1129f, 0x112aa, 0x112b0, 0x112df, 0x112e0, 0x112e3, - 0x112f0, 0x112fa, 0x11302, 0x11304, 0x11305, 0x1130d, 0x1130f, 0x11311, 0x11313, 0x11329, 0x1132a, 0x11331, - 0x11332, 0x11334, 0x11335, 0x1133a, 0x1133d, 0x11340, 0x11341, 0x11345, 0x11347, 0x11349, 0x1134b, 0x1134e, - 0x11350, 0x11351, 0x11357, 0x11358, 0x1135d, 0x11364, 0x11400, 0x11438, 0x11440, 0x11442, 0x11445, 0x11446, - 0x11447, 0x1145a, 0x1145b, 0x1145c, 0x1145d, 0x1145e, 0x11480, 0x114b3, 0x114b9, 0x114ba, 0x114bb, 0x114bf, - 0x114c1, 0x114c2, 0x114c4, 0x114c8, 0x114d0, 0x114da, 0x11580, 0x115b2, 0x115b8, 0x115bc, 0x115be, 0x115bf, - 0x115c1, 0x115dc, 0x11600, 0x11633, 0x1163b, 0x1163d, 0x1163e, 0x1163f, 0x11641, 0x11645, 0x11650, 0x1165a, - 0x11680, 0x116ab, 0x116ac, 0x116ad, 0x116ae, 0x116b0, 0x116b6, 0x116b7, 0x116c0, 0x116ca, 0x11700, 0x1171b, - 0x11720, 0x11722, 0x11726, 0x11727, 0x11730, 0x1182f, 0x11838, 0x11839, 0x1183b, 0x1183c, 0x118a0, 0x118f3, - 0x118ff, 0x11900, 0x11a00, 0x11a01, 0x11a07, 0x11a09, 0x11a0b, 0x11a33, 0x11a3a, 0x11a3b, 0x11a3f, 0x11a47, - 0x11a50, 0x11a51, 0x11a57, 0x11a59, 0x11a5c, 0x11a84, 0x11a86, 0x11a8a, 0x11a97, 0x11a98, 0x11a9a, 0x11aa3, - 0x11ac0, 0x11af9, 0x11c00, 0x11c09, 0x11c0a, 0x11c30, 0x11c3e, 0x11c46, 0x11c50, 0x11c6d, 0x11c70, 0x11c90, - 0x11ca9, 0x11caa, 0x11cb1, 0x11cb2, 0x11cb4, 0x11cb5, 0x11d00, 0x11d07, 0x11d08, 0x11d0a, 0x11d0b, 0x11d31, - 0x11d46, 0x11d47, 0x11d50, 0x11d5a, 0x11d60, 0x11d66, 0x11d67, 0x11d69, 0x11d6a, 0x11d8f, 0x11d93, 0x11d95, - 0x11d96, 0x11d97, 0x11d98, 0x11d99, 0x11da0, 0x11daa, 0x11ee0, 0x11ef3, 0x11ef5, 0x11ef9, 0x12000, 0x1239a, - 0x12400, 0x1246f, 0x12470, 0x12475, 0x12480, 0x12544, 0x13000, 0x1342f, 0x14400, 0x14647, 0x16800, 0x16a39, - 0x16a40, 0x16a5f, 0x16a60, 0x16a6a, 0x16a6e, 0x16a70, 0x16ad0, 0x16aee, 0x16af5, 0x16af6, 0x16b00, 0x16b30, - 0x16b37, 0x16b46, 0x16b50, 0x16b5a, 0x16b5b, 0x16b62, 0x16b63, 0x16b78, 0x16b7d, 0x16b90, 0x16e40, 0x16e9b, - 0x16f00, 0x16f45, 0x16f50, 0x16f7f, 0x16f93, 0x16fa0, 0x16fe0, 0x16fe2, 0x17000, 0x187f2, 0x18800, 0x18af3, - 0x1b000, 0x1b11f, 0x1b170, 0x1b2fc, 0x1bc00, 0x1bc6b, 0x1bc70, 0x1bc7d, 0x1bc80, 0x1bc89, 0x1bc90, 0x1bc9a, - 0x1bc9c, 0x1bc9d, 0x1bc9f, 0x1bca0, 0x1d000, 0x1d0f6, 0x1d100, 0x1d127, 0x1d129, 0x1d167, 0x1d16a, 0x1d173, - 0x1d183, 0x1d185, 0x1d18c, 0x1d1aa, 0x1d1ae, 0x1d1e9, 0x1d2e0, 0x1d2f4, 0x1d360, 0x1d379, 0x1d400, 0x1d455, - 0x1d456, 0x1d49d, 0x1d49e, 0x1d4a0, 0x1d4a2, 0x1d4a3, 0x1d4a5, 0x1d4a7, 0x1d4a9, 0x1d4ad, 0x1d4ae, 0x1d4ba, - 0x1d4bb, 0x1d4bc, 0x1d4bd, 0x1d4c4, 0x1d4c5, 0x1d506, 0x1d507, 0x1d50b, 0x1d50d, 0x1d515, 0x1d516, 0x1d51d, - 0x1d51e, 0x1d53a, 0x1d53b, 0x1d53f, 0x1d540, 0x1d545, 0x1d546, 0x1d547, 0x1d54a, 0x1d551, 0x1d552, 0x1d6a6, - 0x1d6a8, 0x1d6db, 0x1d6dc, 0x1d715, 0x1d716, 0x1d74f, 0x1d750, 0x1d789, 0x1d78a, 0x1d7c3, 0x1d7c4, 0x1d7cc, - 0x1d800, 0x1da00, 0x1da37, 0x1da3b, 0x1da6d, 0x1da75, 0x1da76, 0x1da84, 0x1da85, 0x1da8c, 0x1e800, 0x1e8d0, - 0x1e8d7, 0x1e944, 0x1e94b, 0x1ec70, 0x1ecc0, 0x1ee00, 0x1ef00, 0x1f000, 0x1f110, 0x1f12f, 0x1f130, 0x1f16a, - 0x1f170, 0x1f1ad, 0x1f1e6, 0x1f203, 0x1f210, 0x1f23c, 0x1f240, 0x1f249, 0x1f250, 0x1f252, 0x20000, 0x2a6d7, - 0x2a700, 0x2b735, 0x2b740, 0x2b81e, 0x2b820, 0x2cea2, 0x2ceb0, 0x2ebe1, 0x2f800, 0x2fa1e, 0xf0000, 0xffffe, - 0x100000, 0x10fffe, 0x10ffff // sentinel - }; - - // use a binary search with a cache - - private transient volatile int stCache = 0; - - private boolean isStrongDirectional(char c) { - int cachedIndex = stCache; - if (c < strongTable[cachedIndex]) { - cachedIndex = search(c, strongTable, 0, cachedIndex); - } else if (c >= strongTable[cachedIndex + 1]) { - cachedIndex = search(c, strongTable, cachedIndex + 1, strongTable.length - cachedIndex - 1); - } - boolean val = (cachedIndex & 0x1) == 1; - stCache = cachedIndex; - return val; - } - - private static int getKeyFromMask(int mask) { - int key = 0; - while (key < NUM_KEYS && ((mask & (1 << key)) == 0)) { - ++key; - } - if (key == NUM_KEYS || ((mask & ~(1 << key)) != 0)) { - throw new IllegalArgumentException("invalid shaper: " + Integer.toHexString(mask)); - } - return key; - } - - /** - * Returns a shaper for the provided unicode range. All Latin-1 (EUROPEAN) - * digits are converted to the corresponding decimal unicode digits. - * - * @param singleRange the specified Unicode range - * @return a non-contextual numeric shaper - * @throws IllegalArgumentException if the range is not a single range - */ - public static NumericShaper getShaper(int singleRange) { - int key = getKeyFromMask(singleRange); - return new NumericShaper(key, singleRange); - } - - /** - * Returns a shaper for the provided Unicode range. All Latin-1 (EUROPEAN) - * digits are converted to the corresponding decimal digits of the specified - * Unicode range. - * - * @param singleRange the Unicode range given by a {@link NumericShaper.Range} - * constant. - * @return a non-contextual {@code NumericShaper}. - * @throws NullPointerException if {@code singleRange} is {@code null} - * @since 1.7 - */ - public static NumericShaper getShaper(Range singleRange) { - return new NumericShaper(singleRange, EnumSet.of(singleRange)); - } - - /** - * Returns a contextual shaper for the provided unicode range(s). Latin-1 - * (EUROPEAN) digits are converted to the decimal digits corresponding to the - * range of the preceding text, if the range is one of the provided ranges. - * Multiple ranges are represented by or-ing the values together, such as, - * {@code NumericShaper.ARABIC | NumericShaper.THAI}. The shaper assumes - * EUROPEAN as the starting context, that is, if EUROPEAN digits are encountered - * before any strong directional text in the string, the context is presumed to - * be EUROPEAN, and so the digits will not shape. - * - * @param ranges the specified Unicode ranges - * @return a shaper for the specified ranges - */ - public static NumericShaper getContextualShaper(int ranges) { - ranges |= CONTEXTUAL_MASK; - return new NumericShaper(EUROPEAN_KEY, ranges); - } - - /** - * Returns a contextual shaper for the provided Unicode range(s). The Latin-1 - * (EUROPEAN) digits are converted to the decimal digits corresponding to the - * range of the preceding text, if the range is one of the provided ranges. - * - *

- * The shaper assumes EUROPEAN as the starting context, that is, if EUROPEAN - * digits are encountered before any strong directional text in the string, the - * context is presumed to be EUROPEAN, and so the digits will not shape. - * - * @param ranges the specified Unicode ranges - * @return a contextual shaper for the specified ranges - * @throws NullPointerException if {@code ranges} is {@code null}. - * @since 1.7 - */ - public static NumericShaper getContextualShaper(Set ranges) { - NumericShaper shaper = new NumericShaper(Range.EUROPEAN, ranges); - shaper.mask = CONTEXTUAL_MASK; - return shaper; - } - - /** - * Returns a contextual shaper for the provided unicode range(s). Latin-1 - * (EUROPEAN) digits will be converted to the decimal digits corresponding to - * the range of the preceding text, if the range is one of the provided ranges. - * Multiple ranges are represented by or-ing the values together, for example, - * {@code NumericShaper.ARABIC | NumericShaper.THAI}. The shaper uses - * defaultContext as the starting context. - * - * @param ranges the specified Unicode ranges - * @param defaultContext the starting context, such as - * {@code NumericShaper.EUROPEAN} - * @return a shaper for the specified Unicode ranges. - * @throws IllegalArgumentException if the specified {@code defaultContext} is - * not a single valid range. - */ - public static NumericShaper getContextualShaper(int ranges, int defaultContext) { - int key = getKeyFromMask(defaultContext); - ranges |= CONTEXTUAL_MASK; - return new NumericShaper(key, ranges); - } - - /** - * Returns a contextual shaper for the provided Unicode range(s). The Latin-1 - * (EUROPEAN) digits will be converted to the decimal digits corresponding to - * the range of the preceding text, if the range is one of the provided ranges. - * The shaper uses {@code - * defaultContext} as the starting context. - * - * @param ranges the specified Unicode ranges - * @param defaultContext the starting context, such as - * {@code NumericShaper.Range.EUROPEAN} - * @return a contextual shaper for the specified Unicode ranges. - * @throws NullPointerException if {@code ranges} or {@code defaultContext} is - * {@code null} - * @since 1.7 - */ - public static NumericShaper getContextualShaper(Set ranges, Range defaultContext) { - if (defaultContext == null) { - throw new NullPointerException(); - } - NumericShaper shaper = new NumericShaper(defaultContext, ranges); - shaper.mask = CONTEXTUAL_MASK; - return shaper; - } - - /** - * Private constructor. - */ - private NumericShaper(int key, int mask) { - this.key = key; - this.mask = mask; - } - - private NumericShaper(Range defaultContext, Set ranges) { - shapingRange = defaultContext; - rangeSet = EnumSet.copyOf(ranges); // throws NPE if ranges is null. - - // Give precedence to EASTERN_ARABIC if both ARABIC and - // EASTERN_ARABIC are specified. - if (rangeSet.contains(Range.EASTERN_ARABIC) && rangeSet.contains(Range.ARABIC)) { - rangeSet.remove(Range.ARABIC); - } - - // As well as the above case, give precedence to TAI_THAM_THAM if both - // TAI_THAM_HORA and TAI_THAM_THAM are specified. - if (rangeSet.contains(Range.TAI_THAM_THAM) && rangeSet.contains(Range.TAI_THAM_HORA)) { - rangeSet.remove(Range.TAI_THAM_HORA); - } - - rangeArray = rangeSet.toArray(new Range[rangeSet.size()]); - if (rangeArray.length > BSEARCH_THRESHOLD) { - // sort rangeArray for binary search - Arrays.sort(rangeArray, new Comparator() { - public int compare(Range s1, Range s2) { - return s1.base > s2.base ? 1 : s1.base == s2.base ? 0 : -1; - } - }); - } - } - - /** - * Converts the digits in the text that occur between start and start + count. - * - * @param text an array of characters to convert - * @param start the index into {@code text} to start converting - * @param count the number of characters in {@code text} to convert - * @throws IndexOutOfBoundsException if start or start + count is out of bounds - * @throws NullPointerException if text is null - */ - public void shape(char[] text, int start, int count) { - checkParams(text, start, count); - if (isContextual()) { - if (rangeSet == null) { - shapeContextually(text, start, count, key); - } else { - shapeContextually(text, start, count, shapingRange); - } - } else { - shapeNonContextually(text, start, count); - } - } - - /** - * Converts the digits in the text that occur between start and start + count, - * using the provided context. Context is ignored if the shaper is not a - * contextual shaper. - * - * @param text an array of characters - * @param start the index into {@code text} to start converting - * @param count the number of characters in {@code text} to convert - * @param context the context to which to convert the characters, such as - * {@code NumericShaper.EUROPEAN} - * @throws IndexOutOfBoundsException if start or start + count is out of bounds - * @throws NullPointerException if text is null - * @throws IllegalArgumentException if this is a contextual shaper and the - * specified {@code context} is not a single - * valid range. - */ - public void shape(char[] text, int start, int count, int context) { - checkParams(text, start, count); - if (isContextual()) { - int ctxKey = getKeyFromMask(context); - if (rangeSet == null) { - shapeContextually(text, start, count, ctxKey); - } else { - shapeContextually(text, start, count, Range.values()[ctxKey]); - } - } else { - shapeNonContextually(text, start, count); - } - } - - /** - * Converts the digits in the text that occur between {@code - * start} and {@code start + count}, using the provided {@code - * context}. {@code Context} is ignored if the shaper is not a contextual - * shaper. - * - * @param text a {@code char} array - * @param start the index into {@code text} to start converting - * @param count the number of {@code char}s in {@code text} to convert - * @param context the context to which to convert the characters, such as - * {@code NumericShaper.Range.EUROPEAN} - * @throws IndexOutOfBoundsException if {@code start} or {@code start + count} - * is out of bounds - * @throws NullPointerException if {@code text} or {@code context} is null - * @since 1.7 - */ - public void shape(char[] text, int start, int count, Range context) { - checkParams(text, start, count); - if (context == null) { - throw new NullPointerException("context is null"); - } - - if (isContextual()) { - if (rangeSet != null) { - shapeContextually(text, start, count, context); - } else { - int key = Range.toRangeIndex(context); - if (key >= 0) { - shapeContextually(text, start, count, key); - } else { - shapeContextually(text, start, count, shapingRange); - } - } - } else { - shapeNonContextually(text, start, count); - } - } - - private void checkParams(char[] text, int start, int count) { - if (text == null) { - throw new NullPointerException("text is null"); - } - if ((start < 0) || (start > text.length) || ((start + count) < 0) || ((start + count) > text.length)) { - throw new IndexOutOfBoundsException("bad start or count for text of length " + text.length); - } - } - - /** - * Returns a {@code boolean} indicating whether or not this shaper shapes - * contextually. - * - * @return {@code true} if this shaper is contextual; {@code false} otherwise. - */ - public boolean isContextual() { - return (mask & CONTEXTUAL_MASK) != 0; - } - - /** - * Returns an {@code int} that ORs together the values for all the ranges that - * will be shaped. - *

- * For example, to check if a shaper shapes to Arabic, you would use the - * following:

- * {@code if ((shaper.getRanges() & shaper.ARABIC) != 0) { ... } - *
- * - *

- * Note that this method supports only the bit mask-based ranges. Call - * {@link #getRangeSet()} for the enum-based ranges. - * - * @return the values for all the ranges to be shaped. - */ - public int getRanges() { - return mask & ~CONTEXTUAL_MASK; - } - - /** - * Returns a {@code Set} representing all the Unicode ranges in this - * {@code NumericShaper} that will be shaped. - * - * @return all the Unicode ranges to be shaped. - * @since 1.7 - */ - public Set getRangeSet() { - if (rangeSet != null) { - return EnumSet.copyOf(rangeSet); - } - return Range.maskToRangeSet(mask); - } - - /** - * Perform non-contextual shaping. - */ - private void shapeNonContextually(char[] text, int start, int count) { - int base; - char minDigit = '0'; - if (shapingRange != null) { - base = shapingRange.getDigitBase(); - minDigit += shapingRange.getNumericBase(); - } else { - base = bases[key]; - if (key == ETHIOPIC_KEY) { - minDigit++; // Ethiopic doesn't use decimal zero - } - } - for (int i = start, e = start + count; i < e; ++i) { - char c = text[i]; - if (c >= minDigit && c <= '\u0039') { - text[i] = (char) (c + base); - } - } - } - - /** - * Perform contextual shaping. Synchronized to protect caches used in - * getContextKey. - */ - private synchronized void shapeContextually(char[] text, int start, int count, int ctxKey) { - - // if we don't support this context, then don't shape - if ((mask & (1 << ctxKey)) == 0) { - ctxKey = EUROPEAN_KEY; - } - int lastkey = ctxKey; - - int base = bases[ctxKey]; - char minDigit = ctxKey == ETHIOPIC_KEY ? '1' : '0'; // Ethiopic doesn't use decimal zero - - synchronized (NumericShaper.class) { - for (int i = start, e = start + count; i < e; ++i) { - char c = text[i]; - if (c >= minDigit && c <= '\u0039') { - text[i] = (char) (c + base); - } - - if (isStrongDirectional(c)) { - int newkey = getContextKey(c); - if (newkey != lastkey) { - lastkey = newkey; - - ctxKey = newkey; - if (((mask & EASTERN_ARABIC) != 0) && (ctxKey == ARABIC_KEY || ctxKey == EASTERN_ARABIC_KEY)) { - ctxKey = EASTERN_ARABIC_KEY; - } else if (((mask & ARABIC) != 0) && (ctxKey == ARABIC_KEY || ctxKey == EASTERN_ARABIC_KEY)) { - ctxKey = ARABIC_KEY; - } else if ((mask & (1 << ctxKey)) == 0) { - ctxKey = EUROPEAN_KEY; - } - - base = bases[ctxKey]; - - minDigit = ctxKey == ETHIOPIC_KEY ? '1' : '0'; // Ethiopic doesn't use decimal zero - } - } - } - } - } - - private void shapeContextually(char[] text, int start, int count, Range ctxKey) { - // if we don't support the specified context, then don't shape. - if (ctxKey == null || !rangeSet.contains(ctxKey)) { - ctxKey = Range.EUROPEAN; - } - - Range lastKey = ctxKey; - int base = ctxKey.getDigitBase(); - char minDigit = (char) ('0' + ctxKey.getNumericBase()); - final int end = start + count; - for (int i = start; i < end; ++i) { - char c = text[i]; - if (c >= minDigit && c <= '9') { - text[i] = (char) (c + base); - continue; - } - if (isStrongDirectional(c)) { - ctxKey = rangeForCodePoint(c); - if (ctxKey != lastKey) { - lastKey = ctxKey; - base = ctxKey.getDigitBase(); - minDigit = (char) ('0' + ctxKey.getNumericBase()); - } - } - } - } - - /** - * Returns a hash code for this shaper. - * - * @return this shaper's hash code. - * @see java.lang.Object#hashCode - */ - public int hashCode() { - int hash = mask; - if (rangeSet != null) { - // Use the CONTEXTUAL_MASK bit only for the enum-based - // NumericShaper. A deserialized NumericShaper might have - // bit masks. - hash &= CONTEXTUAL_MASK; - hash ^= rangeSet.hashCode(); - } - return hash; - } - - /** - * Returns {@code true} if the specified object is an instance of - * {@code NumericShaper} and shapes identically to this one, regardless of the - * range representations, the bit mask or the enum. For example, the following - * code produces {@code "true"}.

- * - *
-	 * NumericShaper ns1 = NumericShaper.getShaper(NumericShaper.ARABIC);
-	 * NumericShaper ns2 = NumericShaper.getShaper(NumericShaper.Range.ARABIC);
-	 * System.out.println(ns1.equals(ns2));
-	 * 
- * - *
- * - * @param o the specified object to compare to this {@code NumericShaper} - * @return {@code true} if {@code o} is an instance of {@code NumericShaper} and - * shapes in the same way; {@code false} otherwise. - * @see java.lang.Object#equals(java.lang.Object) - */ - public boolean equals(Object o) { - if (o != null) { - try { - NumericShaper rhs = (NumericShaper) o; - if (rangeSet != null) { - if (rhs.rangeSet != null) { - return isContextual() == rhs.isContextual() && rangeSet.equals(rhs.rangeSet) - && shapingRange == rhs.shapingRange; - } - return isContextual() == rhs.isContextual() && rangeSet.equals(Range.maskToRangeSet(rhs.mask)) - && shapingRange == Range.indexToRange(rhs.key); - } else if (rhs.rangeSet != null) { - Set rset = Range.maskToRangeSet(mask); - Range srange = Range.indexToRange(key); - return isContextual() == rhs.isContextual() && rset.equals(rhs.rangeSet) - && srange == rhs.shapingRange; - } - return rhs.mask == mask && rhs.key == key; - } catch (ClassCastException e) { - } - } - return false; - } - - /** - * Returns a {@code String} that describes this shaper. This method is used for - * debugging purposes only. - * - * @return a {@code String} describing this shaper. - */ - public String toString() { - StringBuilder buf = new StringBuilder(super.toString()); - - buf.append("[contextual:").append(isContextual()); - - String[] keyNames = null; - if (isContextual()) { - buf.append(", context:"); - buf.append(shapingRange == null ? Range.values()[key] : shapingRange); - } - - if (rangeSet == null) { - buf.append(", range(s): "); - boolean first = true; - for (int i = 0; i < NUM_KEYS; ++i) { - if ((mask & (1 << i)) != 0) { - if (first) { - first = false; - } else { - buf.append(", "); - } - buf.append(Range.values()[i]); - } - } - } else { - buf.append(", range set: ").append(rangeSet); - } - buf.append(']'); - - return buf.toString(); - } - - /** - * Returns the index of the high bit in value (assuming le, actually power of 2 - * >= value). value must be positive. - */ - private static int getHighBit(int value) { - if (value <= 0) { - return -32; - } - - int bit = 0; - - 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) { - bit += 1; - } - - return bit; - } - - /** - * fast binary search over subrange of array. - */ - private static int search(int value, int[] array, int start, int length) { - int power = 1 << getHighBit(length); - int extra = length - power; - int probe = power; - int index = start; - - if (value >= array[index + extra]) { - index += extra; - } - - while (probe > 1) { - probe >>= 1; - - if (value >= array[index + probe]) { - index += probe; - } - } - - return index; - } - - /** - * Converts the {@code NumericShaper.Range} enum-based parameters, if any, to - * the bit mask-based counterparts and writes this object to the {@code stream}. - * Any enum constants that have no bit mask-based counterparts are ignored in - * the conversion. - * - * @param stream the output stream to write to - * @throws IOException if an I/O error occurs while writing to {@code stream} - * @since 1.7 - */ - private void writeObject(ObjectOutputStream stream) throws IOException { - if (shapingRange != null) { - int index = Range.toRangeIndex(shapingRange); - if (index >= 0) { - key = index; - } - } - if (rangeSet != null) { - mask |= Range.toRangeMask(rangeSet); - } - stream.defaultWriteObject(); - } -} +/* + * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk_internal.bidi; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.util.Arrays; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.Set; + +/** + * The {@code NumericShaper} class is used to convert Latin-1 (European) digits + * to other Unicode decimal digits. Users of this class will primarily be people + * who wish to present data using national digit shapes, but find it more + * convenient to represent the data internally using Latin-1 (European) digits. + * This does not interpret the deprecated numeric shape selector character + * (U+206E). + *

+ * Instances of {@code NumericShaper} are typically applied as attributes to + * text with the {@link TextAttribute#NUMERIC_SHAPING NUMERIC_SHAPING} attribute + * of the {@code TextAttribute} class. For example, this code snippet causes a + * {@code TextLayout} to shape European digits to Arabic in an Arabic + * context:
+ *

+ * + *
+ * Map map = new HashMap();
+ * map.put(TextAttribute.NUMERIC_SHAPING,
+ *     NumericShaper.getContextualShaper(NumericShaper.ARABIC));
+ * FontRenderContext frc = ...;
+ * TextLayout layout = new TextLayout(text, map, frc);
+ * layout.draw(g2d, x, y);
+ * 
+ * + *

+ * It is also possible to perform numeric shaping explicitly using instances of + * {@code NumericShaper}, as this code snippet demonstrates:
+ *
+ * + *
+ * char[] text = ...;
+ * // shape all EUROPEAN digits (except zero) to ARABIC digits
+ * NumericShaper shaper = NumericShaper.getShaper(NumericShaper.ARABIC);
+ * shaper.shape(text, start, count);
+ *
+ * // shape European digits to ARABIC digits if preceding text is Arabic, or
+ * // shape European digits to TAMIL digits if preceding text is Tamil, or
+ * // leave European digits alone if there is no preceding text, or
+ * // preceding text is neither Arabic nor Tamil
+ * NumericShaper shaper =
+ *     NumericShaper.getContextualShaper(NumericShaper.ARABIC |
+ *                                         NumericShaper.TAMIL,
+ *                                       NumericShaper.EUROPEAN);
+ * shaper.shape(text, start, count);
+ * 
+ * + *
+ * + *

+ * Bit mask- and enum-based Unicode ranges + *

+ * + *

+ * This class supports two different programming interfaces to represent Unicode + * ranges for script-specific digits: bit mask-based ones, such as + * {@link #ARABIC NumericShaper.ARABIC}, and enum-based ones, such as + * {@link NumericShaper.Range#ARABIC}. Multiple ranges can be specified by ORing + * bit mask-based constants, such as:

+ * + *
+ * NumericShaper.ARABIC | NumericShaper.TAMIL
+ * 
+ * + *
or creating a {@code Set} with the {@link NumericShaper.Range} + * constants, such as:
+ * + *
+ * EnumSet.of(NumericShaper.Range.ARABIC, NumericShaper.Range.TAMIL)
+ * 
+ * + *
The enum-based ranges are a super set of the bit mask-based + * ones. + * + *

+ * If the two interfaces are mixed (including serialization), Unicode range + * values are mapped to their counterparts where such mapping is possible, such + * as {@code NumericShaper.Range.ARABIC} from/to {@code NumericShaper.ARABIC}. + * If any unmappable range values are specified, such as + * {@code NumericShaper.Range.BALINESE}, those ranges are ignored. + * + *

+ * Decimal Digits Precedence + *

+ * + *

+ * A Unicode range may have more than one set of decimal digits. If multiple + * decimal digits sets are specified for the same Unicode range, one of the sets + * will take precedence as follows. + * + * + * + * + * + * + * + * + * + * + * + *
NumericShaper constants precedence
Unicode Range + * {@code NumericShaper} Constants + * Precedence
Arabic + * {@link NumericShaper#ARABIC NumericShaper.ARABIC}
+ * {@link NumericShaper#EASTERN_ARABIC NumericShaper.EASTERN_ARABIC} + *
{@link NumericShaper#EASTERN_ARABIC NumericShaper.EASTERN_ARABIC} + *
{@link NumericShaper.Range#ARABIC}
+ * {@link NumericShaper.Range#EASTERN_ARABIC} + *
{@link NumericShaper.Range#EASTERN_ARABIC}
Tai Tham + * {@link NumericShaper.Range#TAI_THAM_HORA}
+ * {@link NumericShaper.Range#TAI_THAM_THAM} + *
{@link NumericShaper.Range#TAI_THAM_THAM}
+ * + * @since 1.4 + */ +public final class NumericShaper implements java.io.Serializable { + + /** + * A {@code NumericShaper.Range} represents a Unicode range of a script having + * its own decimal digits. For example, the {@link NumericShaper.Range#THAI} + * range has the Thai digits, THAI DIGIT ZERO (U+0E50) to THAI DIGIT NINE + * (U+0E59). + * + *

+ * The {@code Range} enum replaces the traditional bit mask-based values (e.g., + * {@link NumericShaper#ARABIC}), and supports more Unicode ranges than the bit + * mask-based ones. For example, the following code using the bit mask: + *

+ * + *
+	 * NumericShaper.getContextualShaper(NumericShaper.ARABIC | NumericShaper.TAMIL, NumericShaper.EUROPEAN);
+	 * 
+ * + *
can be written using this enum as:
+ * + *
+	 * NumericShaper.getContextualShaper(EnumSet.of(NumericShaper.Range.ARABIC, NumericShaper.Range.TAMIL),
+	 * 		NumericShaper.Range.EUROPEAN);
+	 * 
+ * + *
+ * + * @since 1.7 + */ + public static enum Range { + // The order of EUROPEAN to MOGOLIAN must be consistent + // with the bitmask-based constants. + /** + * The Latin (European) range with the Latin (ASCII) digits. + */ + EUROPEAN('\u0030', '\u0000', '\u0300'), + /** + * The Arabic range with the Arabic-Indic digits. + */ + ARABIC('\u0660', '\u0600', '\u0780'), + /** + * The Arabic range with the Eastern Arabic-Indic digits. + */ + EASTERN_ARABIC('\u06f0', '\u0600', '\u0780'), + /** + * The Devanagari range with the Devanagari digits. + */ + DEVANAGARI('\u0966', '\u0900', '\u0980'), + /** + * The Bengali range with the Bengali digits. + */ + BENGALI('\u09e6', '\u0980', '\u0a00'), + /** + * The Gurmukhi range with the Gurmukhi digits. + */ + GURMUKHI('\u0a66', '\u0a00', '\u0a80'), + /** + * The Gujarati range with the Gujarati digits. + */ + GUJARATI('\u0ae6', '\u0b00', '\u0b80'), + /** + * The Oriya range with the Oriya digits. + */ + ORIYA('\u0b66', '\u0b00', '\u0b80'), + /** + * The Tamil range with the Tamil digits. + */ + TAMIL('\u0be6', '\u0b80', '\u0c00'), + /** + * The Telugu range with the Telugu digits. + */ + TELUGU('\u0c66', '\u0c00', '\u0c80'), + /** + * The Kannada range with the Kannada digits. + */ + KANNADA('\u0ce6', '\u0c80', '\u0d00'), + /** + * The Malayalam range with the Malayalam digits. + */ + MALAYALAM('\u0d66', '\u0d00', '\u0d80'), + /** + * The Thai range with the Thai digits. + */ + THAI('\u0e50', '\u0e00', '\u0e80'), + /** + * The Lao range with the Lao digits. + */ + LAO('\u0ed0', '\u0e80', '\u0f00'), + /** + * The Tibetan range with the Tibetan digits. + */ + TIBETAN('\u0f20', '\u0f00', '\u1000'), + /** + * The Myanmar range with the Myanmar digits. + */ + MYANMAR('\u1040', '\u1000', '\u1080'), + /** + * The Ethiopic range with the Ethiopic digits. Ethiopic does not have a decimal + * digit 0 so Latin (European) 0 is used. + */ + ETHIOPIC('\u1369', '\u1200', '\u1380') { + @Override + char getNumericBase() { + return 1; + } + }, + /** + * The Khmer range with the Khmer digits. + */ + KHMER('\u17e0', '\u1780', '\u1800'), + /** + * The Mongolian range with the Mongolian digits. + */ + MONGOLIAN('\u1810', '\u1800', '\u1900'), + // The order of EUROPEAN to MOGOLIAN must be consistent + // with the bitmask-based constants. + + /** + * The N'Ko range with the N'Ko digits. + */ + NKO('\u07c0', '\u07c0', '\u0800'), + /** + * The Myanmar range with the Myanmar Shan digits. + */ + MYANMAR_SHAN('\u1090', '\u1000', '\u10a0'), + /** + * The Limbu range with the Limbu digits. + */ + LIMBU('\u1946', '\u1900', '\u1950'), + /** + * The New Tai Lue range with the New Tai Lue digits. + */ + NEW_TAI_LUE('\u19d0', '\u1980', '\u19e0'), + /** + * The Balinese range with the Balinese digits. + */ + BALINESE('\u1b50', '\u1b00', '\u1b80'), + /** + * The Sundanese range with the Sundanese digits. + */ + SUNDANESE('\u1bb0', '\u1b80', '\u1bc0'), + /** + * The Lepcha range with the Lepcha digits. + */ + LEPCHA('\u1c40', '\u1c00', '\u1c50'), + /** + * The Ol Chiki range with the Ol Chiki digits. + */ + OL_CHIKI('\u1c50', '\u1c50', '\u1c80'), + /** + * The Vai range with the Vai digits. + */ + VAI('\ua620', '\ua500', '\ua640'), + /** + * The Saurashtra range with the Saurashtra digits. + */ + SAURASHTRA('\ua8d0', '\ua880', '\ua8e0'), + /** + * The Kayah Li range with the Kayah Li digits. + */ + KAYAH_LI('\ua900', '\ua900', '\ua930'), + /** + * The Cham range with the Cham digits. + */ + CHAM('\uaa50', '\uaa00', '\uaa60'), + /** + * The Tai Tham Hora range with the Tai Tham Hora digits. + */ + TAI_THAM_HORA('\u1a80', '\u1a20', '\u1ab0'), + /** + * The Tai Tham Tham range with the Tai Tham Tham digits. + */ + TAI_THAM_THAM('\u1a90', '\u1a20', '\u1ab0'), + /** + * The Javanese range with the Javanese digits. + */ + JAVANESE('\ua9d0', '\ua980', '\ua9e0'), + /** + * The Meetei Mayek range with the Meetei Mayek digits. + */ + MEETEI_MAYEK('\uabf0', '\uabc0', '\uac00'), + /** + * The Sinhala range with the Sinhala digits. + * + * @since 9 + */ + SINHALA('\u0de6', '\u0d80', '\u0e00'), + /** + * The Myanmar Extended-B range with the Myanmar Tai Laing digits. + * + * @since 9 + */ + MYANMAR_TAI_LAING('\ua9f0', '\ua9e0', '\uaa00'); + + private static int toRangeIndex(Range script) { + int index = script.ordinal(); + return index < NUM_KEYS ? index : -1; + } + + private static Range indexToRange(int index) { + return index < NUM_KEYS ? Range.values()[index] : null; + } + + private static int toRangeMask(Set ranges) { + int m = 0; + for (Range range : ranges) { + int index = range.ordinal(); + if (index < NUM_KEYS) { + m |= 1 << index; + } + } + return m; + } + + private static Set maskToRangeSet(int mask) { + Set set = EnumSet.noneOf(Range.class); + Range[] a = Range.values(); + for (int i = 0; i < NUM_KEYS; i++) { + if ((mask & (1 << i)) != 0) { + set.add(a[i]); + } + } + return set; + } + + // base character of range digits + private final int base; + // Unicode range + private final int start, // inclusive + end; // exclusive + + private Range(int base, int start, int end) { + this.base = base - ('0' + getNumericBase()); + this.start = start; + this.end = end; + } + + private int getDigitBase() { + return base; + } + + char getNumericBase() { + return 0; + } + + private boolean inRange(int c) { + return start <= c && c < end; + } + } + + /** index of context for contextual shaping - values range from 0 to 18 */ + private int key; + + /** + * flag indicating whether to shape contextually (high bit) and which digit + * ranges to shape (bits 0-18) + */ + private int mask; + + /** + * The context {@code Range} for contextual shaping or the {@code + * Range} for non-contextual shaping. {@code null} for the bit mask-based API. + * + * @since 1.7 + */ + private Range shapingRange; + + /** + * {@code Set} indicating which Unicode ranges to shape. {@code null} for + * the bit mask-based API. + */ + private transient Set rangeSet; + + /** + * rangeSet.toArray() value. Sorted by Range.base when the number of elements is + * greater than BSEARCH_THRESHOLD. + */ + private transient Range[] rangeArray; + + /** + * If more than BSEARCH_THRESHOLD ranges are specified, binary search is used. + */ + private static final int BSEARCH_THRESHOLD = 3; + + /** + * Use serialVersionUID from JDK 1.7 for interoperability. + */ + private static final long serialVersionUID = -8022764705923730308L; + + /** + * Identifies the Latin-1 (European) and extended range, and Latin-1 (European) + * decimal base. + */ + public static final int EUROPEAN = 1 << 0; + + /** Identifies the ARABIC range and decimal base. */ + public static final int ARABIC = 1 << 1; + + /** Identifies the ARABIC range and ARABIC_EXTENDED decimal base. */ + public static final int EASTERN_ARABIC = 1 << 2; + + /** Identifies the DEVANAGARI range and decimal base. */ + public static final int DEVANAGARI = 1 << 3; + + /** Identifies the BENGALI range and decimal base. */ + public static final int BENGALI = 1 << 4; + + /** Identifies the GURMUKHI range and decimal base. */ + public static final int GURMUKHI = 1 << 5; + + /** Identifies the GUJARATI range and decimal base. */ + public static final int GUJARATI = 1 << 6; + + /** Identifies the ORIYA range and decimal base. */ + public static final int ORIYA = 1 << 7; + + /** Identifies the TAMIL range and decimal base. */ + // TAMIL DIGIT ZERO was added in Unicode 4.1 + public static final int TAMIL = 1 << 8; + + /** Identifies the TELUGU range and decimal base. */ + public static final int TELUGU = 1 << 9; + + /** Identifies the KANNADA range and decimal base. */ + public static final int KANNADA = 1 << 10; + + /** Identifies the MALAYALAM range and decimal base. */ + public static final int MALAYALAM = 1 << 11; + + /** Identifies the THAI range and decimal base. */ + public static final int THAI = 1 << 12; + + /** Identifies the LAO range and decimal base. */ + public static final int LAO = 1 << 13; + + /** Identifies the TIBETAN range and decimal base. */ + public static final int TIBETAN = 1 << 14; + + /** Identifies the MYANMAR range and decimal base. */ + public static final int MYANMAR = 1 << 15; + + /** Identifies the ETHIOPIC range and decimal base. */ + public static final int ETHIOPIC = 1 << 16; + + /** Identifies the KHMER range and decimal base. */ + public static final int KHMER = 1 << 17; + + /** Identifies the MONGOLIAN range and decimal base. */ + public static final int MONGOLIAN = 1 << 18; + + /** + * Identifies all ranges, for full contextual shaping. + * + *

+ * This constant specifies all of the bit mask-based ranges. Use + * {@code EnumSet.allOf(NumericShaper.Range.class)} to specify all of the + * enum-based ranges. + */ + public static final int ALL_RANGES = 0x0007ffff; + + private static final int EUROPEAN_KEY = 0; + private static final int ARABIC_KEY = 1; + private static final int EASTERN_ARABIC_KEY = 2; + private static final int DEVANAGARI_KEY = 3; + private static final int BENGALI_KEY = 4; + private static final int GURMUKHI_KEY = 5; + private static final int GUJARATI_KEY = 6; + private static final int ORIYA_KEY = 7; + private static final int TAMIL_KEY = 8; + private static final int TELUGU_KEY = 9; + private static final int KANNADA_KEY = 10; + private static final int MALAYALAM_KEY = 11; + private static final int THAI_KEY = 12; + private static final int LAO_KEY = 13; + private static final int TIBETAN_KEY = 14; + private static final int MYANMAR_KEY = 15; + private static final int ETHIOPIC_KEY = 16; + private static final int KHMER_KEY = 17; + private static final int MONGOLIAN_KEY = 18; + + private static final int NUM_KEYS = MONGOLIAN_KEY + 1; // fixed + + private static final int CONTEXTUAL_MASK = 1 << 31; + + private static final char[] bases = { '\u0030' - '\u0030', // EUROPEAN + '\u0660' - '\u0030', // ARABIC-INDIC + '\u06f0' - '\u0030', // EXTENDED ARABIC-INDIC (EASTERN_ARABIC) + '\u0966' - '\u0030', // DEVANAGARI + '\u09e6' - '\u0030', // BENGALI + '\u0a66' - '\u0030', // GURMUKHI + '\u0ae6' - '\u0030', // GUJARATI + '\u0b66' - '\u0030', // ORIYA + '\u0be6' - '\u0030', // TAMIL - zero was added in Unicode 4.1 + '\u0c66' - '\u0030', // TELUGU + '\u0ce6' - '\u0030', // KANNADA + '\u0d66' - '\u0030', // MALAYALAM + '\u0e50' - '\u0030', // THAI + '\u0ed0' - '\u0030', // LAO + '\u0f20' - '\u0030', // TIBETAN + '\u1040' - '\u0030', // MYANMAR + '\u1369' - '\u0031', // ETHIOPIC - no zero + '\u17e0' - '\u0030', // KHMER + '\u1810' - '\u0030', // MONGOLIAN + }; + + // some ranges adjoin or overlap, rethink if we want to do a binary search on + // this + + private static final char[] contexts = { '\u0000', '\u0300', // 'EUROPEAN' (really latin-1 and extended) + '\u0600', '\u0780', // ARABIC + '\u0600', '\u0780', // EASTERN_ARABIC -- note overlap with arabic + '\u0900', '\u0980', // DEVANAGARI + '\u0980', '\u0a00', // BENGALI + '\u0a00', '\u0a80', // GURMUKHI + '\u0a80', '\u0b00', // GUJARATI + '\u0b00', '\u0b80', // ORIYA + '\u0b80', '\u0c00', // TAMIL + '\u0c00', '\u0c80', // TELUGU + '\u0c80', '\u0d00', // KANNADA + '\u0d00', '\u0d80', // MALAYALAM + '\u0e00', '\u0e80', // THAI + '\u0e80', '\u0f00', // LAO + '\u0f00', '\u1000', // TIBETAN + '\u1000', '\u1080', // MYANMAR + '\u1200', '\u1380', // ETHIOPIC - note missing zero + '\u1780', '\u1800', // KHMER + '\u1800', '\u1900', // MONGOLIAN + '\uffff', }; + + // assume most characters are near each other so probing the cache is + // infrequent, + // and a linear probe is ok. + + private static int ctCache = 0; + private static int ctCacheLimit = contexts.length - 2; + + // warning, synchronize access to this as it modifies state + private static int getContextKey(char c) { + if (c < contexts[ctCache]) { + while (ctCache > 0 && c < contexts[ctCache]) + --ctCache; + } else if (c >= contexts[ctCache + 1]) { + while (ctCache < ctCacheLimit && c >= contexts[ctCache + 1]) + ++ctCache; + } + + // if we're not in a known range, then return EUROPEAN as the range key + return (ctCache & 0x1) == 0 ? (ctCache / 2) : EUROPEAN_KEY; + } + + // cache for the NumericShaper.Range version + private transient volatile Range currentRange = Range.EUROPEAN; + + private Range rangeForCodePoint(final int codepoint) { + if (currentRange.inRange(codepoint)) { + return currentRange; + } + + final Range[] ranges = rangeArray; + if (ranges.length > BSEARCH_THRESHOLD) { + int lo = 0; + int hi = ranges.length - 1; + while (lo <= hi) { + int mid = (lo + hi) / 2; + Range range = ranges[mid]; + if (codepoint < range.start) { + hi = mid - 1; + } else if (codepoint >= range.end) { + lo = mid + 1; + } else { + currentRange = range; + return range; + } + } + } else { + for (int i = 0; i < ranges.length; i++) { + if (ranges[i].inRange(codepoint)) { + return ranges[i]; + } + } + } + return Range.EUROPEAN; + } + + /* + * A range table of strong directional characters (types L, R, AL). Even (left) + * indexes are starts of ranges of non-strong-directional (or undefined) + * characters, odd (right) indexes are starts of ranges of strong directional + * characters. + */ + private static int[] strongTable = { 0x0000, 0x0041, 0x005b, 0x0061, 0x007b, 0x00aa, 0x00ab, 0x00b5, 0x00b6, 0x00ba, + 0x00bb, 0x00c0, 0x00d7, 0x00d8, 0x00f7, 0x00f8, 0x02b9, 0x02bb, 0x02c2, 0x02d0, 0x02d2, 0x02e0, 0x02e5, + 0x02ee, 0x02ef, 0x0370, 0x0374, 0x0376, 0x0378, 0x037a, 0x037e, 0x037f, 0x0380, 0x0386, 0x0387, 0x0388, + 0x038b, 0x038c, 0x038d, 0x038e, 0x03a2, 0x03a3, 0x03f6, 0x03f7, 0x0483, 0x048a, 0x0530, 0x0531, 0x0557, + 0x0559, 0x058a, 0x0590, 0x0591, 0x05be, 0x05bf, 0x05c0, 0x05c1, 0x05c3, 0x05c4, 0x05c6, 0x05c7, 0x05c8, + 0x0600, 0x0608, 0x0609, 0x060b, 0x060c, 0x060d, 0x060e, 0x061b, 0x064b, 0x066d, 0x0670, 0x0671, 0x06d6, + 0x06e5, 0x06e7, 0x06ee, 0x06f0, 0x06fa, 0x0711, 0x0712, 0x0730, 0x074b, 0x07a6, 0x07b1, 0x07eb, 0x07f4, + 0x07f6, 0x07fa, 0x07fd, 0x07fe, 0x0816, 0x081a, 0x081b, 0x0824, 0x0825, 0x0828, 0x0829, 0x082e, 0x0859, + 0x085c, 0x08e3, 0x0903, 0x093a, 0x093b, 0x093c, 0x093d, 0x0941, 0x0949, 0x094d, 0x094e, 0x0951, 0x0958, + 0x0962, 0x0964, 0x0981, 0x0982, 0x0984, 0x0985, 0x098d, 0x098f, 0x0991, 0x0993, 0x09a9, 0x09aa, 0x09b1, + 0x09b2, 0x09b3, 0x09b6, 0x09ba, 0x09bd, 0x09c1, 0x09c7, 0x09c9, 0x09cb, 0x09cd, 0x09ce, 0x09cf, 0x09d7, + 0x09d8, 0x09dc, 0x09de, 0x09df, 0x09e2, 0x09e6, 0x09f2, 0x09f4, 0x09fb, 0x09fc, 0x09fe, 0x0a03, 0x0a04, + 0x0a05, 0x0a0b, 0x0a0f, 0x0a11, 0x0a13, 0x0a29, 0x0a2a, 0x0a31, 0x0a32, 0x0a34, 0x0a35, 0x0a37, 0x0a38, + 0x0a3a, 0x0a3e, 0x0a41, 0x0a59, 0x0a5d, 0x0a5e, 0x0a5f, 0x0a66, 0x0a70, 0x0a72, 0x0a75, 0x0a76, 0x0a73, + 0x0a83, 0x0a84, 0x0a85, 0x0a8e, 0x0a8f, 0x0a92, 0x0a93, 0x0aa9, 0x0aaa, 0x0ab1, 0x0ab2, 0x0ab4, 0x0ab5, + 0x0aba, 0x0abd, 0x0ac1, 0x0ac9, 0x0aca, 0x0acb, 0x0acd, 0x0ad0, 0x0ad1, 0x0ae0, 0x0ae2, 0x0ae6, 0x0af1, + 0x0af9, 0x0afa, 0x0b02, 0x0b04, 0x0b05, 0x0b0d, 0x0b0f, 0x0b11, 0x0b13, 0x0b29, 0x0b2a, 0x0b31, 0x0b32, + 0x0b34, 0x0b35, 0x0b3a, 0x0b3d, 0x0b3f, 0x0b40, 0x0b41, 0x0b47, 0x0b49, 0x0b4b, 0x0b4d, 0x0b57, 0x0b58, + 0x0b5c, 0x0b5e, 0x0b5f, 0x0b62, 0x0b66, 0x0b78, 0x0b83, 0x0b84, 0x0b85, 0x0b8b, 0x0b8e, 0x0b91, 0x0b92, + 0x0b96, 0x0b99, 0x0b9b, 0x0b9c, 0x0b9d, 0x0b9e, 0x0ba0, 0x0ba3, 0x0ba5, 0x0ba8, 0x0bab, 0x0bae, 0x0bba, + 0x0bbe, 0x0bc0, 0x0bc1, 0x0bc3, 0x0bc6, 0x0bc9, 0x0bca, 0x0bcd, 0x0bd0, 0x0bd1, 0x0bd7, 0x0bd8, 0x0be6, + 0x0bf3, 0x0c01, 0x0c04, 0x0c05, 0x0c0d, 0x0c0e, 0x0c11, 0x0c12, 0x0c29, 0x0c2a, 0x0c3a, 0x0c3d, 0x0c3e, + 0x0c41, 0x0c45, 0x0c58, 0x0c5b, 0x0c60, 0x0c62, 0x0c66, 0x0c70, 0x0c7f, 0x0c81, 0x0c82, 0x0c8d, 0x0c8e, + 0x0c91, 0x0c92, 0x0ca9, 0x0caa, 0x0cb4, 0x0cb5, 0x0cba, 0x0cbd, 0x0cc5, 0x0cc6, 0x0cc9, 0x0cca, 0x0ccc, + 0x0cd5, 0x0cd7, 0x0cde, 0x0cdf, 0x0ce0, 0x0ce2, 0x0ce6, 0x0cf0, 0x0cf1, 0x0cf3, 0x0d02, 0x0d04, 0x0d05, + 0x0d0d, 0x0d0e, 0x0d11, 0x0d12, 0x0d3b, 0x0d3d, 0x0d41, 0x0d46, 0x0d49, 0x0d4a, 0x0d4d, 0x0d4e, 0x0d62, + 0x0d66, 0x0d80, 0x0d82, 0x0d84, 0x0d85, 0x0d97, 0x0d9a, 0x0db2, 0x0db3, 0x0dbc, 0x0dbd, 0x0dbe, 0x0dc0, + 0x0dc7, 0x0dcf, 0x0dd2, 0x0dd8, 0x0de0, 0x0de6, 0x0df0, 0x0df2, 0x0df5, 0x0e01, 0x0e31, 0x0e32, 0x0e34, + 0x0e40, 0x0e47, 0x0e4f, 0x0e5c, 0x0e81, 0x0e83, 0x0e84, 0x0e85, 0x0e87, 0x0e89, 0x0e8a, 0x0e8b, 0x0e8d, + 0x0e8e, 0x0e94, 0x0e98, 0x0e99, 0x0ea0, 0x0ea1, 0x0ea4, 0x0ea5, 0x0ea6, 0x0ea7, 0x0ea8, 0x0eaa, 0x0eac, + 0x0ead, 0x0eb1, 0x0eb2, 0x0eb4, 0x0ebd, 0x0ebe, 0x0ec0, 0x0ec5, 0x0ec6, 0x0ec7, 0x0ed0, 0x0eda, 0x0edc, + 0x0ee0, 0x0f00, 0x0f18, 0x0f1a, 0x0f35, 0x0f36, 0x0f37, 0x0f38, 0x0f39, 0x0f3e, 0x0f48, 0x0f49, 0x0f6d, + 0x0f7f, 0x0f80, 0x0f85, 0x0f86, 0x0f88, 0x0f8d, 0x0fbe, 0x0fc6, 0x0fc7, 0x0fcd, 0x0fce, 0x0fdb, 0x1000, + 0x102d, 0x1031, 0x1032, 0x1038, 0x1039, 0x103b, 0x103d, 0x103f, 0x1058, 0x105a, 0x105e, 0x1061, 0x1071, + 0x1075, 0x1082, 0x1083, 0x1085, 0x1087, 0x108d, 0x108e, 0x109d, 0x109e, 0x10c6, 0x10c7, 0x10c8, 0x10cd, + 0x10ce, 0x10d0, 0x1249, 0x124a, 0x124e, 0x1250, 0x1257, 0x1258, 0x1259, 0x125a, 0x125e, 0x1260, 0x1289, + 0x128a, 0x128e, 0x1290, 0x12b1, 0x12b2, 0x12b6, 0x12b8, 0x12bf, 0x12c0, 0x12c1, 0x12c2, 0x12c6, 0x12c8, + 0x12d7, 0x12d8, 0x1311, 0x1312, 0x1316, 0x1318, 0x135b, 0x1360, 0x137d, 0x1380, 0x1390, 0x13a0, 0x13f6, + 0x13f8, 0x13fe, 0x1401, 0x1680, 0x1681, 0x169b, 0x16a0, 0x16f9, 0x1700, 0x170d, 0x170e, 0x1712, 0x1720, + 0x1732, 0x1735, 0x1737, 0x1740, 0x1752, 0x1760, 0x176d, 0x176e, 0x1771, 0x1780, 0x17b4, 0x17b6, 0x17b7, + 0x17be, 0x17c6, 0x17c7, 0x17c9, 0x17d4, 0x17db, 0x17dc, 0x17dd, 0x17e0, 0x17ea, 0x1810, 0x181a, 0x1820, + 0x1879, 0x1884, 0x1885, 0x1887, 0x18a9, 0x18aa, 0x18ab, 0x18b0, 0x18f6, 0x1900, 0x191f, 0x1923, 0x1927, + 0x1929, 0x192c, 0x1930, 0x1932, 0x1933, 0x1939, 0x1946, 0x196e, 0x1970, 0x1975, 0x1980, 0x19ac, 0x19b0, + 0x19ca, 0x19d0, 0x19db, 0x1a00, 0x1a17, 0x1a19, 0x1a1b, 0x1a1e, 0x1a56, 0x1a57, 0x1a58, 0x1a61, 0x1a62, + 0x1a63, 0x1a65, 0x1a6d, 0x1a73, 0x1a80, 0x1a8a, 0x1a90, 0x1a9a, 0x1aa0, 0x1aae, 0x1b04, 0x1b34, 0x1b35, + 0x1b36, 0x1b3b, 0x1b3c, 0x1b3d, 0x1b42, 0x1b43, 0x1b4c, 0x1b50, 0x1b6b, 0x1b74, 0x1b7d, 0x1b82, 0x1ba2, + 0x1ba6, 0x1ba8, 0x1baa, 0x1bab, 0x1bae, 0x1be6, 0x1be7, 0x1be8, 0x1bea, 0x1bed, 0x1bee, 0x1bef, 0x1bf2, + 0x1bf4, 0x1bfc, 0x1c2c, 0x1c34, 0x1c36, 0x1c3b, 0x1c4a, 0x1c4d, 0x1c89, 0x1c90, 0x1cbb, 0x1cbd, 0x1cc8, + 0x1cd3, 0x1cd4, 0x1ce1, 0x1ce2, 0x1ce9, 0x1ced, 0x1cee, 0x1cf4, 0x1cf5, 0x1cf8, 0x1d00, 0x1dc0, 0x1e00, + 0x1f16, 0x1f18, 0x1f1e, 0x1f20, 0x1f46, 0x1f48, 0x1f4e, 0x1f50, 0x1f58, 0x1f59, 0x1f5a, 0x1f5b, 0x1f5c, + 0x1f5d, 0x1f5e, 0x1f5f, 0x1f7e, 0x1f80, 0x1fb5, 0x1fb6, 0x1fbd, 0x1fbe, 0x1fbf, 0x1fc2, 0x1fc5, 0x1fc6, + 0x1fcd, 0x1fd0, 0x1fd4, 0x1fd6, 0x1fdc, 0x1fe0, 0x1fed, 0x1ff2, 0x1ff5, 0x1ff6, 0x1ffd, 0x200e, 0x2010, + 0x2071, 0x2072, 0x207f, 0x2080, 0x2090, 0x209d, 0x2102, 0x2103, 0x2107, 0x2108, 0x210a, 0x2114, 0x2115, + 0x2116, 0x2119, 0x211e, 0x2124, 0x2125, 0x2126, 0x2127, 0x2128, 0x2129, 0x212a, 0x212e, 0x212f, 0x213a, + 0x213c, 0x2140, 0x2145, 0x214a, 0x214e, 0x2150, 0x2160, 0x2189, 0x2336, 0x237b, 0x2395, 0x2396, 0x249c, + 0x24ea, 0x26ac, 0x26ad, 0x2800, 0x2900, 0x2c00, 0x2c2f, 0x2c30, 0x2c5f, 0x2c60, 0x2ce5, 0x2ceb, 0x2cef, + 0x2cf2, 0x2cf4, 0x2d00, 0x2d26, 0x2d27, 0x2d28, 0x2d2d, 0x2d2e, 0x2d30, 0x2d68, 0x2d6f, 0x2d71, 0x2d80, + 0x2d97, 0x2da0, 0x2da7, 0x2da8, 0x2daf, 0x2db0, 0x2db7, 0x2db8, 0x2dbf, 0x2dc0, 0x2dc7, 0x2dc8, 0x2dcf, + 0x2dd0, 0x2dd7, 0x2dd8, 0x2ddf, 0x3005, 0x3008, 0x3021, 0x302a, 0x302e, 0x3030, 0x3031, 0x3036, 0x3038, + 0x303d, 0x3041, 0x3097, 0x309d, 0x30a0, 0x30a1, 0x30fb, 0x30fc, 0x3100, 0x3105, 0x3130, 0x3131, 0x318f, + 0x3190, 0x31bb, 0x31f0, 0x321d, 0x3220, 0x3250, 0x3260, 0x327c, 0x327f, 0x32b1, 0x32c0, 0x32cc, 0x32d0, + 0x32ff, 0x3300, 0x3377, 0x337b, 0x33de, 0x33e0, 0x33ff, 0x3400, 0x4db6, 0x4e00, 0x9ff0, 0xa000, 0xa48d, + 0xa4d0, 0xa60d, 0xa610, 0xa62c, 0xa640, 0xa66f, 0xa680, 0xa69e, 0xa6a0, 0xa6f0, 0xa6f2, 0xa6f8, 0xa722, + 0xa788, 0xa789, 0xa7ba, 0xa7f7, 0xa802, 0xa803, 0xa806, 0xa807, 0xa80b, 0xa80c, 0xa825, 0xa827, 0xa828, + 0xa830, 0xa838, 0xa840, 0xa874, 0xa880, 0xa8c4, 0xa8ce, 0xa8da, 0xa8f2, 0xa8ff, 0xa900, 0xa926, 0xa92e, + 0xa947, 0xa952, 0xa954, 0xa95f, 0xa97d, 0xa983, 0xa9b3, 0xa9b4, 0xa9b6, 0xa9ba, 0xa9bc, 0xa9bd, 0xa9ce, + 0xa9cf, 0xa9da, 0xa9de, 0xa9e5, 0xa9e6, 0xa9ff, 0xaa00, 0xaa29, 0xaa2f, 0xaa31, 0xaa33, 0xaa35, 0xaa40, + 0xaa43, 0xaa44, 0xaa4c, 0xaa4d, 0xaa4e, 0xaa50, 0xaa5a, 0xaa5c, 0xaa7c, 0xaa7d, 0xaab0, 0xaab1, 0xaab2, + 0xaab5, 0xaab7, 0xaab9, 0xaabe, 0xaac0, 0xaac1, 0xaac2, 0xaac3, 0xaadb, 0xaaec, 0xaaee, 0xaaf6, 0xab01, + 0xab07, 0xab09, 0xab0f, 0xab11, 0xab17, 0xab20, 0xab27, 0xab28, 0xab2f, 0xab30, 0xab66, 0xab70, 0xabe5, + 0xabe6, 0xabe8, 0xabe9, 0xabed, 0xabf0, 0xabfa, 0xac00, 0xd7a4, 0xd7b0, 0xd7c7, 0xd7cb, 0xd7fc, 0xe000, + 0xfa6e, 0xfa70, 0xfada, 0xfb00, 0xfb07, 0xfb13, 0xfb18, 0xfb1d, 0xfb1e, 0xfb1f, 0xfb29, 0xfb2a, 0xfd3e, + 0xfd40, 0xfdd0, 0xfdf0, 0xfdfd, 0xfdfe, 0xfe00, 0xfe70, 0xfeff, 0xff21, 0xff3b, 0xff41, 0xff5b, 0xff66, + 0xffbf, 0xffc2, 0xffc8, 0xffca, 0xffd0, 0xffd2, 0xffd8, 0xffda, 0xffdd, 0x10000, 0x1000c, 0x1000d, 0x10027, + 0x10028, 0x1003b, 0x1003c, 0x1003e, 0x1003f, 0x1004e, 0x10050, 0x1005e, 0x10080, 0x100fb, 0x10100, 0x10101, + 0x10102, 0x10103, 0x10107, 0x10134, 0x10137, 0x10140, 0x1018d, 0x1018f, 0x101d0, 0x101fd, 0x10280, 0x1029d, + 0x102a0, 0x102d1, 0x10300, 0x10324, 0x1032d, 0x1034b, 0x10350, 0x10376, 0x10380, 0x1039e, 0x1039f, 0x103c4, + 0x103c8, 0x103d6, 0x10400, 0x1049e, 0x104a0, 0x104aa, 0x104d3, 0x104d4, 0x104d8, 0x104fc, 0x10500, 0x10528, + 0x10530, 0x10564, 0x1056f, 0x10570, 0x10600, 0x10737, 0x10740, 0x10756, 0x10760, 0x10768, 0x10800, 0x1091f, + 0x10920, 0x10a01, 0x10a04, 0x10a05, 0x10a07, 0x10a0c, 0x10a10, 0x10a38, 0x10a3b, 0x10a3f, 0x10a40, 0x10ae5, + 0x10ae7, 0x10b39, 0x10b40, 0x10d00, 0x10d40, 0x10e60, 0x10e7f, 0x10f30, 0x10f70, 0x11001, 0x11002, 0x11038, + 0x11047, 0x1104e, 0x11066, 0x11070, 0x11082, 0x110b3, 0x110b7, 0x110b9, 0x110bb, 0x110c2, 0x110cd, 0x110ce, + 0x110d0, 0x110e9, 0x110f0, 0x110fa, 0x11103, 0x11127, 0x1112c, 0x1112d, 0x11136, 0x11147, 0x11150, 0x11173, + 0x11174, 0x11177, 0x11182, 0x111b6, 0x111bf, 0x111c9, 0x111cd, 0x111ce, 0x111d0, 0x111e0, 0x111e1, 0x111f5, + 0x11200, 0x11212, 0x11213, 0x1122f, 0x11232, 0x11234, 0x11235, 0x11236, 0x11238, 0x1123e, 0x11280, 0x11287, + 0x11288, 0x11289, 0x1128a, 0x1128e, 0x1128f, 0x1129e, 0x1129f, 0x112aa, 0x112b0, 0x112df, 0x112e0, 0x112e3, + 0x112f0, 0x112fa, 0x11302, 0x11304, 0x11305, 0x1130d, 0x1130f, 0x11311, 0x11313, 0x11329, 0x1132a, 0x11331, + 0x11332, 0x11334, 0x11335, 0x1133a, 0x1133d, 0x11340, 0x11341, 0x11345, 0x11347, 0x11349, 0x1134b, 0x1134e, + 0x11350, 0x11351, 0x11357, 0x11358, 0x1135d, 0x11364, 0x11400, 0x11438, 0x11440, 0x11442, 0x11445, 0x11446, + 0x11447, 0x1145a, 0x1145b, 0x1145c, 0x1145d, 0x1145e, 0x11480, 0x114b3, 0x114b9, 0x114ba, 0x114bb, 0x114bf, + 0x114c1, 0x114c2, 0x114c4, 0x114c8, 0x114d0, 0x114da, 0x11580, 0x115b2, 0x115b8, 0x115bc, 0x115be, 0x115bf, + 0x115c1, 0x115dc, 0x11600, 0x11633, 0x1163b, 0x1163d, 0x1163e, 0x1163f, 0x11641, 0x11645, 0x11650, 0x1165a, + 0x11680, 0x116ab, 0x116ac, 0x116ad, 0x116ae, 0x116b0, 0x116b6, 0x116b7, 0x116c0, 0x116ca, 0x11700, 0x1171b, + 0x11720, 0x11722, 0x11726, 0x11727, 0x11730, 0x1182f, 0x11838, 0x11839, 0x1183b, 0x1183c, 0x118a0, 0x118f3, + 0x118ff, 0x11900, 0x11a00, 0x11a01, 0x11a07, 0x11a09, 0x11a0b, 0x11a33, 0x11a3a, 0x11a3b, 0x11a3f, 0x11a47, + 0x11a50, 0x11a51, 0x11a57, 0x11a59, 0x11a5c, 0x11a84, 0x11a86, 0x11a8a, 0x11a97, 0x11a98, 0x11a9a, 0x11aa3, + 0x11ac0, 0x11af9, 0x11c00, 0x11c09, 0x11c0a, 0x11c30, 0x11c3e, 0x11c46, 0x11c50, 0x11c6d, 0x11c70, 0x11c90, + 0x11ca9, 0x11caa, 0x11cb1, 0x11cb2, 0x11cb4, 0x11cb5, 0x11d00, 0x11d07, 0x11d08, 0x11d0a, 0x11d0b, 0x11d31, + 0x11d46, 0x11d47, 0x11d50, 0x11d5a, 0x11d60, 0x11d66, 0x11d67, 0x11d69, 0x11d6a, 0x11d8f, 0x11d93, 0x11d95, + 0x11d96, 0x11d97, 0x11d98, 0x11d99, 0x11da0, 0x11daa, 0x11ee0, 0x11ef3, 0x11ef5, 0x11ef9, 0x12000, 0x1239a, + 0x12400, 0x1246f, 0x12470, 0x12475, 0x12480, 0x12544, 0x13000, 0x1342f, 0x14400, 0x14647, 0x16800, 0x16a39, + 0x16a40, 0x16a5f, 0x16a60, 0x16a6a, 0x16a6e, 0x16a70, 0x16ad0, 0x16aee, 0x16af5, 0x16af6, 0x16b00, 0x16b30, + 0x16b37, 0x16b46, 0x16b50, 0x16b5a, 0x16b5b, 0x16b62, 0x16b63, 0x16b78, 0x16b7d, 0x16b90, 0x16e40, 0x16e9b, + 0x16f00, 0x16f45, 0x16f50, 0x16f7f, 0x16f93, 0x16fa0, 0x16fe0, 0x16fe2, 0x17000, 0x187f2, 0x18800, 0x18af3, + 0x1b000, 0x1b11f, 0x1b170, 0x1b2fc, 0x1bc00, 0x1bc6b, 0x1bc70, 0x1bc7d, 0x1bc80, 0x1bc89, 0x1bc90, 0x1bc9a, + 0x1bc9c, 0x1bc9d, 0x1bc9f, 0x1bca0, 0x1d000, 0x1d0f6, 0x1d100, 0x1d127, 0x1d129, 0x1d167, 0x1d16a, 0x1d173, + 0x1d183, 0x1d185, 0x1d18c, 0x1d1aa, 0x1d1ae, 0x1d1e9, 0x1d2e0, 0x1d2f4, 0x1d360, 0x1d379, 0x1d400, 0x1d455, + 0x1d456, 0x1d49d, 0x1d49e, 0x1d4a0, 0x1d4a2, 0x1d4a3, 0x1d4a5, 0x1d4a7, 0x1d4a9, 0x1d4ad, 0x1d4ae, 0x1d4ba, + 0x1d4bb, 0x1d4bc, 0x1d4bd, 0x1d4c4, 0x1d4c5, 0x1d506, 0x1d507, 0x1d50b, 0x1d50d, 0x1d515, 0x1d516, 0x1d51d, + 0x1d51e, 0x1d53a, 0x1d53b, 0x1d53f, 0x1d540, 0x1d545, 0x1d546, 0x1d547, 0x1d54a, 0x1d551, 0x1d552, 0x1d6a6, + 0x1d6a8, 0x1d6db, 0x1d6dc, 0x1d715, 0x1d716, 0x1d74f, 0x1d750, 0x1d789, 0x1d78a, 0x1d7c3, 0x1d7c4, 0x1d7cc, + 0x1d800, 0x1da00, 0x1da37, 0x1da3b, 0x1da6d, 0x1da75, 0x1da76, 0x1da84, 0x1da85, 0x1da8c, 0x1e800, 0x1e8d0, + 0x1e8d7, 0x1e944, 0x1e94b, 0x1ec70, 0x1ecc0, 0x1ee00, 0x1ef00, 0x1f000, 0x1f110, 0x1f12f, 0x1f130, 0x1f16a, + 0x1f170, 0x1f1ad, 0x1f1e6, 0x1f203, 0x1f210, 0x1f23c, 0x1f240, 0x1f249, 0x1f250, 0x1f252, 0x20000, 0x2a6d7, + 0x2a700, 0x2b735, 0x2b740, 0x2b81e, 0x2b820, 0x2cea2, 0x2ceb0, 0x2ebe1, 0x2f800, 0x2fa1e, 0xf0000, 0xffffe, + 0x100000, 0x10fffe, 0x10ffff // sentinel + }; + + // use a binary search with a cache + + private transient volatile int stCache = 0; + + private boolean isStrongDirectional(char c) { + int cachedIndex = stCache; + if (c < strongTable[cachedIndex]) { + cachedIndex = search(c, strongTable, 0, cachedIndex); + } else if (c >= strongTable[cachedIndex + 1]) { + cachedIndex = search(c, strongTable, cachedIndex + 1, strongTable.length - cachedIndex - 1); + } + boolean val = (cachedIndex & 0x1) == 1; + stCache = cachedIndex; + return val; + } + + private static int getKeyFromMask(int mask) { + int key = 0; + while (key < NUM_KEYS && ((mask & (1 << key)) == 0)) { + ++key; + } + if (key == NUM_KEYS || ((mask & ~(1 << key)) != 0)) { + throw new IllegalArgumentException("invalid shaper: " + Integer.toHexString(mask)); + } + return key; + } + + /** + * Returns a shaper for the provided unicode range. All Latin-1 (EUROPEAN) + * digits are converted to the corresponding decimal unicode digits. + * + * @param singleRange the specified Unicode range + * @return a non-contextual numeric shaper + * @throws IllegalArgumentException if the range is not a single range + */ + public static NumericShaper getShaper(int singleRange) { + int key = getKeyFromMask(singleRange); + return new NumericShaper(key, singleRange); + } + + /** + * Returns a shaper for the provided Unicode range. All Latin-1 (EUROPEAN) + * digits are converted to the corresponding decimal digits of the specified + * Unicode range. + * + * @param singleRange the Unicode range given by a {@link NumericShaper.Range} + * constant. + * @return a non-contextual {@code NumericShaper}. + * @throws NullPointerException if {@code singleRange} is {@code null} + * @since 1.7 + */ + public static NumericShaper getShaper(Range singleRange) { + return new NumericShaper(singleRange, EnumSet.of(singleRange)); + } + + /** + * Returns a contextual shaper for the provided unicode range(s). Latin-1 + * (EUROPEAN) digits are converted to the decimal digits corresponding to the + * range of the preceding text, if the range is one of the provided ranges. + * Multiple ranges are represented by or-ing the values together, such as, + * {@code NumericShaper.ARABIC | NumericShaper.THAI}. The shaper assumes + * EUROPEAN as the starting context, that is, if EUROPEAN digits are encountered + * before any strong directional text in the string, the context is presumed to + * be EUROPEAN, and so the digits will not shape. + * + * @param ranges the specified Unicode ranges + * @return a shaper for the specified ranges + */ + public static NumericShaper getContextualShaper(int ranges) { + ranges |= CONTEXTUAL_MASK; + return new NumericShaper(EUROPEAN_KEY, ranges); + } + + /** + * Returns a contextual shaper for the provided Unicode range(s). The Latin-1 + * (EUROPEAN) digits are converted to the decimal digits corresponding to the + * range of the preceding text, if the range is one of the provided ranges. + * + *

+ * The shaper assumes EUROPEAN as the starting context, that is, if EUROPEAN + * digits are encountered before any strong directional text in the string, the + * context is presumed to be EUROPEAN, and so the digits will not shape. + * + * @param ranges the specified Unicode ranges + * @return a contextual shaper for the specified ranges + * @throws NullPointerException if {@code ranges} is {@code null}. + * @since 1.7 + */ + public static NumericShaper getContextualShaper(Set ranges) { + NumericShaper shaper = new NumericShaper(Range.EUROPEAN, ranges); + shaper.mask = CONTEXTUAL_MASK; + return shaper; + } + + /** + * Returns a contextual shaper for the provided unicode range(s). Latin-1 + * (EUROPEAN) digits will be converted to the decimal digits corresponding to + * the range of the preceding text, if the range is one of the provided ranges. + * Multiple ranges are represented by or-ing the values together, for example, + * {@code NumericShaper.ARABIC | NumericShaper.THAI}. The shaper uses + * defaultContext as the starting context. + * + * @param ranges the specified Unicode ranges + * @param defaultContext the starting context, such as + * {@code NumericShaper.EUROPEAN} + * @return a shaper for the specified Unicode ranges. + * @throws IllegalArgumentException if the specified {@code defaultContext} is + * not a single valid range. + */ + public static NumericShaper getContextualShaper(int ranges, int defaultContext) { + int key = getKeyFromMask(defaultContext); + ranges |= CONTEXTUAL_MASK; + return new NumericShaper(key, ranges); + } + + /** + * Returns a contextual shaper for the provided Unicode range(s). The Latin-1 + * (EUROPEAN) digits will be converted to the decimal digits corresponding to + * the range of the preceding text, if the range is one of the provided ranges. + * The shaper uses {@code + * defaultContext} as the starting context. + * + * @param ranges the specified Unicode ranges + * @param defaultContext the starting context, such as + * {@code NumericShaper.Range.EUROPEAN} + * @return a contextual shaper for the specified Unicode ranges. + * @throws NullPointerException if {@code ranges} or {@code defaultContext} is + * {@code null} + * @since 1.7 + */ + public static NumericShaper getContextualShaper(Set ranges, Range defaultContext) { + if (defaultContext == null) { + throw new NullPointerException(); + } + NumericShaper shaper = new NumericShaper(defaultContext, ranges); + shaper.mask = CONTEXTUAL_MASK; + return shaper; + } + + /** + * Private constructor. + */ + private NumericShaper(int key, int mask) { + this.key = key; + this.mask = mask; + } + + private NumericShaper(Range defaultContext, Set ranges) { + shapingRange = defaultContext; + rangeSet = EnumSet.copyOf(ranges); // throws NPE if ranges is null. + + // Give precedence to EASTERN_ARABIC if both ARABIC and + // EASTERN_ARABIC are specified. + if (rangeSet.contains(Range.EASTERN_ARABIC) && rangeSet.contains(Range.ARABIC)) { + rangeSet.remove(Range.ARABIC); + } + + // As well as the above case, give precedence to TAI_THAM_THAM if both + // TAI_THAM_HORA and TAI_THAM_THAM are specified. + if (rangeSet.contains(Range.TAI_THAM_THAM) && rangeSet.contains(Range.TAI_THAM_HORA)) { + rangeSet.remove(Range.TAI_THAM_HORA); + } + + rangeArray = rangeSet.toArray(new Range[rangeSet.size()]); + if (rangeArray.length > BSEARCH_THRESHOLD) { + // sort rangeArray for binary search + Arrays.sort(rangeArray, new Comparator() { + public int compare(Range s1, Range s2) { + return s1.base > s2.base ? 1 : s1.base == s2.base ? 0 : -1; + } + }); + } + } + + /** + * Converts the digits in the text that occur between start and start + count. + * + * @param text an array of characters to convert + * @param start the index into {@code text} to start converting + * @param count the number of characters in {@code text} to convert + * @throws IndexOutOfBoundsException if start or start + count is out of bounds + * @throws NullPointerException if text is null + */ + public void shape(char[] text, int start, int count) { + checkParams(text, start, count); + if (isContextual()) { + if (rangeSet == null) { + shapeContextually(text, start, count, key); + } else { + shapeContextually(text, start, count, shapingRange); + } + } else { + shapeNonContextually(text, start, count); + } + } + + /** + * Converts the digits in the text that occur between start and start + count, + * using the provided context. Context is ignored if the shaper is not a + * contextual shaper. + * + * @param text an array of characters + * @param start the index into {@code text} to start converting + * @param count the number of characters in {@code text} to convert + * @param context the context to which to convert the characters, such as + * {@code NumericShaper.EUROPEAN} + * @throws IndexOutOfBoundsException if start or start + count is out of bounds + * @throws NullPointerException if text is null + * @throws IllegalArgumentException if this is a contextual shaper and the + * specified {@code context} is not a single + * valid range. + */ + public void shape(char[] text, int start, int count, int context) { + checkParams(text, start, count); + if (isContextual()) { + int ctxKey = getKeyFromMask(context); + if (rangeSet == null) { + shapeContextually(text, start, count, ctxKey); + } else { + shapeContextually(text, start, count, Range.values()[ctxKey]); + } + } else { + shapeNonContextually(text, start, count); + } + } + + /** + * Converts the digits in the text that occur between {@code + * start} and {@code start + count}, using the provided {@code + * context}. {@code Context} is ignored if the shaper is not a contextual + * shaper. + * + * @param text a {@code char} array + * @param start the index into {@code text} to start converting + * @param count the number of {@code char}s in {@code text} to convert + * @param context the context to which to convert the characters, such as + * {@code NumericShaper.Range.EUROPEAN} + * @throws IndexOutOfBoundsException if {@code start} or {@code start + count} + * is out of bounds + * @throws NullPointerException if {@code text} or {@code context} is null + * @since 1.7 + */ + public void shape(char[] text, int start, int count, Range context) { + checkParams(text, start, count); + if (context == null) { + throw new NullPointerException("context is null"); + } + + if (isContextual()) { + if (rangeSet != null) { + shapeContextually(text, start, count, context); + } else { + int key = Range.toRangeIndex(context); + if (key >= 0) { + shapeContextually(text, start, count, key); + } else { + shapeContextually(text, start, count, shapingRange); + } + } + } else { + shapeNonContextually(text, start, count); + } + } + + private void checkParams(char[] text, int start, int count) { + if (text == null) { + throw new NullPointerException("text is null"); + } + if ((start < 0) || (start > text.length) || ((start + count) < 0) || ((start + count) > text.length)) { + throw new IndexOutOfBoundsException("bad start or count for text of length " + text.length); + } + } + + /** + * Returns a {@code boolean} indicating whether or not this shaper shapes + * contextually. + * + * @return {@code true} if this shaper is contextual; {@code false} otherwise. + */ + public boolean isContextual() { + return (mask & CONTEXTUAL_MASK) != 0; + } + + /** + * Returns an {@code int} that ORs together the values for all the ranges that + * will be shaped. + *

+ * For example, to check if a shaper shapes to Arabic, you would use the + * following:

+ * {@code if ((shaper.getRanges() & shaper.ARABIC) != 0) { ... } + *
+ * + *

+ * Note that this method supports only the bit mask-based ranges. Call + * {@link #getRangeSet()} for the enum-based ranges. + * + * @return the values for all the ranges to be shaped. + */ + public int getRanges() { + return mask & ~CONTEXTUAL_MASK; + } + + /** + * Returns a {@code Set} representing all the Unicode ranges in this + * {@code NumericShaper} that will be shaped. + * + * @return all the Unicode ranges to be shaped. + * @since 1.7 + */ + public Set getRangeSet() { + if (rangeSet != null) { + return EnumSet.copyOf(rangeSet); + } + return Range.maskToRangeSet(mask); + } + + /** + * Perform non-contextual shaping. + */ + private void shapeNonContextually(char[] text, int start, int count) { + int base; + char minDigit = '0'; + if (shapingRange != null) { + base = shapingRange.getDigitBase(); + minDigit += shapingRange.getNumericBase(); + } else { + base = bases[key]; + if (key == ETHIOPIC_KEY) { + minDigit++; // Ethiopic doesn't use decimal zero + } + } + for (int i = start, e = start + count; i < e; ++i) { + char c = text[i]; + if (c >= minDigit && c <= '\u0039') { + text[i] = (char) (c + base); + } + } + } + + /** + * Perform contextual shaping. Synchronized to protect caches used in + * getContextKey. + */ + private synchronized void shapeContextually(char[] text, int start, int count, int ctxKey) { + + // if we don't support this context, then don't shape + if ((mask & (1 << ctxKey)) == 0) { + ctxKey = EUROPEAN_KEY; + } + int lastkey = ctxKey; + + int base = bases[ctxKey]; + char minDigit = ctxKey == ETHIOPIC_KEY ? '1' : '0'; // Ethiopic doesn't use decimal zero + + synchronized (NumericShaper.class) { + for (int i = start, e = start + count; i < e; ++i) { + char c = text[i]; + if (c >= minDigit && c <= '\u0039') { + text[i] = (char) (c + base); + } + + if (isStrongDirectional(c)) { + int newkey = getContextKey(c); + if (newkey != lastkey) { + lastkey = newkey; + + ctxKey = newkey; + if (((mask & EASTERN_ARABIC) != 0) && (ctxKey == ARABIC_KEY || ctxKey == EASTERN_ARABIC_KEY)) { + ctxKey = EASTERN_ARABIC_KEY; + } else if (((mask & ARABIC) != 0) && (ctxKey == ARABIC_KEY || ctxKey == EASTERN_ARABIC_KEY)) { + ctxKey = ARABIC_KEY; + } else if ((mask & (1 << ctxKey)) == 0) { + ctxKey = EUROPEAN_KEY; + } + + base = bases[ctxKey]; + + minDigit = ctxKey == ETHIOPIC_KEY ? '1' : '0'; // Ethiopic doesn't use decimal zero + } + } + } + } + } + + private void shapeContextually(char[] text, int start, int count, Range ctxKey) { + // if we don't support the specified context, then don't shape. + if (ctxKey == null || !rangeSet.contains(ctxKey)) { + ctxKey = Range.EUROPEAN; + } + + Range lastKey = ctxKey; + int base = ctxKey.getDigitBase(); + char minDigit = (char) ('0' + ctxKey.getNumericBase()); + final int end = start + count; + for (int i = start; i < end; ++i) { + char c = text[i]; + if (c >= minDigit && c <= '9') { + text[i] = (char) (c + base); + continue; + } + if (isStrongDirectional(c)) { + ctxKey = rangeForCodePoint(c); + if (ctxKey != lastKey) { + lastKey = ctxKey; + base = ctxKey.getDigitBase(); + minDigit = (char) ('0' + ctxKey.getNumericBase()); + } + } + } + } + + /** + * Returns a hash code for this shaper. + * + * @return this shaper's hash code. + * @see java.lang.Object#hashCode + */ + public int hashCode() { + int hash = mask; + if (rangeSet != null) { + // Use the CONTEXTUAL_MASK bit only for the enum-based + // NumericShaper. A deserialized NumericShaper might have + // bit masks. + hash &= CONTEXTUAL_MASK; + hash ^= rangeSet.hashCode(); + } + return hash; + } + + /** + * Returns {@code true} if the specified object is an instance of + * {@code NumericShaper} and shapes identically to this one, regardless of the + * range representations, the bit mask or the enum. For example, the following + * code produces {@code "true"}.

+ * + *
+	 * NumericShaper ns1 = NumericShaper.getShaper(NumericShaper.ARABIC);
+	 * NumericShaper ns2 = NumericShaper.getShaper(NumericShaper.Range.ARABIC);
+	 * System.out.println(ns1.equals(ns2));
+	 * 
+ * + *
+ * + * @param o the specified object to compare to this {@code NumericShaper} + * @return {@code true} if {@code o} is an instance of {@code NumericShaper} and + * shapes in the same way; {@code false} otherwise. + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object o) { + if (o != null) { + try { + NumericShaper rhs = (NumericShaper) o; + if (rangeSet != null) { + if (rhs.rangeSet != null) { + return isContextual() == rhs.isContextual() && rangeSet.equals(rhs.rangeSet) + && shapingRange == rhs.shapingRange; + } + return isContextual() == rhs.isContextual() && rangeSet.equals(Range.maskToRangeSet(rhs.mask)) + && shapingRange == Range.indexToRange(rhs.key); + } else if (rhs.rangeSet != null) { + Set rset = Range.maskToRangeSet(mask); + Range srange = Range.indexToRange(key); + return isContextual() == rhs.isContextual() && rset.equals(rhs.rangeSet) + && srange == rhs.shapingRange; + } + return rhs.mask == mask && rhs.key == key; + } catch (ClassCastException e) { + } + } + return false; + } + + /** + * Returns a {@code String} that describes this shaper. This method is used for + * debugging purposes only. + * + * @return a {@code String} describing this shaper. + */ + public String toString() { + StringBuilder buf = new StringBuilder(super.toString()); + + buf.append("[contextual:").append(isContextual()); + + String[] keyNames = null; + if (isContextual()) { + buf.append(", context:"); + buf.append(shapingRange == null ? Range.values()[key] : shapingRange); + } + + if (rangeSet == null) { + buf.append(", range(s): "); + boolean first = true; + for (int i = 0; i < NUM_KEYS; ++i) { + if ((mask & (1 << i)) != 0) { + if (first) { + first = false; + } else { + buf.append(", "); + } + buf.append(Range.values()[i]); + } + } + } else { + buf.append(", range set: ").append(rangeSet); + } + buf.append(']'); + + return buf.toString(); + } + + /** + * Returns the index of the high bit in value (assuming le, actually power of 2 + * >= value). value must be positive. + */ + private static int getHighBit(int value) { + if (value <= 0) { + return -32; + } + + int bit = 0; + + 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) { + bit += 1; + } + + return bit; + } + + /** + * fast binary search over subrange of array. + */ + private static int search(int value, int[] array, int start, int length) { + int power = 1 << getHighBit(length); + int extra = length - power; + int probe = power; + int index = start; + + if (value >= array[index + extra]) { + index += extra; + } + + while (probe > 1) { + probe >>= 1; + + if (value >= array[index + probe]) { + index += probe; + } + } + + return index; + } + + /** + * Converts the {@code NumericShaper.Range} enum-based parameters, if any, to + * the bit mask-based counterparts and writes this object to the {@code stream}. + * Any enum constants that have no bit mask-based counterparts are ignored in + * the conversion. + * + * @param stream the output stream to write to + * @throws IOException if an I/O error occurs while writing to {@code stream} + * @since 1.7 + */ + private void writeObject(ObjectOutputStream stream) throws IOException { + if (shapingRange != null) { + int index = Range.toRangeIndex(shapingRange); + if (index >= 0) { + key = index; + } + } + if (rangeSet != null) { + mask |= Range.toRangeMask(rangeSet); + } + stream.defaultWriteObject(); + } +} diff --git a/src/main/java/jdk_internal/bidi/ParseException.java b/src/main/java/jdk_internal/bidi/ParseException.java index 3d3e9d09..a7edf091 100755 --- a/src/main/java/jdk_internal/bidi/ParseException.java +++ b/src/main/java/jdk_internal/bidi/ParseException.java @@ -1,83 +1,83 @@ -/* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved - * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved - * - * The original version of this source code and documentation is copyrighted - * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These - * materials are provided under terms of a License Agreement between Taligent - * and Sun. This technology is protected by multiple US and International - * patents. This notice and attribution to Taligent may not be removed. - * Taligent is a registered trademark of Taligent, Inc. - * - */ - -package jdk_internal.bidi; - -/** - * Signals that an error has been reached unexpectedly while parsing. - * - * @see java.lang.Exception - * @see java.text.Format - * @see java.text.FieldPosition - * @author Mark Davis - * @since 1.1 - */ -public class ParseException extends Exception { - - private static final long serialVersionUID = 2703218443322787634L; - - /** - * Constructs a ParseException with the specified detail message and offset. A - * detail message is a String that describes this particular exception. - * - * @param s the detail message - * @param errorOffset the position where the error is found while parsing. - */ - public ParseException(String s, int errorOffset) { - super(s); - this.errorOffset = errorOffset; - } - - /** - * Returns the position where the error was found. - * - * @return the position where the error was found - */ - public int getErrorOffset() { - return errorOffset; - } - - // ============ privates ============ - /** - * The zero-based character offset into the string being parsed at which the - * error was found during parsing. - * - * @serial - */ - private int errorOffset; -} +/* + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved + * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved + * + * The original version of this source code and documentation is copyrighted + * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These + * materials are provided under terms of a License Agreement between Taligent + * and Sun. This technology is protected by multiple US and International + * patents. This notice and attribution to Taligent may not be removed. + * Taligent is a registered trademark of Taligent, Inc. + * + */ + +package jdk_internal.bidi; + +/** + * Signals that an error has been reached unexpectedly while parsing. + * + * @see java.lang.Exception + * @see java.text.Format + * @see java.text.FieldPosition + * @author Mark Davis + * @since 1.1 + */ +public class ParseException extends Exception { + + private static final long serialVersionUID = 2703218443322787634L; + + /** + * Constructs a ParseException with the specified detail message and offset. A + * detail message is a String that describes this particular exception. + * + * @param s the detail message + * @param errorOffset the position where the error is found while parsing. + */ + public ParseException(String s, int errorOffset) { + super(s); + this.errorOffset = errorOffset; + } + + /** + * Returns the position where the error was found. + * + * @return the position where the error was found + */ + public int getErrorOffset() { + return errorOffset; + } + + // ============ privates ============ + /** + * The zero-based character offset into the string being parsed at which the + * error was found during parsing. + * + * @serial + */ + private int errorOffset; +} diff --git a/src/main/java/jdk_internal/bidi/SunNormalizer.java b/src/main/java/jdk_internal/bidi/SunNormalizer.java index f550f2db..2396f858 100755 --- a/src/main/java/jdk_internal/bidi/SunNormalizer.java +++ b/src/main/java/jdk_internal/bidi/SunNormalizer.java @@ -1,96 +1,94 @@ -/* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk_internal.bidi; - -import jdk_internal.icu.lang.UCharacter; -import jdk_internal.icu.text.NormalizerBase; - -/** - * This Normalizer is for Unicode 3.2 support for IDNA only. Developers should - * not use this class. - * - * @since 1.6 - */ -public final class SunNormalizer { - - private SunNormalizer() { - }; - - /** - * Option to select Unicode 3.2 (without corrigendum 4 corrections) for - * normalization. - */ - public static final int UNICODE_3_2 = NormalizerBase.UNICODE_3_2_0_ORIGINAL; - - /** - * Normalize a sequence of char values. The sequence will be normalized - * according to the specified normalization from. - * - * @param src The sequence of char values to normalize. - * @param form The normalization form; one of - * {@link java.text.Normalizer.Form#NFC}, - * {@link java.text.Normalizer.Form#NFD}, - * {@link java.text.Normalizer.Form#NFKC}, - * {@link java.text.Normalizer.Form#NFKD} - * @param option The normalization option; - * {@link sun.text.Normalizer#UNICODE_3_2} - * @return The normalized String - * @throws NullPointerException If src or form is - * null. - */ - public static String normalize(CharSequence src, Normalizer.Form form, int option) { - return NormalizerBase.normalize(src.toString(), form, option); - }; - - /** - * Determines if the given sequence of char values is normalized. - * - * @param src The sequence of char values to be checked. - * @param form The normalization form; one of - * {@link java.text.Normalizer.Form#NFC}, - * {@link java.text.Normalizer.Form#NFD}, - * {@link java.text.Normalizer.Form#NFKC}, - * {@link java.text.Normalizer.Form#NFKD} - * @param option The normalization option; - * {@link sun.text.Normalizer#UNICODE_3_2} - * @return true if the sequence of char values is normalized; false otherwise. - * @throws NullPointerException If src or form is - * null. - */ - public static boolean isNormalized(CharSequence src, Normalizer.Form form, int option) { - return NormalizerBase.isNormalized(src.toString(), form, option); - } - - /** - * Returns the combining class of the given character - * - * @param ch character to retrieve combining class of - * @return combining class of the given character - */ - public static final int getCombiningClass(int ch) { - return UCharacter.getCombiningClass(ch); - } -} +/* + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk_internal.bidi; + +import jdk_internal.icu.lang.UCharacter; +import jdk_internal.icu.text.NormalizerBase; + +/** + * This Normalizer is for Unicode 3.2 support for IDNA only. Developers should + * not use this class. + * + * @since 1.6 + */ +public final class SunNormalizer { + + private SunNormalizer() { + }; + + /** + * Option to select Unicode 3.2 (without corrigendum 4 corrections) for + * normalization. + */ + public static final int UNICODE_3_2 = NormalizerBase.UNICODE_3_2_0_ORIGINAL; + + /** + * Normalize a sequence of char values. The sequence will be normalized + * according to the specified normalization from. + * + * @param src The sequence of char values to normalize. + * @param form The normalization form; one of + * {@link java.text.Normalizer.Form#NFC}, + * {@link java.text.Normalizer.Form#NFD}, + * {@link java.text.Normalizer.Form#NFKC}, + * {@link java.text.Normalizer.Form#NFKD} + * @param option The normalization option; + * @return The normalized String + * @throws NullPointerException If src or form is + * null. + */ + public static String normalize(CharSequence src, Normalizer.Form form, int option) { + return NormalizerBase.normalize(src.toString(), form, option); + }; + + /** + * Determines if the given sequence of char values is normalized. + * + * @param src The sequence of char values to be checked. + * @param form The normalization form; one of + * {@link java.text.Normalizer.Form#NFC}, + * {@link java.text.Normalizer.Form#NFD}, + * {@link java.text.Normalizer.Form#NFKC}, + * {@link java.text.Normalizer.Form#NFKD} + * @param option The normalization option; + * @return true if the sequence of char values is normalized; false otherwise. + * @throws NullPointerException If src or form is + * null. + */ + public static boolean isNormalized(CharSequence src, Normalizer.Form form, int option) { + return NormalizerBase.isNormalized(src.toString(), form, option); + } + + /** + * Returns the combining class of the given character + * + * @param ch character to retrieve combining class of + * @return combining class of the given character + */ + public static final int getCombiningClass(int ch) { + return UCharacter.getCombiningClass(ch); + } +} diff --git a/src/main/java/jdk_internal/bidi/TextAttribute.java b/src/main/java/jdk_internal/bidi/TextAttribute.java index 003e85b7..5c828690 100755 --- a/src/main/java/jdk_internal/bidi/TextAttribute.java +++ b/src/main/java/jdk_internal/bidi/TextAttribute.java @@ -1,1065 +1,1065 @@ -/* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved - * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved - * - * The original version of this source code and documentation is - * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary - * of IBM. These materials are provided under terms of a License - * Agreement between Taligent and Sun. This technology is protected - * by multiple US and International patents. - * - * This notice and attribution to Taligent may not be removed. - * Taligent is a registered trademark of Taligent, Inc. - * - */ - -package jdk_internal.bidi; - -import java.io.InvalidObjectException; -import java.util.HashMap; -import java.util.Map; - -import jdk_internal.bidi.AttributedCharacterIterator.Attribute; - -/** - * The {@code TextAttribute} class defines attribute keys and attribute values - * used for text rendering. - *

- * {@code TextAttribute} instances are used as attribute keys to identify - * attributes in {@link java.awt.Font Font}, {@link java.awt.font.TextLayout - * TextLayout}, {@link java.text.AttributedCharacterIterator - * AttributedCharacterIterator}, and other classes handling text attributes. - * Other constants defined in this class can be used as attribute values. - *

- * For each text attribute, the documentation provides: - *

    - *
  • the type of its value, - *
  • the relevant predefined constants, if any - *
  • the default effect if the attribute is absent - *
  • the valid values if there are limitations - *
  • a description of the effect. - *
- * - *

Values

- *
    - *
  • The values of attributes must always be immutable. - *
  • Where value limitations are given, any value outside of that set is - * reserved for future use; the value will be treated as the default. - *
  • The value {@code null} is treated the same as the default value and - * results in the default behavior. - *
  • If the value is not of the proper type, the attribute will be ignored. - *
  • The identity of the value does not matter, only the actual value. For - * example, {@code TextAttribute.WEIGHT_BOLD} and {@code Float.valueOf(2.0f)} - * indicate the same {@code WEIGHT}. - *
  • Attribute values of type {@code Number} (used for {@code WEIGHT}, - * {@code WIDTH}, {@code POSTURE}, {@code SIZE}, {@code JUSTIFICATION}, and - * {@code TRACKING}) can vary along their natural range and are not restricted - * to the predefined constants. {@code Number.floatValue()} is used to get the - * actual value from the {@code Number}. - *
  • The values for {@code WEIGHT}, {@code WIDTH}, and {@code POSTURE} are - * interpolated by the system, which can select the 'nearest available' font or - * use other techniques to approximate the user's request. - * - *
- * - *

Summary of attributes

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Key, value type, principal constants, and default value behavior of - * all TextAttributes
Key - * Value Type - * Principal Constants - * Default Value
{@link #FAMILY} - * String - * See Font {@link java.awt.Font#DIALOG DIALOG}, - * {@link java.awt.Font#DIALOG_INPUT DIALOG_INPUT},
- * {@link java.awt.Font#SERIF SERIF}, {@link java.awt.Font#SANS_SERIF - * SANS_SERIF}, and {@link java.awt.Font#MONOSPACED MONOSPACED}. - *
"Default" (use platform default) - *
{@link #WEIGHT} - * Number - * WEIGHT_REGULAR, WEIGHT_BOLD - * WEIGHT_REGULAR - *
{@link #WIDTH} - * Number - * WIDTH_CONDENSED, WIDTH_REGULAR,
- * WIDTH_EXTENDED - *
WIDTH_REGULAR - *
{@link #POSTURE} - * Number - * POSTURE_REGULAR, POSTURE_OBLIQUE - * POSTURE_REGULAR - *
{@link #SIZE} - * Number - * none - * 12.0 - *
{@link #TRANSFORM} - * {@link TransformAttribute} - * See TransformAttribute {@link TransformAttribute#IDENTITY IDENTITY} - * TransformAttribute.IDENTITY - *
{@link #SUPERSCRIPT} - * Integer - * SUPERSCRIPT_SUPER, SUPERSCRIPT_SUB - * 0 (use the standard glyphs and metrics) - *
{@link #FONT} - * {@link java.awt.Font} - * none - * null (do not override font resolution) - *
{@link #CHAR_REPLACEMENT} - * {@link GraphicAttribute} - * none - * null (draw text using font glyphs) - *
{@link #FOREGROUND} - * {@link java.awt.Paint} - * none - * null (use current graphics paint) - *
{@link #BACKGROUND} - * {@link java.awt.Paint} - * none - * null (do not render background) - *
{@link #UNDERLINE} - * Integer - * UNDERLINE_ON - * -1 (do not render underline) - *
{@link #STRIKETHROUGH} - * Boolean - * STRIKETHROUGH_ON - * false (do not render strikethrough) - *
{@link #RUN_DIRECTION} - * Boolean - * RUN_DIRECTION_LTR
- * RUN_DIRECTION_RTL - *
null (use {@link java.text.Bidi} standard default) - *
{@link #BIDI_EMBEDDING} - * Integer - * none - * 0 (use base line direction) - *
{@link #JUSTIFICATION} - * Number - * JUSTIFICATION_FULL - * JUSTIFICATION_FULL - *
{@link #INPUT_METHOD_HIGHLIGHT} - * {@link java.awt.im.InputMethodHighlight},
- * {@link java.text.Annotation} - *
(see class) - * null (do not apply input highlighting) - *
{@link #INPUT_METHOD_UNDERLINE} - * Integer - * UNDERLINE_LOW_ONE_PIXEL,
- * UNDERLINE_LOW_TWO_PIXEL - *
-1 (do not render underline) - *
{@link #SWAP_COLORS} - * Boolean - * SWAP_COLORS_ON - * false (do not swap colors) - *
{@link #NUMERIC_SHAPING} - * {@link java.awt.font.NumericShaper} - * none - * null (do not shape digits) - *
{@link #KERNING} - * Integer - * KERNING_ON - * 0 (do not request kerning) - *
{@link #LIGATURES} - * Integer - * LIGATURES_ON - * 0 (do not form optional ligatures) - *
{@link #TRACKING} - * Number - * TRACKING_LOOSE, TRACKING_TIGHT - * 0 (do not add tracking) - *
- * - * @see java.awt.Font - * @see java.awt.font.TextLayout - * @see java.text.AttributedCharacterIterator - */ -public final class TextAttribute extends Attribute { - - // table of all instances in this class, used by readResolve - private static final Map instanceMap = new HashMap(29); - - /** - * Constructs a {@code TextAttribute} with the specified name. - * - * @param name the attribute name to assign to this {@code TextAttribute} - */ - protected TextAttribute(String name) { - super(name); - if (this.getClass() == TextAttribute.class) { - instanceMap.put(name, this); - } - } - - /** - * Resolves instances being deserialized to the predefined constants. - */ - protected Object readResolve() throws InvalidObjectException { - if (this.getClass() != TextAttribute.class) { - throw new InvalidObjectException("subclass didn't correctly implement readResolve"); - } - - TextAttribute instance = instanceMap.get(getName()); - if (instance != null) { - return instance; - } else { - throw new InvalidObjectException("unknown attribute name"); - } - } - - /** - * Use serialVersionUID from JDK 1.2 for interoperability. 1.2 will throw an - * InvalidObjectException if ever asked to deserialize INPUT_METHOD_UNDERLINE. - * This shouldn't happen in real life. - */ - private static final long serialVersionUID = 7744112784117861702L; - - // - // For use with Font. - // - - /** - * Attribute key for the font name. Values are instances of - * {@code String}. The default value is {@code "Default"}, which causes - * the platform default font family to be used. - * - *

- * The {@code Font} class defines constants for the logical font names - * {@link java.awt.Font#DIALOG DIALOG}, {@link java.awt.Font#DIALOG_INPUT - * DIALOG_INPUT}, {@link java.awt.Font#SANS_SERIF SANS_SERIF}, - * {@link java.awt.Font#SERIF SERIF}, and {@link java.awt.Font#MONOSPACED - * MONOSPACED}. - * - *

- * This defines the value passed as {@code name} to the {@code Font} - * constructor. Both logical and physical font names are allowed. If a font with - * the requested name is not found, the default font is used. - * - *

- * Note: This attribute is unfortunately misnamed, as it specifies the - * face name and not just the family. Thus values such as "Lucida Sans Bold" - * will select that face if it exists. Note, though, that if the requested face - * does not exist, the default will be used with regular weight. The - * "Bold" in the name is part of the face name, not a separate request that the - * font's weight be bold. - *

- */ - public static final TextAttribute FAMILY = new TextAttribute("family"); - - /** - * Attribute key for the weight of a font. Values are instances of - * {@code Number}. The default value is {@code WEIGHT_REGULAR}. - * - *

- * Several constant values are provided, see {@link #WEIGHT_EXTRA_LIGHT}, - * {@link #WEIGHT_LIGHT}, {@link #WEIGHT_DEMILIGHT}, {@link #WEIGHT_REGULAR}, - * {@link #WEIGHT_SEMIBOLD}, {@link #WEIGHT_MEDIUM}, {@link #WEIGHT_DEMIBOLD}, - * {@link #WEIGHT_BOLD}, {@link #WEIGHT_HEAVY}, {@link #WEIGHT_EXTRABOLD}, and - * {@link #WEIGHT_ULTRABOLD}. The value {@code WEIGHT_BOLD} corresponds to the - * style value {@code Font.BOLD} as passed to the {@code Font} constructor. - * - *

- * The value is roughly the ratio of the stem width to that of the regular - * weight. - * - *

- * The system can interpolate the provided value. - */ - public static final TextAttribute WEIGHT = new TextAttribute("weight"); - - /** - * The lightest predefined weight. - * - * @see #WEIGHT - */ - public static final Float WEIGHT_EXTRA_LIGHT = Float.valueOf(0.5f); - - /** - * The standard light weight. - * - * @see #WEIGHT - */ - public static final Float WEIGHT_LIGHT = Float.valueOf(0.75f); - - /** - * An intermediate weight between {@code WEIGHT_LIGHT} and - * {@code WEIGHT_STANDARD}. - * - * @see #WEIGHT - */ - public static final Float WEIGHT_DEMILIGHT = Float.valueOf(0.875f); - - /** - * The standard weight. This is the default value for {@code WEIGHT}. - * - * @see #WEIGHT - */ - public static final Float WEIGHT_REGULAR = Float.valueOf(1.0f); - - /** - * A moderately heavier weight than {@code WEIGHT_REGULAR}. - * - * @see #WEIGHT - */ - public static final Float WEIGHT_SEMIBOLD = Float.valueOf(1.25f); - - /** - * An intermediate weight between {@code WEIGHT_REGULAR} and - * {@code WEIGHT_BOLD}. - * - * @see #WEIGHT - */ - public static final Float WEIGHT_MEDIUM = Float.valueOf(1.5f); - - /** - * A moderately lighter weight than {@code WEIGHT_BOLD}. - * - * @see #WEIGHT - */ - public static final Float WEIGHT_DEMIBOLD = Float.valueOf(1.75f); - - /** - * The standard bold weight. - * - * @see #WEIGHT - */ - public static final Float WEIGHT_BOLD = Float.valueOf(2.0f); - - /** - * A moderately heavier weight than {@code WEIGHT_BOLD}. - * - * @see #WEIGHT - */ - public static final Float WEIGHT_HEAVY = Float.valueOf(2.25f); - - /** - * An extra heavy weight. - * - * @see #WEIGHT - */ - public static final Float WEIGHT_EXTRABOLD = Float.valueOf(2.5f); - - /** - * The heaviest predefined weight. - * - * @see #WEIGHT - */ - public static final Float WEIGHT_ULTRABOLD = Float.valueOf(2.75f); - - /** - * Attribute key for the width of a font. Values are instances of - * {@code Number}. The default value is {@code WIDTH_REGULAR}. - * - *

- * Several constant values are provided, see {@link #WIDTH_CONDENSED}, - * {@link #WIDTH_SEMI_CONDENSED}, {@link #WIDTH_REGULAR}, - * {@link #WIDTH_SEMI_EXTENDED}, {@link #WIDTH_EXTENDED}. - * - *

- * The value is roughly the ratio of the advance width to that of the regular - * width. - * - *

- * The system can interpolate the provided value. - */ - public static final TextAttribute WIDTH = new TextAttribute("width"); - - /** - * The most condensed predefined width. - * - * @see #WIDTH - */ - public static final Float WIDTH_CONDENSED = Float.valueOf(0.75f); - - /** - * A moderately condensed width. - * - * @see #WIDTH - */ - public static final Float WIDTH_SEMI_CONDENSED = Float.valueOf(0.875f); - - /** - * The standard width. This is the default value for {@code WIDTH}. - * - * @see #WIDTH - */ - public static final Float WIDTH_REGULAR = Float.valueOf(1.0f); - - /** - * A moderately extended width. - * - * @see #WIDTH - */ - public static final Float WIDTH_SEMI_EXTENDED = Float.valueOf(1.25f); - - /** - * The most extended predefined width. - * - * @see #WIDTH - */ - public static final Float WIDTH_EXTENDED = Float.valueOf(1.5f); - - /** - * Attribute key for the posture of a font. Values are instances of - * {@code Number}. The default value is {@code POSTURE_REGULAR}. - * - *

- * Two constant values are provided, {@link #POSTURE_REGULAR} and - * {@link #POSTURE_OBLIQUE}. The value {@code POSTURE_OBLIQUE} corresponds to - * the style value {@code Font.ITALIC} as passed to the {@code Font} - * constructor. - * - *

- * The value is roughly the slope of the stems of the font, expressed as the run - * over the rise. Positive values lean right. - * - *

- * The system can interpolate the provided value. - * - *

- * This will affect the font's italic angle as returned by - * {@code Font.getItalicAngle}. - * - * @see java.awt.Font#getItalicAngle() - */ - public static final TextAttribute POSTURE = new TextAttribute("posture"); - - /** - * The standard posture, upright. This is the default value for {@code POSTURE}. - * - * @see #POSTURE - */ - public static final Float POSTURE_REGULAR = Float.valueOf(0.0f); - - /** - * The standard italic posture. - * - * @see #POSTURE - */ - public static final Float POSTURE_OBLIQUE = Float.valueOf(0.20f); - - /** - * Attribute key for the font size. Values are instances of - * {@code Number}. The default value is 12pt. - * - *

- * This corresponds to the {@code size} parameter to the {@code Font} - * constructor. - * - *

- * Very large or small sizes will impact rendering performance, and the - * rendering system might not render text at these sizes. Negative sizes are - * illegal and result in the default size. - * - *

- * Note that the appearance and metrics of a 12pt font with a 2x transform might - * be different than that of a 24 point font with no transform. - */ - public static final TextAttribute SIZE = new TextAttribute("size"); - - /** - * Attribute key for the transform of a font. Values are instances of - * {@code TransformAttribute}. The default value is - * {@code TransformAttribute.IDENTITY}. - * - *

- * The {@code TransformAttribute} class defines the constant - * {@link TransformAttribute#IDENTITY IDENTITY}. - * - *

- * This corresponds to the transform passed to - * {@code Font.deriveFont(AffineTransform)}. Since that transform is mutable and - * {@code TextAttribute} values must not be, the {@code TransformAttribute} - * wrapper class is used. - * - *

- * The primary intent is to support scaling and skewing, though other effects - * are possible. - *

- * - *

- * Some transforms will cause the baseline to be rotated and/or shifted. The - * text and the baseline are transformed together so that the text follows the - * new baseline. For example, with text on a horizontal baseline, the new - * baseline follows the direction of the unit x vector passed through the - * transform. Text metrics are measured against this new baseline. So, for - * example, with other things being equal, text rendered with a rotated - * TRANSFORM and an unrotated TRANSFORM will measure as having the same ascent, - * descent, and advance. - *

- * - *

- * In styled text, the baselines for each such run are aligned one after the - * other to potentially create a non-linear baseline for the entire run of text. - * For more information, see {@link TextLayout#getLayoutPath}. - *

- * - * @see TransformAttribute - * @see java.awt.geom.AffineTransform - */ - public static final TextAttribute TRANSFORM = new TextAttribute("transform"); - - /** - * Attribute key for superscripting and subscripting. Values are instances of - * {@code Integer}. The default value is 0, which means that no - * superscript or subscript is used. - * - *

- * Two constant values are provided, see {@link #SUPERSCRIPT_SUPER} and - * {@link #SUPERSCRIPT_SUB}. These have the values 1 and -1 respectively. Values - * of greater magnitude define greater levels of superscript or subscripting, - * for example, 2 corresponds to super-superscript, 3 to - * super-super-superscript, and similarly for negative values and subscript, up - * to a level of 7 (or -7). Values beyond this range are reserved; behavior is - * platform-dependent. - * - *

- * {@code SUPERSCRIPT} can impact the ascent and descent of a font. The ascent - * and descent can never become negative, however. - */ - public static final TextAttribute SUPERSCRIPT = new TextAttribute("superscript"); - - /** - * Standard superscript. - * - * @see #SUPERSCRIPT - */ - public static final Integer SUPERSCRIPT_SUPER = Integer.valueOf(1); - - /** - * Standard subscript. - * - * @see #SUPERSCRIPT - */ - public static final Integer SUPERSCRIPT_SUB = Integer.valueOf(-1); - - /** - * Attribute key used to provide the font to use to render text. Values are - * instances of {@link java.awt.Font}. The default value is null, indicating - * that normal resolution of a {@code Font} from attributes should be performed. - * - *

- * {@code TextLayout} and {@code AttributedCharacterIterator} work in terms of - * {@code Maps} of {@code TextAttributes}. Normally, all the attributes are - * examined and used to select and configure a {@code Font} instance. If a - * {@code FONT} attribute is present, though, its associated {@code Font} will - * be used. This provides a way for users to override the resolution of font - * attributes into a {@code Font}, or force use of a particular {@code Font} - * instance. This also allows users to specify subclasses of {@code Font} in - * cases where a {@code Font} can be subclassed. - * - *

- * {@code FONT} is used for special situations where clients already have a - * {@code Font} instance but still need to use {@code Map}-based APIs. - * Typically, there will be no other attributes in the {@code Map} except the - * {@code FONT} attribute. With {@code Map}-based APIs the common case is to - * specify all attributes individually, so {@code FONT} is not needed or - * desirable. - * - *

- * However, if both {@code FONT} and other attributes are present in the - * {@code Map}, the rendering system will merge the attributes defined in the - * {@code Font} with the additional attributes. This merging process classifies - * {@code TextAttributes} into two groups. One group, the 'primary' group, is - * considered fundamental to the selection and metric behavior of a font. These - * attributes are {@code FAMILY}, {@code WEIGHT}, {@code WIDTH}, - * {@code POSTURE}, {@code SIZE}, {@code TRANSFORM}, {@code SUPERSCRIPT}, and - * {@code TRACKING}. The other group, the 'secondary' group, consists of all - * other defined attributes, with the exception of {@code FONT} itself. - * - *

- * To generate the new {@code Map}, first the {@code Font} is obtained from the - * {@code FONT} attribute, and all of its attributes extracted into a - * new {@code Map}. Then only the secondary attributes from the - * original {@code Map} are added to those in the new {@code Map}. Thus the - * values of primary attributes come solely from the {@code Font}, and the - * values of secondary attributes originate with the {@code Font} but can be - * overridden by other values in the {@code Map}. - * - *

- * Note:{@code Font's Map}-based constructor and {@code deriveFont} - * methods do not process the {@code FONT} attribute, as these are used to - * create new {@code Font} objects. Instead, {@link java.awt.Font#getFont(Map) - * Font.getFont(Map)} should be used to handle the {@code FONT} attribute. - * - * @see java.awt.Font - */ - public static final TextAttribute FONT = new TextAttribute("font"); - - /** - * Attribute key for a user-defined glyph to display in lieu of the font's - * standard glyph for a character. Values are instances of GraphicAttribute. The - * default value is null, indicating that the standard glyphs provided by the - * font should be used. - * - *

- * This attribute is used to reserve space for a graphic or other component - * embedded in a line of text. It is required for correct positioning of - * 'inline' components within a line when bidirectional reordering (see - * {@link java.text.Bidi}) is performed. Each character (Unicode code point) - * will be rendered using the provided GraphicAttribute. Typically, the - * characters to which this attribute is applied should be - * \uFFFC. - * - *

- * The GraphicAttribute determines the logical and visual bounds of the text; - * the actual Font values are ignored. - * - * @see GraphicAttribute - */ - public static final TextAttribute CHAR_REPLACEMENT = new TextAttribute("char_replacement"); - - // - // Adornments added to text. - // - - /** - * Attribute key for the paint used to render the text. Values are instances of - * {@code Paint}. The default value is null, indicating that the - * {@code Paint} set on the {@code Graphics2D} at the time of rendering is used. - * - *

- * Glyphs will be rendered using this {@code Paint} regardless of the - * {@code Paint} value set on the {@code Graphics} (but see - * {@link #SWAP_COLORS}). - * - * @see java.awt.Paint - * @see #SWAP_COLORS - */ - public static final TextAttribute FOREGROUND = new TextAttribute("foreground"); - - /** - * Attribute key for the paint used to render the background of the text. Values - * are instances of {@code Paint}. The default value is null, indicating - * that the background should not be rendered. - * - *

- * The logical bounds of the text will be filled using this {@code Paint}, and - * then the text will be rendered on top of it (but see {@link #SWAP_COLORS}). - * - *

- * The visual bounds of the text is extended to include the logical bounds, if - * necessary. The outline is not affected. - * - * @see java.awt.Paint - * @see #SWAP_COLORS - */ - public static final TextAttribute BACKGROUND = new TextAttribute("background"); - - /** - * Attribute key for underline. Values are instances of {@code Integer}. - * The default value is -1, which means no underline. - * - *

- * The constant value {@link #UNDERLINE_ON} is provided. - * - *

- * The underline affects both the visual bounds and the outline of the text. - */ - public static final TextAttribute UNDERLINE = new TextAttribute("underline"); - - /** - * Standard underline. - * - * @see #UNDERLINE - */ - public static final Integer UNDERLINE_ON = Integer.valueOf(0); - - /** - * Attribute key for strikethrough. Values are instances of - * {@code Boolean}. The default value is {@code false}, which means no - * strikethrough. - * - *

- * The constant value {@link #STRIKETHROUGH_ON} is provided. - * - *

- * The strikethrough affects both the visual bounds and the outline of the text. - */ - public static final TextAttribute STRIKETHROUGH = new TextAttribute("strikethrough"); - - /** - * A single strikethrough. - * - * @see #STRIKETHROUGH - */ - public static final Boolean STRIKETHROUGH_ON = Boolean.TRUE; - - // - // Attributes use to control layout of text on a line. - // - - /** - * Attribute key for the run direction of the line. Values are instances of - * {@code Boolean}. The default value is null, which indicates that the - * standard Bidi algorithm for determining run direction should be used with the - * value {@link java.text.Bidi#DIRECTION_DEFAULT_LEFT_TO_RIGHT}. - * - *

- * The constants {@link #RUN_DIRECTION_RTL} and {@link #RUN_DIRECTION_LTR} are - * provided. - * - *

- * This determines the value passed to the {@link java.text.Bidi} constructor to - * select the primary direction of the text in the paragraph. - * - *

- * Note: This attribute should have the same value for all the text in - * a paragraph, otherwise the behavior is undetermined. - * - * @see java.text.Bidi - */ - public static final TextAttribute RUN_DIRECTION = new TextAttribute("run_direction"); - - /** - * Left-to-right run direction. - * - * @see #RUN_DIRECTION - */ - public static final Boolean RUN_DIRECTION_LTR = Boolean.FALSE; - - /** - * Right-to-left run direction. - * - * @see #RUN_DIRECTION - */ - public static final Boolean RUN_DIRECTION_RTL = Boolean.TRUE; - - /** - * Attribute key for the embedding level of the text. Values are instances of - * {@code Integer}. The default value is {@code null}, indicating that - * the Bidirectional algorithm should run without explicit embeddings. - * - *

- * Positive values 1 through 61 are embedding levels, negative values - * -1 through -61 are override levels. The value 0 means that the base - * line direction is used. These levels are passed in the embedding levels array - * to the {@link java.text.Bidi} constructor. - * - *

- * Note: When this attribute is present anywhere in a paragraph, then - * any Unicode bidi control characters (RLO, LRO, RLE, LRE, and PDF) in the - * paragraph are disregarded, and runs of text where this attribute is not - * present are treated as though it were present and had the value 0. - * - * @see java.text.Bidi - */ - public static final TextAttribute BIDI_EMBEDDING = new TextAttribute("bidi_embedding"); - - /** - * Attribute key for the justification of a paragraph. Values are instances of - * {@code Number}. The default value is 1, indicating that justification - * should use the full width provided. Values are pinned to the range [0..1]. - * - *

- * The constants {@link #JUSTIFICATION_FULL} and {@link #JUSTIFICATION_NONE} are - * provided. - * - *

- * Specifies the fraction of the extra space to use when justification is - * requested on a {@code TextLayout}. For example, if the line is 50 points wide - * and it is requested to justify to 70 points, a value of 0.75 will pad to use - * three-quarters of the remaining space, or 15 points, so that the resulting - * line will be 65 points in length. - * - *

- * Note: This should have the same value for all the text in a - * paragraph, otherwise the behavior is undetermined. - * - * @see TextLayout#getJustifiedLayout - */ - public static final TextAttribute JUSTIFICATION = new TextAttribute("justification"); - - /** - * Justify the line to the full requested width. This is the default value for - * {@code JUSTIFICATION}. - * - * @see #JUSTIFICATION - */ - public static final Float JUSTIFICATION_FULL = Float.valueOf(1.0f); - - /** - * Do not allow the line to be justified. - * - * @see #JUSTIFICATION - */ - public static final Float JUSTIFICATION_NONE = Float.valueOf(0.0f); - - // - // For use by input method. - // - - /** - * Attribute key for input method highlight styles. - * - *

- * Values are instances of {@link java.awt.im.InputMethodHighlight} or - * {@link java.text.Annotation}. The default value is {@code null}, which means - * that input method styles should not be applied before rendering. - * - *

- * If adjacent runs of text with the same {@code InputMethodHighlight} need to - * be rendered separately, the {@code InputMethodHighlights} should be wrapped - * in {@code Annotation} instances. - * - *

- * Input method highlights are used while text is being composed by an input - * method. Text editing components should retain them even if they generally - * only deal with unstyled text, and make them available to the drawing - * routines. - * - * @see java.awt.Font - * @see java.awt.im.InputMethodHighlight - * @see java.text.Annotation - */ - public static final TextAttribute INPUT_METHOD_HIGHLIGHT = new TextAttribute("input method highlight"); - - /** - * Attribute key for input method underlines. Values are instances of - * {@code Integer}. The default value is {@code -1}, which means no - * underline. - * - *

- * Several constant values are provided, see {@link #UNDERLINE_LOW_ONE_PIXEL}, - * {@link #UNDERLINE_LOW_TWO_PIXEL}, {@link #UNDERLINE_LOW_DOTTED}, - * {@link #UNDERLINE_LOW_GRAY}, and {@link #UNDERLINE_LOW_DASHED}. - * - *

- * This may be used in conjunction with {@link #UNDERLINE} if desired. The - * primary purpose is for use by input methods. Other use of these underlines - * for simple ornamentation might confuse users. - * - *

- * The input method underline affects both the visual bounds and the outline of - * the text. - * - * @since 1.3 - */ - public static final TextAttribute INPUT_METHOD_UNDERLINE = new TextAttribute("input method underline"); - - /** - * Single pixel solid low underline. - * - * @see #INPUT_METHOD_UNDERLINE - * @since 1.3 - */ - public static final Integer UNDERLINE_LOW_ONE_PIXEL = Integer.valueOf(1); - - /** - * Double pixel solid low underline. - * - * @see #INPUT_METHOD_UNDERLINE - * @since 1.3 - */ - public static final Integer UNDERLINE_LOW_TWO_PIXEL = Integer.valueOf(2); - - /** - * Single pixel dotted low underline. - * - * @see #INPUT_METHOD_UNDERLINE - * @since 1.3 - */ - public static final Integer UNDERLINE_LOW_DOTTED = Integer.valueOf(3); - - /** - * Double pixel gray low underline. - * - * @see #INPUT_METHOD_UNDERLINE - * @since 1.3 - */ - public static final Integer UNDERLINE_LOW_GRAY = Integer.valueOf(4); - - /** - * Single pixel dashed low underline. - * - * @see #INPUT_METHOD_UNDERLINE - * @since 1.3 - */ - public static final Integer UNDERLINE_LOW_DASHED = Integer.valueOf(5); - - /** - * Attribute key for swapping foreground and background {@code Paints}. Values - * are instances of {@code Boolean}. The default value is {@code false}, - * which means do not swap colors. - * - *

- * The constant value {@link #SWAP_COLORS_ON} is defined. - * - *

- * If the {@link #FOREGROUND} attribute is set, its {@code Paint} will be used - * as the background, otherwise the {@code Paint} currently on the - * {@code Graphics} will be used. If the {@link #BACKGROUND} attribute is set, - * its {@code Paint} will be used as the foreground, otherwise the system will - * find a contrasting color to the (resolved) background so that the text will - * be visible. - * - * @see #FOREGROUND - * @see #BACKGROUND - */ - public static final TextAttribute SWAP_COLORS = new TextAttribute("swap_colors"); - - /** - * Swap foreground and background. - * - * @see #SWAP_COLORS - * @since 1.3 - */ - public static final Boolean SWAP_COLORS_ON = Boolean.TRUE; - - /** - * Attribute key for converting ASCII decimal digits to other decimal ranges. - * Values are instances of {@link NumericShaper}. The default is {@code null}, - * which means do not perform numeric shaping. - * - *

- * When a numeric shaper is defined, the text is first processed by the shaper - * before any other analysis of the text is performed. - * - *

- * Note: This should have the same value for all the text in the - * paragraph, otherwise the behavior is undetermined. - * - * @see NumericShaper - * @since 1.4 - */ - public static final TextAttribute NUMERIC_SHAPING = new TextAttribute("numeric_shaping"); - - /** - * Attribute key to request kerning. Values are instances of - * {@code Integer}. The default value is {@code 0}, which does not - * request kerning. - * - *

- * The constant value {@link #KERNING_ON} is provided. - * - *

- * The default advances of single characters are not appropriate for some - * character sequences, for example "To" or "AWAY". Without kerning the adjacent - * characters appear to be separated by too much space. Kerning causes selected - * sequences of characters to be spaced differently for a more pleasing visual - * appearance. - * - * @since 1.6 - */ - public static final TextAttribute KERNING = new TextAttribute("kerning"); - - /** - * Request standard kerning. - * - * @see #KERNING - * @since 1.6 - */ - public static final Integer KERNING_ON = Integer.valueOf(1); - - /** - * Attribute key for enabling optional ligatures. Values are instances of - * {@code Integer}. The default value is {@code 0}, which means do not - * use optional ligatures. - * - *

- * The constant value {@link #LIGATURES_ON} is defined. - * - *

- * Ligatures required by the writing system are always enabled. - * - * @since 1.6 - */ - public static final TextAttribute LIGATURES = new TextAttribute("ligatures"); - - /** - * Request standard optional ligatures. - * - * @see #LIGATURES - * @since 1.6 - */ - public static final Integer LIGATURES_ON = Integer.valueOf(1); - - /** - * Attribute key to control tracking. Values are instances of - * {@code Number}. The default value is {@code 0}, which means no - * additional tracking. - * - *

- * The constant values {@link #TRACKING_TIGHT} and {@link #TRACKING_LOOSE} are - * provided. - * - *

- * The tracking value is multiplied by the font point size and passed through - * the font transform to determine an additional amount to add to the advance of - * each glyph cluster. Positive tracking values will inhibit formation of - * optional ligatures. Tracking values are typically between {@code -0.1} and - * {@code 0.3}; values outside this range are generally not desirable. - * - * @since 1.6 - */ - public static final TextAttribute TRACKING = new TextAttribute("tracking"); - - /** - * Perform tight tracking. - * - * @see #TRACKING - * @since 1.6 - */ - public static final Float TRACKING_TIGHT = Float.valueOf(-.04f); - - /** - * Perform loose tracking. - * - * @see #TRACKING - * @since 1.6 - */ - public static final Float TRACKING_LOOSE = Float.valueOf(.04f); -} +/* + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved + * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved + * + * The original version of this source code and documentation is + * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary + * of IBM. These materials are provided under terms of a License + * Agreement between Taligent and Sun. This technology is protected + * by multiple US and International patents. + * + * This notice and attribution to Taligent may not be removed. + * Taligent is a registered trademark of Taligent, Inc. + * + */ + +package jdk_internal.bidi; + +import java.io.InvalidObjectException; +import java.util.HashMap; +import java.util.Map; + +import jdk_internal.bidi.AttributedCharacterIterator.Attribute; + +/** + * The {@code TextAttribute} class defines attribute keys and attribute values + * used for text rendering. + *

+ * {@code TextAttribute} instances are used as attribute keys to identify + * attributes in {@link java.awt.Font Font}, {@link java.awt.font.TextLayout + * TextLayout}, {@link java.text.AttributedCharacterIterator + * AttributedCharacterIterator}, and other classes handling text attributes. + * Other constants defined in this class can be used as attribute values. + *

+ * For each text attribute, the documentation provides: + *

    + *
  • the type of its value, + *
  • the relevant predefined constants, if any + *
  • the default effect if the attribute is absent + *
  • the valid values if there are limitations + *
  • a description of the effect. + *
+ * + *

Values

+ *
    + *
  • The values of attributes must always be immutable. + *
  • Where value limitations are given, any value outside of that set is + * reserved for future use; the value will be treated as the default. + *
  • The value {@code null} is treated the same as the default value and + * results in the default behavior. + *
  • If the value is not of the proper type, the attribute will be ignored. + *
  • The identity of the value does not matter, only the actual value. For + * example, {@code TextAttribute.WEIGHT_BOLD} and {@code Float.valueOf(2.0f)} + * indicate the same {@code WEIGHT}. + *
  • Attribute values of type {@code Number} (used for {@code WEIGHT}, + * {@code WIDTH}, {@code POSTURE}, {@code SIZE}, {@code JUSTIFICATION}, and + * {@code TRACKING}) can vary along their natural range and are not restricted + * to the predefined constants. {@code Number.floatValue()} is used to get the + * actual value from the {@code Number}. + *
  • The values for {@code WEIGHT}, {@code WIDTH}, and {@code POSTURE} are + * interpolated by the system, which can select the 'nearest available' font or + * use other techniques to approximate the user's request. + * + *
+ * + *

Summary of attributes

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Key, value type, principal constants, and default value behavior of + * all TextAttributes
Key + * Value Type + * Principal Constants + * Default Value
{@link #FAMILY} + * String + * See Font {@link java.awt.Font#DIALOG DIALOG}, + * {@link java.awt.Font#DIALOG_INPUT DIALOG_INPUT},
+ * {@link java.awt.Font#SERIF SERIF}, {@link java.awt.Font#SANS_SERIF + * SANS_SERIF}, and {@link java.awt.Font#MONOSPACED MONOSPACED}. + *
"Default" (use platform default) + *
{@link #WEIGHT} + * Number + * WEIGHT_REGULAR, WEIGHT_BOLD + * WEIGHT_REGULAR + *
{@link #WIDTH} + * Number + * WIDTH_CONDENSED, WIDTH_REGULAR,
+ * WIDTH_EXTENDED + *
WIDTH_REGULAR + *
{@link #POSTURE} + * Number + * POSTURE_REGULAR, POSTURE_OBLIQUE + * POSTURE_REGULAR + *
{@link #SIZE} + * Number + * none + * 12.0 + *
{@link #TRANSFORM} + * {@link TransformAttribute} + * See TransformAttribute {@link TransformAttribute#IDENTITY IDENTITY} + * TransformAttribute.IDENTITY + *
{@link #SUPERSCRIPT} + * Integer + * SUPERSCRIPT_SUPER, SUPERSCRIPT_SUB + * 0 (use the standard glyphs and metrics) + *
{@link #FONT} + * {@link java.awt.Font} + * none + * null (do not override font resolution) + *
{@link #CHAR_REPLACEMENT} + * {@link GraphicAttribute} + * none + * null (draw text using font glyphs) + *
{@link #FOREGROUND} + * {@link java.awt.Paint} + * none + * null (use current graphics paint) + *
{@link #BACKGROUND} + * {@link java.awt.Paint} + * none + * null (do not render background) + *
{@link #UNDERLINE} + * Integer + * UNDERLINE_ON + * -1 (do not render underline) + *
{@link #STRIKETHROUGH} + * Boolean + * STRIKETHROUGH_ON + * false (do not render strikethrough) + *
{@link #RUN_DIRECTION} + * Boolean + * RUN_DIRECTION_LTR
+ * RUN_DIRECTION_RTL + *
null (use {@link java.text.Bidi} standard default) + *
{@link #BIDI_EMBEDDING} + * Integer + * none + * 0 (use base line direction) + *
{@link #JUSTIFICATION} + * Number + * JUSTIFICATION_FULL + * JUSTIFICATION_FULL + *
{@link #INPUT_METHOD_HIGHLIGHT} + * {@link java.awt.im.InputMethodHighlight},
+ * {@link java.text.Annotation} + *
(see class) + * null (do not apply input highlighting) + *
{@link #INPUT_METHOD_UNDERLINE} + * Integer + * UNDERLINE_LOW_ONE_PIXEL,
+ * UNDERLINE_LOW_TWO_PIXEL + *
-1 (do not render underline) + *
{@link #SWAP_COLORS} + * Boolean + * SWAP_COLORS_ON + * false (do not swap colors) + *
{@link #NUMERIC_SHAPING} + * {@link java.awt.font.NumericShaper} + * none + * null (do not shape digits) + *
{@link #KERNING} + * Integer + * KERNING_ON + * 0 (do not request kerning) + *
{@link #LIGATURES} + * Integer + * LIGATURES_ON + * 0 (do not form optional ligatures) + *
{@link #TRACKING} + * Number + * TRACKING_LOOSE, TRACKING_TIGHT + * 0 (do not add tracking) + *
+ * + * @see java.awt.Font + * @see java.awt.font.TextLayout + * @see java.text.AttributedCharacterIterator + */ +public final class TextAttribute extends Attribute { + + // table of all instances in this class, used by readResolve + private static final Map instanceMap = new HashMap(29); + + /** + * Constructs a {@code TextAttribute} with the specified name. + * + * @param name the attribute name to assign to this {@code TextAttribute} + */ + protected TextAttribute(String name) { + super(name); + if (this.getClass() == TextAttribute.class) { + instanceMap.put(name, this); + } + } + + /** + * Resolves instances being deserialized to the predefined constants. + */ + protected Object readResolve() throws InvalidObjectException { + if (this.getClass() != TextAttribute.class) { + throw new InvalidObjectException("subclass didn't correctly implement readResolve"); + } + + TextAttribute instance = instanceMap.get(getName()); + if (instance != null) { + return instance; + } else { + throw new InvalidObjectException("unknown attribute name"); + } + } + + /** + * Use serialVersionUID from JDK 1.2 for interoperability. 1.2 will throw an + * InvalidObjectException if ever asked to deserialize INPUT_METHOD_UNDERLINE. + * This shouldn't happen in real life. + */ + private static final long serialVersionUID = 7744112784117861702L; + + // + // For use with Font. + // + + /** + * Attribute key for the font name. Values are instances of + * {@code String}. The default value is {@code "Default"}, which causes + * the platform default font family to be used. + * + *

+ * The {@code Font} class defines constants for the logical font names + * {@link java.awt.Font#DIALOG DIALOG}, {@link java.awt.Font#DIALOG_INPUT + * DIALOG_INPUT}, {@link java.awt.Font#SANS_SERIF SANS_SERIF}, + * {@link java.awt.Font#SERIF SERIF}, and {@link java.awt.Font#MONOSPACED + * MONOSPACED}. + * + *

+ * This defines the value passed as {@code name} to the {@code Font} + * constructor. Both logical and physical font names are allowed. If a font with + * the requested name is not found, the default font is used. + * + *

+ * Note: This attribute is unfortunately misnamed, as it specifies the + * face name and not just the family. Thus values such as "Lucida Sans Bold" + * will select that face if it exists. Note, though, that if the requested face + * does not exist, the default will be used with regular weight. The + * "Bold" in the name is part of the face name, not a separate request that the + * font's weight be bold. + *

+ */ + public static final TextAttribute FAMILY = new TextAttribute("family"); + + /** + * Attribute key for the weight of a font. Values are instances of + * {@code Number}. The default value is {@code WEIGHT_REGULAR}. + * + *

+ * Several constant values are provided, see {@link #WEIGHT_EXTRA_LIGHT}, + * {@link #WEIGHT_LIGHT}, {@link #WEIGHT_DEMILIGHT}, {@link #WEIGHT_REGULAR}, + * {@link #WEIGHT_SEMIBOLD}, {@link #WEIGHT_MEDIUM}, {@link #WEIGHT_DEMIBOLD}, + * {@link #WEIGHT_BOLD}, {@link #WEIGHT_HEAVY}, {@link #WEIGHT_EXTRABOLD}, and + * {@link #WEIGHT_ULTRABOLD}. The value {@code WEIGHT_BOLD} corresponds to the + * style value {@code Font.BOLD} as passed to the {@code Font} constructor. + * + *

+ * The value is roughly the ratio of the stem width to that of the regular + * weight. + * + *

+ * The system can interpolate the provided value. + */ + public static final TextAttribute WEIGHT = new TextAttribute("weight"); + + /** + * The lightest predefined weight. + * + * @see #WEIGHT + */ + public static final Float WEIGHT_EXTRA_LIGHT = Float.valueOf(0.5f); + + /** + * The standard light weight. + * + * @see #WEIGHT + */ + public static final Float WEIGHT_LIGHT = Float.valueOf(0.75f); + + /** + * An intermediate weight between {@code WEIGHT_LIGHT} and + * {@code WEIGHT_STANDARD}. + * + * @see #WEIGHT + */ + public static final Float WEIGHT_DEMILIGHT = Float.valueOf(0.875f); + + /** + * The standard weight. This is the default value for {@code WEIGHT}. + * + * @see #WEIGHT + */ + public static final Float WEIGHT_REGULAR = Float.valueOf(1.0f); + + /** + * A moderately heavier weight than {@code WEIGHT_REGULAR}. + * + * @see #WEIGHT + */ + public static final Float WEIGHT_SEMIBOLD = Float.valueOf(1.25f); + + /** + * An intermediate weight between {@code WEIGHT_REGULAR} and + * {@code WEIGHT_BOLD}. + * + * @see #WEIGHT + */ + public static final Float WEIGHT_MEDIUM = Float.valueOf(1.5f); + + /** + * A moderately lighter weight than {@code WEIGHT_BOLD}. + * + * @see #WEIGHT + */ + public static final Float WEIGHT_DEMIBOLD = Float.valueOf(1.75f); + + /** + * The standard bold weight. + * + * @see #WEIGHT + */ + public static final Float WEIGHT_BOLD = Float.valueOf(2.0f); + + /** + * A moderately heavier weight than {@code WEIGHT_BOLD}. + * + * @see #WEIGHT + */ + public static final Float WEIGHT_HEAVY = Float.valueOf(2.25f); + + /** + * An extra heavy weight. + * + * @see #WEIGHT + */ + public static final Float WEIGHT_EXTRABOLD = Float.valueOf(2.5f); + + /** + * The heaviest predefined weight. + * + * @see #WEIGHT + */ + public static final Float WEIGHT_ULTRABOLD = Float.valueOf(2.75f); + + /** + * Attribute key for the width of a font. Values are instances of + * {@code Number}. The default value is {@code WIDTH_REGULAR}. + * + *

+ * Several constant values are provided, see {@link #WIDTH_CONDENSED}, + * {@link #WIDTH_SEMI_CONDENSED}, {@link #WIDTH_REGULAR}, + * {@link #WIDTH_SEMI_EXTENDED}, {@link #WIDTH_EXTENDED}. + * + *

+ * The value is roughly the ratio of the advance width to that of the regular + * width. + * + *

+ * The system can interpolate the provided value. + */ + public static final TextAttribute WIDTH = new TextAttribute("width"); + + /** + * The most condensed predefined width. + * + * @see #WIDTH + */ + public static final Float WIDTH_CONDENSED = Float.valueOf(0.75f); + + /** + * A moderately condensed width. + * + * @see #WIDTH + */ + public static final Float WIDTH_SEMI_CONDENSED = Float.valueOf(0.875f); + + /** + * The standard width. This is the default value for {@code WIDTH}. + * + * @see #WIDTH + */ + public static final Float WIDTH_REGULAR = Float.valueOf(1.0f); + + /** + * A moderately extended width. + * + * @see #WIDTH + */ + public static final Float WIDTH_SEMI_EXTENDED = Float.valueOf(1.25f); + + /** + * The most extended predefined width. + * + * @see #WIDTH + */ + public static final Float WIDTH_EXTENDED = Float.valueOf(1.5f); + + /** + * Attribute key for the posture of a font. Values are instances of + * {@code Number}. The default value is {@code POSTURE_REGULAR}. + * + *

+ * Two constant values are provided, {@link #POSTURE_REGULAR} and + * {@link #POSTURE_OBLIQUE}. The value {@code POSTURE_OBLIQUE} corresponds to + * the style value {@code Font.ITALIC} as passed to the {@code Font} + * constructor. + * + *

+ * The value is roughly the slope of the stems of the font, expressed as the run + * over the rise. Positive values lean right. + * + *

+ * The system can interpolate the provided value. + * + *

+ * This will affect the font's italic angle as returned by + * {@code Font.getItalicAngle}. + * + * @see java.awt.Font#getItalicAngle() + */ + public static final TextAttribute POSTURE = new TextAttribute("posture"); + + /** + * The standard posture, upright. This is the default value for {@code POSTURE}. + * + * @see #POSTURE + */ + public static final Float POSTURE_REGULAR = Float.valueOf(0.0f); + + /** + * The standard italic posture. + * + * @see #POSTURE + */ + public static final Float POSTURE_OBLIQUE = Float.valueOf(0.20f); + + /** + * Attribute key for the font size. Values are instances of + * {@code Number}. The default value is 12pt. + * + *

+ * This corresponds to the {@code size} parameter to the {@code Font} + * constructor. + * + *

+ * Very large or small sizes will impact rendering performance, and the + * rendering system might not render text at these sizes. Negative sizes are + * illegal and result in the default size. + * + *

+ * Note that the appearance and metrics of a 12pt font with a 2x transform might + * be different than that of a 24 point font with no transform. + */ + public static final TextAttribute SIZE = new TextAttribute("size"); + + /** + * Attribute key for the transform of a font. Values are instances of + * {@code TransformAttribute}. The default value is + * {@code TransformAttribute.IDENTITY}. + * + *

+ * The {@code TransformAttribute} class defines the constant + * {@link TransformAttribute#IDENTITY IDENTITY}. + * + *

+ * This corresponds to the transform passed to + * {@code Font.deriveFont(AffineTransform)}. Since that transform is mutable and + * {@code TextAttribute} values must not be, the {@code TransformAttribute} + * wrapper class is used. + * + *

+ * The primary intent is to support scaling and skewing, though other effects + * are possible. + *

+ * + *

+ * Some transforms will cause the baseline to be rotated and/or shifted. The + * text and the baseline are transformed together so that the text follows the + * new baseline. For example, with text on a horizontal baseline, the new + * baseline follows the direction of the unit x vector passed through the + * transform. Text metrics are measured against this new baseline. So, for + * example, with other things being equal, text rendered with a rotated + * TRANSFORM and an unrotated TRANSFORM will measure as having the same ascent, + * descent, and advance. + *

+ * + *

+ * In styled text, the baselines for each such run are aligned one after the + * other to potentially create a non-linear baseline for the entire run of text. + * For more information, see {@link TextLayout#getLayoutPath}. + *

+ * + * @see TransformAttribute + * @see java.awt.geom.AffineTransform + */ + public static final TextAttribute TRANSFORM = new TextAttribute("transform"); + + /** + * Attribute key for superscripting and subscripting. Values are instances of + * {@code Integer}. The default value is 0, which means that no + * superscript or subscript is used. + * + *

+ * Two constant values are provided, see {@link #SUPERSCRIPT_SUPER} and + * {@link #SUPERSCRIPT_SUB}. These have the values 1 and -1 respectively. Values + * of greater magnitude define greater levels of superscript or subscripting, + * for example, 2 corresponds to super-superscript, 3 to + * super-super-superscript, and similarly for negative values and subscript, up + * to a level of 7 (or -7). Values beyond this range are reserved; behavior is + * platform-dependent. + * + *

+ * {@code SUPERSCRIPT} can impact the ascent and descent of a font. The ascent + * and descent can never become negative, however. + */ + public static final TextAttribute SUPERSCRIPT = new TextAttribute("superscript"); + + /** + * Standard superscript. + * + * @see #SUPERSCRIPT + */ + public static final Integer SUPERSCRIPT_SUPER = Integer.valueOf(1); + + /** + * Standard subscript. + * + * @see #SUPERSCRIPT + */ + public static final Integer SUPERSCRIPT_SUB = Integer.valueOf(-1); + + /** + * Attribute key used to provide the font to use to render text. Values are + * instances of {@link java.awt.Font}. The default value is null, indicating + * that normal resolution of a {@code Font} from attributes should be performed. + * + *

+ * {@code TextLayout} and {@code AttributedCharacterIterator} work in terms of + * {@code Maps} of {@code TextAttributes}. Normally, all the attributes are + * examined and used to select and configure a {@code Font} instance. If a + * {@code FONT} attribute is present, though, its associated {@code Font} will + * be used. This provides a way for users to override the resolution of font + * attributes into a {@code Font}, or force use of a particular {@code Font} + * instance. This also allows users to specify subclasses of {@code Font} in + * cases where a {@code Font} can be subclassed. + * + *

+ * {@code FONT} is used for special situations where clients already have a + * {@code Font} instance but still need to use {@code Map}-based APIs. + * Typically, there will be no other attributes in the {@code Map} except the + * {@code FONT} attribute. With {@code Map}-based APIs the common case is to + * specify all attributes individually, so {@code FONT} is not needed or + * desirable. + * + *

+ * However, if both {@code FONT} and other attributes are present in the + * {@code Map}, the rendering system will merge the attributes defined in the + * {@code Font} with the additional attributes. This merging process classifies + * {@code TextAttributes} into two groups. One group, the 'primary' group, is + * considered fundamental to the selection and metric behavior of a font. These + * attributes are {@code FAMILY}, {@code WEIGHT}, {@code WIDTH}, + * {@code POSTURE}, {@code SIZE}, {@code TRANSFORM}, {@code SUPERSCRIPT}, and + * {@code TRACKING}. The other group, the 'secondary' group, consists of all + * other defined attributes, with the exception of {@code FONT} itself. + * + *

+ * To generate the new {@code Map}, first the {@code Font} is obtained from the + * {@code FONT} attribute, and all of its attributes extracted into a + * new {@code Map}. Then only the secondary attributes from the + * original {@code Map} are added to those in the new {@code Map}. Thus the + * values of primary attributes come solely from the {@code Font}, and the + * values of secondary attributes originate with the {@code Font} but can be + * overridden by other values in the {@code Map}. + * + *

+ * Note:{@code Font's Map}-based constructor and {@code deriveFont} + * methods do not process the {@code FONT} attribute, as these are used to + * create new {@code Font} objects. Instead, {@link java.awt.Font#getFont(Map) + * Font.getFont(Map)} should be used to handle the {@code FONT} attribute. + * + * @see java.awt.Font + */ + public static final TextAttribute FONT = new TextAttribute("font"); + + /** + * Attribute key for a user-defined glyph to display in lieu of the font's + * standard glyph for a character. Values are instances of GraphicAttribute. The + * default value is null, indicating that the standard glyphs provided by the + * font should be used. + * + *

+ * This attribute is used to reserve space for a graphic or other component + * embedded in a line of text. It is required for correct positioning of + * 'inline' components within a line when bidirectional reordering (see + * {@link java.text.Bidi}) is performed. Each character (Unicode code point) + * will be rendered using the provided GraphicAttribute. Typically, the + * characters to which this attribute is applied should be + * \uFFFC. + * + *

+ * The GraphicAttribute determines the logical and visual bounds of the text; + * the actual Font values are ignored. + * + * @see GraphicAttribute + */ + public static final TextAttribute CHAR_REPLACEMENT = new TextAttribute("char_replacement"); + + // + // Adornments added to text. + // + + /** + * Attribute key for the paint used to render the text. Values are instances of + * {@code Paint}. The default value is null, indicating that the + * {@code Paint} set on the {@code Graphics2D} at the time of rendering is used. + * + *

+ * Glyphs will be rendered using this {@code Paint} regardless of the + * {@code Paint} value set on the {@code Graphics} (but see + * {@link #SWAP_COLORS}). + * + * @see java.awt.Paint + * @see #SWAP_COLORS + */ + public static final TextAttribute FOREGROUND = new TextAttribute("foreground"); + + /** + * Attribute key for the paint used to render the background of the text. Values + * are instances of {@code Paint}. The default value is null, indicating + * that the background should not be rendered. + * + *

+ * The logical bounds of the text will be filled using this {@code Paint}, and + * then the text will be rendered on top of it (but see {@link #SWAP_COLORS}). + * + *

+ * The visual bounds of the text is extended to include the logical bounds, if + * necessary. The outline is not affected. + * + * @see java.awt.Paint + * @see #SWAP_COLORS + */ + public static final TextAttribute BACKGROUND = new TextAttribute("background"); + + /** + * Attribute key for underline. Values are instances of {@code Integer}. + * The default value is -1, which means no underline. + * + *

+ * The constant value {@link #UNDERLINE_ON} is provided. + * + *

+ * The underline affects both the visual bounds and the outline of the text. + */ + public static final TextAttribute UNDERLINE = new TextAttribute("underline"); + + /** + * Standard underline. + * + * @see #UNDERLINE + */ + public static final Integer UNDERLINE_ON = Integer.valueOf(0); + + /** + * Attribute key for strikethrough. Values are instances of + * {@code Boolean}. The default value is {@code false}, which means no + * strikethrough. + * + *

+ * The constant value {@link #STRIKETHROUGH_ON} is provided. + * + *

+ * The strikethrough affects both the visual bounds and the outline of the text. + */ + public static final TextAttribute STRIKETHROUGH = new TextAttribute("strikethrough"); + + /** + * A single strikethrough. + * + * @see #STRIKETHROUGH + */ + public static final Boolean STRIKETHROUGH_ON = Boolean.TRUE; + + // + // Attributes use to control layout of text on a line. + // + + /** + * Attribute key for the run direction of the line. Values are instances of + * {@code Boolean}. The default value is null, which indicates that the + * standard Bidi algorithm for determining run direction should be used with the + * value {@link java.text.Bidi#DIRECTION_DEFAULT_LEFT_TO_RIGHT}. + * + *

+ * The constants {@link #RUN_DIRECTION_RTL} and {@link #RUN_DIRECTION_LTR} are + * provided. + * + *

+ * This determines the value passed to the {@link java.text.Bidi} constructor to + * select the primary direction of the text in the paragraph. + * + *

+ * Note: This attribute should have the same value for all the text in + * a paragraph, otherwise the behavior is undetermined. + * + * @see java.text.Bidi + */ + public static final TextAttribute RUN_DIRECTION = new TextAttribute("run_direction"); + + /** + * Left-to-right run direction. + * + * @see #RUN_DIRECTION + */ + public static final Boolean RUN_DIRECTION_LTR = Boolean.FALSE; + + /** + * Right-to-left run direction. + * + * @see #RUN_DIRECTION + */ + public static final Boolean RUN_DIRECTION_RTL = Boolean.TRUE; + + /** + * Attribute key for the embedding level of the text. Values are instances of + * {@code Integer}. The default value is {@code null}, indicating that + * the Bidirectional algorithm should run without explicit embeddings. + * + *

+ * Positive values 1 through 61 are embedding levels, negative values + * -1 through -61 are override levels. The value 0 means that the base + * line direction is used. These levels are passed in the embedding levels array + * to the {@link java.text.Bidi} constructor. + * + *

+ * Note: When this attribute is present anywhere in a paragraph, then + * any Unicode bidi control characters (RLO, LRO, RLE, LRE, and PDF) in the + * paragraph are disregarded, and runs of text where this attribute is not + * present are treated as though it were present and had the value 0. + * + * @see java.text.Bidi + */ + public static final TextAttribute BIDI_EMBEDDING = new TextAttribute("bidi_embedding"); + + /** + * Attribute key for the justification of a paragraph. Values are instances of + * {@code Number}. The default value is 1, indicating that justification + * should use the full width provided. Values are pinned to the range [0..1]. + * + *

+ * The constants {@link #JUSTIFICATION_FULL} and {@link #JUSTIFICATION_NONE} are + * provided. + * + *

+ * Specifies the fraction of the extra space to use when justification is + * requested on a {@code TextLayout}. For example, if the line is 50 points wide + * and it is requested to justify to 70 points, a value of 0.75 will pad to use + * three-quarters of the remaining space, or 15 points, so that the resulting + * line will be 65 points in length. + * + *

+ * Note: This should have the same value for all the text in a + * paragraph, otherwise the behavior is undetermined. + * + * @see TextLayout#getJustifiedLayout + */ + public static final TextAttribute JUSTIFICATION = new TextAttribute("justification"); + + /** + * Justify the line to the full requested width. This is the default value for + * {@code JUSTIFICATION}. + * + * @see #JUSTIFICATION + */ + public static final Float JUSTIFICATION_FULL = Float.valueOf(1.0f); + + /** + * Do not allow the line to be justified. + * + * @see #JUSTIFICATION + */ + public static final Float JUSTIFICATION_NONE = Float.valueOf(0.0f); + + // + // For use by input method. + // + + /** + * Attribute key for input method highlight styles. + * + *

+ * Values are instances of {@link java.awt.im.InputMethodHighlight} or + * {@link java.text.Annotation}. The default value is {@code null}, which means + * that input method styles should not be applied before rendering. + * + *

+ * If adjacent runs of text with the same {@code InputMethodHighlight} need to + * be rendered separately, the {@code InputMethodHighlights} should be wrapped + * in {@code Annotation} instances. + * + *

+ * Input method highlights are used while text is being composed by an input + * method. Text editing components should retain them even if they generally + * only deal with unstyled text, and make them available to the drawing + * routines. + * + * @see java.awt.Font + * @see java.awt.im.InputMethodHighlight + * @see java.text.Annotation + */ + public static final TextAttribute INPUT_METHOD_HIGHLIGHT = new TextAttribute("input method highlight"); + + /** + * Attribute key for input method underlines. Values are instances of + * {@code Integer}. The default value is {@code -1}, which means no + * underline. + * + *

+ * Several constant values are provided, see {@link #UNDERLINE_LOW_ONE_PIXEL}, + * {@link #UNDERLINE_LOW_TWO_PIXEL}, {@link #UNDERLINE_LOW_DOTTED}, + * {@link #UNDERLINE_LOW_GRAY}, and {@link #UNDERLINE_LOW_DASHED}. + * + *

+ * This may be used in conjunction with {@link #UNDERLINE} if desired. The + * primary purpose is for use by input methods. Other use of these underlines + * for simple ornamentation might confuse users. + * + *

+ * The input method underline affects both the visual bounds and the outline of + * the text. + * + * @since 1.3 + */ + public static final TextAttribute INPUT_METHOD_UNDERLINE = new TextAttribute("input method underline"); + + /** + * Single pixel solid low underline. + * + * @see #INPUT_METHOD_UNDERLINE + * @since 1.3 + */ + public static final Integer UNDERLINE_LOW_ONE_PIXEL = Integer.valueOf(1); + + /** + * Double pixel solid low underline. + * + * @see #INPUT_METHOD_UNDERLINE + * @since 1.3 + */ + public static final Integer UNDERLINE_LOW_TWO_PIXEL = Integer.valueOf(2); + + /** + * Single pixel dotted low underline. + * + * @see #INPUT_METHOD_UNDERLINE + * @since 1.3 + */ + public static final Integer UNDERLINE_LOW_DOTTED = Integer.valueOf(3); + + /** + * Double pixel gray low underline. + * + * @see #INPUT_METHOD_UNDERLINE + * @since 1.3 + */ + public static final Integer UNDERLINE_LOW_GRAY = Integer.valueOf(4); + + /** + * Single pixel dashed low underline. + * + * @see #INPUT_METHOD_UNDERLINE + * @since 1.3 + */ + public static final Integer UNDERLINE_LOW_DASHED = Integer.valueOf(5); + + /** + * Attribute key for swapping foreground and background {@code Paints}. Values + * are instances of {@code Boolean}. The default value is {@code false}, + * which means do not swap colors. + * + *

+ * The constant value {@link #SWAP_COLORS_ON} is defined. + * + *

+ * If the {@link #FOREGROUND} attribute is set, its {@code Paint} will be used + * as the background, otherwise the {@code Paint} currently on the + * {@code Graphics} will be used. If the {@link #BACKGROUND} attribute is set, + * its {@code Paint} will be used as the foreground, otherwise the system will + * find a contrasting color to the (resolved) background so that the text will + * be visible. + * + * @see #FOREGROUND + * @see #BACKGROUND + */ + public static final TextAttribute SWAP_COLORS = new TextAttribute("swap_colors"); + + /** + * Swap foreground and background. + * + * @see #SWAP_COLORS + * @since 1.3 + */ + public static final Boolean SWAP_COLORS_ON = Boolean.TRUE; + + /** + * Attribute key for converting ASCII decimal digits to other decimal ranges. + * Values are instances of {@link NumericShaper}. The default is {@code null}, + * which means do not perform numeric shaping. + * + *

+ * When a numeric shaper is defined, the text is first processed by the shaper + * before any other analysis of the text is performed. + * + *

+ * Note: This should have the same value for all the text in the + * paragraph, otherwise the behavior is undetermined. + * + * @see NumericShaper + * @since 1.4 + */ + public static final TextAttribute NUMERIC_SHAPING = new TextAttribute("numeric_shaping"); + + /** + * Attribute key to request kerning. Values are instances of + * {@code Integer}. The default value is {@code 0}, which does not + * request kerning. + * + *

+ * The constant value {@link #KERNING_ON} is provided. + * + *

+ * The default advances of single characters are not appropriate for some + * character sequences, for example "To" or "AWAY". Without kerning the adjacent + * characters appear to be separated by too much space. Kerning causes selected + * sequences of characters to be spaced differently for a more pleasing visual + * appearance. + * + * @since 1.6 + */ + public static final TextAttribute KERNING = new TextAttribute("kerning"); + + /** + * Request standard kerning. + * + * @see #KERNING + * @since 1.6 + */ + public static final Integer KERNING_ON = Integer.valueOf(1); + + /** + * Attribute key for enabling optional ligatures. Values are instances of + * {@code Integer}. The default value is {@code 0}, which means do not + * use optional ligatures. + * + *

+ * The constant value {@link #LIGATURES_ON} is defined. + * + *

+ * Ligatures required by the writing system are always enabled. + * + * @since 1.6 + */ + public static final TextAttribute LIGATURES = new TextAttribute("ligatures"); + + /** + * Request standard optional ligatures. + * + * @see #LIGATURES + * @since 1.6 + */ + public static final Integer LIGATURES_ON = Integer.valueOf(1); + + /** + * Attribute key to control tracking. Values are instances of + * {@code Number}. The default value is {@code 0}, which means no + * additional tracking. + * + *

+ * The constant values {@link #TRACKING_TIGHT} and {@link #TRACKING_LOOSE} are + * provided. + * + *

+ * The tracking value is multiplied by the font point size and passed through + * the font transform to determine an additional amount to add to the advance of + * each glyph cluster. Positive tracking values will inhibit formation of + * optional ligatures. Tracking values are typically between {@code -0.1} and + * {@code 0.3}; values outside this range are generally not desirable. + * + * @since 1.6 + */ + public static final TextAttribute TRACKING = new TextAttribute("tracking"); + + /** + * Perform tight tracking. + * + * @see #TRACKING + * @since 1.6 + */ + public static final Float TRACKING_TIGHT = Float.valueOf(-.04f); + + /** + * Perform loose tracking. + * + * @see #TRACKING + * @since 1.6 + */ + public static final Float TRACKING_LOOSE = Float.valueOf(.04f); +} diff --git a/src/main/java/jdk_internal/icu/impl/BMPSet.java b/src/main/java/jdk_internal/icu/impl/BMPSet.java index 78c62aa6..f7c1694c 100755 --- a/src/main/java/jdk_internal/icu/impl/BMPSet.java +++ b/src/main/java/jdk_internal/icu/impl/BMPSet.java @@ -1,530 +1,530 @@ -/* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ****************************************************************************** - * - * Copyright (C) 2009-2014, International Business Machines - * Corporation and others. All Rights Reserved. - * - ****************************************************************************** - */ - -package jdk_internal.icu.impl; - -import jdk_internal.icu.text.UnicodeSet.SpanCondition; -import jdk_internal.icu.util.OutputInt; - -/** - * Helper class for frozen UnicodeSets, implements contains() and span() - * optimized for BMP code points. - * - * 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: Call contains() on the - * parent set. - */ -public final class BMPSet { - - /** - * One boolean ('true' or 'false') per Latin-1 character. - */ - private boolean[] latin1Contains; - - /** - * One bit per code point from U+0000..U+07FF. The bits are organized - * vertically; consecutive code points correspond to the same bit positions in - * consecutive table words. With code point parts lead=c{10..6} trail=c{5..0} it - * is set.contains(c)==(table7FF[trail] bit lead) - * - * Bits for 0..7F (non-shortest forms) are set to the result of contains(FFFD) - * for faster validity checking at runtime. - */ - private int[] table7FF; - - /** - * One bit per 64 BMP code points. The bits are organized vertically; - * consecutive 64-code point blocks correspond to the same bit position in - * consecutive table words. With code point parts lead=c{15..12} t1=c{11..6} - * test bits (lead+16) and lead in bmpBlockBits[t1]. If the upper bit is 0, then - * the lower bit indicates if contains(c) for all code points in the 64-block. - * If the upper bit is 1, then the block is mixed and set.contains(c) must be - * called. - * - * Bits for 0..7FF (non-shortest forms) and D800..DFFF are set to the result of - * contains(FFFD) for faster validity checking at runtime. - */ - private int[] bmpBlockBits; - - /** - * Inversion list indexes for restricted binary searches in findCodePoint(), - * from findCodePoint(U+0800, U+1000, U+2000, .., U+F000, U+10000). U+0800 is - * the first 3-byte-UTF-8 code point. Code points below U+0800 are always looked - * up in the bit tables. The last pair of indexes is for finding supplementary - * code points. - */ - private int[] list4kStarts; - - /** - * The inversion list of the parent set, for the slower contains() - * implementation for mixed BMP blocks and for supplementary code points. The - * list is terminated with list[listLength-1]=0x110000. - */ - private final int[] list; - private final int listLength; // length used; list may be longer to minimize reallocs - - public BMPSet(final int[] parentList, int parentListLength) { - list = parentList; - listLength = parentListLength; - latin1Contains = new boolean[0x100]; - table7FF = new int[64]; - bmpBlockBits = new int[64]; - list4kStarts = new int[18]; - - /* - * 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); - int i; - for (i = 1; i <= 0x10; ++i) { - list4kStarts[i] = findCodePoint(i << 12, list4kStarts[i - 1], listLength - 1); - } - list4kStarts[0x11] = listLength - 1; - - initBits(); - } - - public boolean contains(int c) { - if (c <= 0xff) { - return (latin1Contains[c]); - } else if (c <= 0x7ff) { - return ((table7FF[c & 0x3f] & (1 << (c >> 6))) != 0); - } else if (c < 0xd800 || (c >= 0xe000 && c <= 0xffff)) { - int lead = c >> 12; - int 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 (0 != twoBits); - } else { - // Look up the code point in its 4k block of code points. - return containsSlow(c, list4kStarts[lead], list4kStarts[lead + 1]); - } - } else if (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; - } - } - - /** - * Span the initial substring for which each character c has - * spanCondition==contains(c). It must be spanCondition==0 or 1. - * - * @param start The start index - * @param outCount If not null: Receives the number of code points in the span. - * @return the limit (exclusive end) of the span - * - * NOTE: to reduce the overhead of function call to contains(c), it is - * manually inlined here. Check for sufficient length for trail unit for - * each surrogate pair. Handle single surrogates as surrogate code - * points as usual in ICU. - */ - public final int span(CharSequence s, int start, SpanCondition spanCondition, OutputInt outCount) { - char c, c2; - int i = start; - int limit = s.length(); - int numSupplementary = 0; - if (SpanCondition.NOT_CONTAINED != spanCondition) { - // span - while (i < limit) { - c = s.charAt(i); - if (c <= 0xff) { - if (!latin1Contains[c]) { - break; - } - } else if (c <= 0x7ff) { - if ((table7FF[c & 0x3f] & (1 << (c >> 6))) == 0) { - break; - } - } else if (c < 0xd800 || c >= 0xdc00 || (i + 1) == limit || (c2 = s.charAt(i + 1)) < 0xdc00 - || c2 >= 0xe000) { - int lead = c >> 12; - int 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 { - // surrogate pair - int supplementary = UCharacterProperty.getRawSupplementary(c, c2); - if (!containsSlow(supplementary, list4kStarts[0x10], list4kStarts[0x11])) { - break; - } - ++numSupplementary; - ++i; - } - ++i; - } - } else { - // span not - while (i < limit) { - c = s.charAt(i); - if (c <= 0xff) { - if (latin1Contains[c]) { - break; - } - } else if (c <= 0x7ff) { - if ((table7FF[c & 0x3f] & (1 << (c >> 6))) != 0) { - break; - } - } else if (c < 0xd800 || c >= 0xdc00 || (i + 1) == limit || (c2 = s.charAt(i + 1)) < 0xdc00 - || c2 >= 0xe000) { - int lead = c >> 12; - int 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 { - // surrogate pair - int supplementary = UCharacterProperty.getRawSupplementary(c, c2); - if (containsSlow(supplementary, list4kStarts[0x10], list4kStarts[0x11])) { - break; - } - ++numSupplementary; - ++i; - } - ++i; - } - } - if (outCount != null) { - int spanLength = i - start; - outCount.value = spanLength - numSupplementary; // number of code points - } - return i; - } - - /** - * Symmetrical with span(). Span the trailing substring for which each character - * c has spanCondition==contains(c). It must be s.length >= limit and - * spanCondition==0 or 1. - * - * @return The string index which starts the span (i.e. inclusive). - */ - public final int spanBack(CharSequence s, int limit, SpanCondition spanCondition) { - char c, c2; - - if (SpanCondition.NOT_CONTAINED != spanCondition) { - // span - for (;;) { - c = s.charAt(--limit); - if (c <= 0xff) { - if (!latin1Contains[c]) { - break; - } - } else if (c <= 0x7ff) { - if ((table7FF[c & 0x3f] & (1 << (c >> 6))) == 0) { - break; - } - } else if (c < 0xd800 || c < 0xdc00 || 0 == limit || (c2 = s.charAt(limit - 1)) < 0xd800 - || c2 >= 0xdc00) { - int lead = c >> 12; - int 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 { - // surrogate pair - int supplementary = UCharacterProperty.getRawSupplementary(c2, c); - if (!containsSlow(supplementary, list4kStarts[0x10], list4kStarts[0x11])) { - break; - } - --limit; - } - if (0 == limit) { - return 0; - } - } - } else { - // span not - for (;;) { - c = s.charAt(--limit); - if (c <= 0xff) { - if (latin1Contains[c]) { - break; - } - } else if (c <= 0x7ff) { - if ((table7FF[c & 0x3f] & (1 << (c >> 6))) != 0) { - break; - } - } else if (c < 0xd800 || c < 0xdc00 || 0 == limit || (c2 = s.charAt(limit - 1)) < 0xd800 - || c2 >= 0xdc00) { - int lead = c >> 12; - int 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 { - // surrogate pair - int supplementary = UCharacterProperty.getRawSupplementary(c2, c); - if (containsSlow(supplementary, list4kStarts[0x10], list4kStarts[0x11])) { - break; - } - --limit; - } - if (0 == limit) { - return 0; - } - } - } - return limit + 1; - } - - /** - * Set bits in a bit rectangle in "vertical" bit organization. - * start> 6; // Named for UTF-8 2-byte lead byte with upper 5 bits. - int trail = start & 0x3f; // Named for UTF-8 2-byte trail byte with lower 6 bits. - - // Set one bit indicating an all-one block. - int bits = 1 << lead; - if ((start + 1) == limit) { // Single-character shortcut. - table[trail] |= bits; - return; - } - - int limitLead = limit >> 6; - int limitTrail = limit & 0x3f; - - if (lead == limitLead) { - // Partial vertical bit column. - while (trail < limitTrail) { - table[trail++] |= bits; - } - } else { - // Partial vertical bit column, - // followed by a bit rectangle, - // followed by another partial vertical bit column. - if (trail > 0) { - do { - table[trail++] |= bits; - } while (trail < 64); - ++lead; - } - if (lead < limitLead) { - bits = ~((1 << lead) - 1); - if (limitLead < 0x20) { - bits &= (1 << limitLead) - 1; - } - for (trail = 0; trail < 64; ++trail) { - table[trail] |= bits; - } - } - // limit<=0x800. If limit==0x800 then limitLead=32 and limitTrail=0. - // In that case, bits=1<= 0x100) { - break; - } - do { - latin1Contains[start++] = true; - } while (start < limit && start < 0x100); - } while (limit <= 0x100); - - // Set table7FF[]. - while (start < 0x800) { - set32x64Bits(table7FF, start, limit <= 0x800 ? limit : 0x800); - if (limit > 0x800) { - start = 0x800; - break; - } - - start = list[listIndex++]; - if (listIndex < listLength) { - limit = list[listIndex++]; - } else { - limit = 0x110000; - } - } - - // Set bmpBlockBits[]. - int minStart = 0x800; - while (start < 0x10000) { - if (limit > 0x10000) { - limit = 0x10000; - } - - if (start < minStart) { - start = minStart; - } - if (start < limit) { // Else: Another range entirely in a known mixed-value block. - if (0 != (start & 0x3f)) { - // Mixed-value block of 64 code points. - 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 < limit) { - if (start < (limit & ~0x3f)) { - // Multiple all-ones blocks of 64 code points each. - set32x64Bits(bmpBlockBits, start >> 6, limit >> 6); - } - - if (0 != (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 < listLength) { - limit = list[listIndex++]; - } else { - limit = 0x110000; - } - } - } - - /** - * Same as UnicodeSet.findCodePoint(int c) 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<=hi= hi || c >= list[hi - 1]) - return hi; - // invariant: c >= list[lo] - // invariant: c < list[hi] - for (;;) { - int i = (lo + hi) >>> 1; - if (i == lo) { - break; // Found! - } else if (c < list[i]) { - hi = i; - } else { - lo = i; - } - } - return hi; - } - - private final boolean containsSlow(int c, int lo, int hi) { - return (0 != (findCodePoint(c, lo, hi) & 1)); - } -} +/* + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ****************************************************************************** + * + * Copyright (C) 2009-2014, International Business Machines + * Corporation and others. All Rights Reserved. + * + ****************************************************************************** + */ + +package jdk_internal.icu.impl; + +import jdk_internal.icu.text.UnicodeSet.SpanCondition; +import jdk_internal.icu.util.OutputInt; + +/** + * Helper class for frozen UnicodeSets, implements contains() and span() + * optimized for BMP code points. + * + * 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: Call contains() on the + * parent set. + */ +public final class BMPSet { + + /** + * One boolean ('true' or 'false') per Latin-1 character. + */ + private boolean[] latin1Contains; + + /** + * One bit per code point from U+0000..U+07FF. The bits are organized + * vertically; consecutive code points correspond to the same bit positions in + * consecutive table words. With code point parts lead=c{10..6} trail=c{5..0} it + * is set.contains(c)==(table7FF[trail] bit lead) + * + * Bits for 0..7F (non-shortest forms) are set to the result of contains(FFFD) + * for faster validity checking at runtime. + */ + private int[] table7FF; + + /** + * One bit per 64 BMP code points. The bits are organized vertically; + * consecutive 64-code point blocks correspond to the same bit position in + * consecutive table words. With code point parts lead=c{15..12} t1=c{11..6} + * test bits (lead+16) and lead in bmpBlockBits[t1]. If the upper bit is 0, then + * the lower bit indicates if contains(c) for all code points in the 64-block. + * If the upper bit is 1, then the block is mixed and set.contains(c) must be + * called. + * + * Bits for 0..7FF (non-shortest forms) and D800..DFFF are set to the result of + * contains(FFFD) for faster validity checking at runtime. + */ + private int[] bmpBlockBits; + + /** + * Inversion list indexes for restricted binary searches in findCodePoint(), + * from findCodePoint(U+0800, U+1000, U+2000, .., U+F000, U+10000). U+0800 is + * the first 3-byte-UTF-8 code point. Code points below U+0800 are always looked + * up in the bit tables. The last pair of indexes is for finding supplementary + * code points. + */ + private int[] list4kStarts; + + /** + * The inversion list of the parent set, for the slower contains() + * implementation for mixed BMP blocks and for supplementary code points. The + * list is terminated with list[listLength-1]=0x110000. + */ + private final int[] list; + private final int listLength; // length used; list may be longer to minimize reallocs + + public BMPSet(final int[] parentList, int parentListLength) { + list = parentList; + listLength = parentListLength; + latin1Contains = new boolean[0x100]; + table7FF = new int[64]; + bmpBlockBits = new int[64]; + list4kStarts = new int[18]; + + /* + * 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); + int i; + for (i = 1; i <= 0x10; ++i) { + list4kStarts[i] = findCodePoint(i << 12, list4kStarts[i - 1], listLength - 1); + } + list4kStarts[0x11] = listLength - 1; + + initBits(); + } + + public boolean contains(int c) { + if (c <= 0xff) { + return (latin1Contains[c]); + } else if (c <= 0x7ff) { + return ((table7FF[c & 0x3f] & (1 << (c >> 6))) != 0); + } else if (c < 0xd800 || (c >= 0xe000 && c <= 0xffff)) { + int lead = c >> 12; + int 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 (0 != twoBits); + } else { + // Look up the code point in its 4k block of code points. + return containsSlow(c, list4kStarts[lead], list4kStarts[lead + 1]); + } + } else if (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; + } + } + + /** + * Span the initial substring for which each character c has + * spanCondition==contains(c). It must be spanCondition==0 or 1. + * + * @param start The start index + * @param outCount If not null: Receives the number of code points in the span. + * @return the limit (exclusive end) of the span + * + * NOTE: to reduce the overhead of function call to contains(c), it is + * manually inlined here. Check for sufficient length for trail unit for + * each surrogate pair. Handle single surrogates as surrogate code + * points as usual in ICU. + */ + public final int span(CharSequence s, int start, SpanCondition spanCondition, OutputInt outCount) { + char c, c2; + int i = start; + int limit = s.length(); + int numSupplementary = 0; + if (SpanCondition.NOT_CONTAINED != spanCondition) { + // span + while (i < limit) { + c = s.charAt(i); + if (c <= 0xff) { + if (!latin1Contains[c]) { + break; + } + } else if (c <= 0x7ff) { + if ((table7FF[c & 0x3f] & (1 << (c >> 6))) == 0) { + break; + } + } else if (c < 0xd800 || c >= 0xdc00 || (i + 1) == limit || (c2 = s.charAt(i + 1)) < 0xdc00 + || c2 >= 0xe000) { + int lead = c >> 12; + int 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 { + // surrogate pair + int supplementary = UCharacterProperty.getRawSupplementary(c, c2); + if (!containsSlow(supplementary, list4kStarts[0x10], list4kStarts[0x11])) { + break; + } + ++numSupplementary; + ++i; + } + ++i; + } + } else { + // span not + while (i < limit) { + c = s.charAt(i); + if (c <= 0xff) { + if (latin1Contains[c]) { + break; + } + } else if (c <= 0x7ff) { + if ((table7FF[c & 0x3f] & (1 << (c >> 6))) != 0) { + break; + } + } else if (c < 0xd800 || c >= 0xdc00 || (i + 1) == limit || (c2 = s.charAt(i + 1)) < 0xdc00 + || c2 >= 0xe000) { + int lead = c >> 12; + int 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 { + // surrogate pair + int supplementary = UCharacterProperty.getRawSupplementary(c, c2); + if (containsSlow(supplementary, list4kStarts[0x10], list4kStarts[0x11])) { + break; + } + ++numSupplementary; + ++i; + } + ++i; + } + } + if (outCount != null) { + int spanLength = i - start; + outCount.value = spanLength - numSupplementary; // number of code points + } + return i; + } + + /** + * Symmetrical with span(). Span the trailing substring for which each character + * c has spanCondition==contains(c). It must be s.length >= limit and + * spanCondition==0 or 1. + * + * @return The string index which starts the span (i.e. inclusive). + */ + public final int spanBack(CharSequence s, int limit, SpanCondition spanCondition) { + char c, c2; + + if (SpanCondition.NOT_CONTAINED != spanCondition) { + // span + for (;;) { + c = s.charAt(--limit); + if (c <= 0xff) { + if (!latin1Contains[c]) { + break; + } + } else if (c <= 0x7ff) { + if ((table7FF[c & 0x3f] & (1 << (c >> 6))) == 0) { + break; + } + } else if (c < 0xd800 || c < 0xdc00 || 0 == limit || (c2 = s.charAt(limit - 1)) < 0xd800 + || c2 >= 0xdc00) { + int lead = c >> 12; + int 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 { + // surrogate pair + int supplementary = UCharacterProperty.getRawSupplementary(c2, c); + if (!containsSlow(supplementary, list4kStarts[0x10], list4kStarts[0x11])) { + break; + } + --limit; + } + if (0 == limit) { + return 0; + } + } + } else { + // span not + for (;;) { + c = s.charAt(--limit); + if (c <= 0xff) { + if (latin1Contains[c]) { + break; + } + } else if (c <= 0x7ff) { + if ((table7FF[c & 0x3f] & (1 << (c >> 6))) != 0) { + break; + } + } else if (c < 0xd800 || c < 0xdc00 || 0 == limit || (c2 = s.charAt(limit - 1)) < 0xd800 + || c2 >= 0xdc00) { + int lead = c >> 12; + int 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 { + // surrogate pair + int supplementary = UCharacterProperty.getRawSupplementary(c2, c); + if (containsSlow(supplementary, list4kStarts[0x10], list4kStarts[0x11])) { + break; + } + --limit; + } + if (0 == limit) { + return 0; + } + } + } + return limit + 1; + } + + /** + * Set bits in a bit rectangle in "vertical" bit organization. + * start> 6; // Named for UTF-8 2-byte lead byte with upper 5 bits. + int trail = start & 0x3f; // Named for UTF-8 2-byte trail byte with lower 6 bits. + + // Set one bit indicating an all-one block. + int bits = 1 << lead; + if ((start + 1) == limit) { // Single-character shortcut. + table[trail] |= bits; + return; + } + + int limitLead = limit >> 6; + int limitTrail = limit & 0x3f; + + if (lead == limitLead) { + // Partial vertical bit column. + while (trail < limitTrail) { + table[trail++] |= bits; + } + } else { + // Partial vertical bit column, + // followed by a bit rectangle, + // followed by another partial vertical bit column. + if (trail > 0) { + do { + table[trail++] |= bits; + } while (trail < 64); + ++lead; + } + if (lead < limitLead) { + bits = ~((1 << lead) - 1); + if (limitLead < 0x20) { + bits &= (1 << limitLead) - 1; + } + for (trail = 0; trail < 64; ++trail) { + table[trail] |= bits; + } + } + // limit<=0x800. If limit==0x800 then limitLead=32 and limitTrail=0. + // In that case, bits=1<= 0x100) { + break; + } + do { + latin1Contains[start++] = true; + } while (start < limit && start < 0x100); + } while (limit <= 0x100); + + // Set table7FF[]. + while (start < 0x800) { + set32x64Bits(table7FF, start, limit <= 0x800 ? limit : 0x800); + if (limit > 0x800) { + start = 0x800; + break; + } + + start = list[listIndex++]; + if (listIndex < listLength) { + limit = list[listIndex++]; + } else { + limit = 0x110000; + } + } + + // Set bmpBlockBits[]. + int minStart = 0x800; + while (start < 0x10000) { + if (limit > 0x10000) { + limit = 0x10000; + } + + if (start < minStart) { + start = minStart; + } + if (start < limit) { // Else: Another range entirely in a known mixed-value block. + if (0 != (start & 0x3f)) { + // Mixed-value block of 64 code points. + 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 < limit) { + if (start < (limit & ~0x3f)) { + // Multiple all-ones blocks of 64 code points each. + set32x64Bits(bmpBlockBits, start >> 6, limit >> 6); + } + + if (0 != (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 < listLength) { + limit = list[listIndex++]; + } else { + limit = 0x110000; + } + } + } + + /** + * Same as UnicodeSet.findCodePoint(int c) 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<=hi= hi || c >= list[hi - 1]) + return hi; + // invariant: c >= list[lo] + // invariant: c < list[hi] + for (;;) { + int i = (lo + hi) >>> 1; + if (i == lo) { + break; // Found! + } else if (c < list[i]) { + hi = i; + } else { + lo = i; + } + } + return hi; + } + + private final boolean containsSlow(int c, int lo, int hi) { + return (0 != (findCodePoint(c, lo, hi) & 1)); + } +} diff --git a/src/main/java/jdk_internal/icu/impl/CharTrie.java b/src/main/java/jdk_internal/icu/impl/CharTrie.java index 7df38d6c..46b8463c 100755 --- a/src/main/java/jdk_internal/icu/impl/CharTrie.java +++ b/src/main/java/jdk_internal/icu/impl/CharTrie.java @@ -1,175 +1,175 @@ -/* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ****************************************************************************** - * Copyright (C) 1996-2014, International Business Machines Corporation and - * others. All Rights Reserved. - ****************************************************************************** - */ - -package jdk_internal.icu.impl; - -import java.io.DataInputStream; -import java.io.InputStream; - -import jdk_internal.icu.text.UTF16; - -import java.io.IOException; - -/** - * Trie implementation which stores data in char, 16 bits. - * - * @author synwee - * @see com.ibm.icu.impl.Trie - * @since release 2.1, Jan 01 2002 - */ - -// note that i need to handle the block calculations later, since chartrie -// in icu4c uses the same index array. -public class CharTrie extends Trie { - // public constructors --------------------------------------------- - - /** - *

- * Creates a new Trie with the settings for the trie data. - *

- *

- * Unserialize the 32-bit-aligned input stream and use the data for the trie. - *

- * - * @param inputStream file input stream to a ICU data file, containing the - * trie - * @param dataManipulate object which provides methods to parse the char data - * @throws IOException thrown when data reading fails - * @draft 2.1 - */ - public CharTrie(InputStream inputStream, DataManipulate dataManipulate) throws IOException { - super(inputStream, dataManipulate); - - if (!isCharTrie()) { - throw new IllegalArgumentException("Data given does not belong to a char trie."); - } - } - - // public methods -------------------------------------------------- - - /** - * Gets the value associated with the codepoint. If no value is associated with - * the codepoint, a default value will be returned. - * - * @param ch codepoint - * @return offset to data - */ - public final char getCodePointValue(int ch) { - int offset; - - // fastpath for U+0000..U+D7FF - if (0 <= ch && ch < UTF16.LEAD_SURROGATE_MIN_VALUE) { - // copy of getRawOffset() - offset = (m_index_[ch >> INDEX_STAGE_1_SHIFT_] << INDEX_STAGE_2_SHIFT_) + (ch & INDEX_STAGE_3_MASK_); - return m_data_[offset]; - } - - // handle U+D800..U+10FFFF - offset = getCodePointOffset(ch); - - // return -1 if there is an error, in this case we return the default - // value: m_initialValue_ - return (offset >= 0) ? m_data_[offset] : m_initialValue_; - } - - /** - * Gets the value to the data which this lead surrogate character points to. - * Returned data may contain folding offset information for the next trailing - * surrogate character. This method does not guarantee correct results for trail - * surrogates. - * - * @param ch lead surrogate character - * @return data value - */ - public final char getLeadValue(char ch) { - return m_data_[getLeadOffset(ch)]; - } - - // protected methods ----------------------------------------------- - - /** - *

- * Parses the input stream and stores its trie content into a index and data - * array - *

- * - * @param inputStream data input stream containing trie data - * @exception IOException thrown when data reading fails - */ - protected final void unserialize(InputStream inputStream) throws IOException { - DataInputStream input = new DataInputStream(inputStream); - int indexDataLength = m_dataOffset_ + m_dataLength_; - m_index_ = new char[indexDataLength]; - for (int i = 0; i < indexDataLength; i++) { - m_index_[i] = input.readChar(); - } - m_data_ = m_index_; - m_initialValue_ = m_data_[m_dataOffset_]; - } - - /** - * Gets the offset to the data which the surrogate pair points to. - * - * @param lead lead surrogate - * @param trail trailing surrogate - * @return offset to data - * @draft 2.1 - */ - protected final int getSurrogateOffset(char lead, char trail) { - if (m_dataManipulate_ == null) { - throw new NullPointerException("The field DataManipulate in this Trie is null"); - } - - // get fold position for the next trail surrogate - int offset = m_dataManipulate_.getFoldingOffset(getLeadValue(lead)); - - // get the real data from the folded lead/trail units - if (offset > 0) { - return getRawOffset(offset, (char) (trail & SURROGATE_MASK_)); - } - - // return -1 if there is an error, in this case we return the default - // value: m_initialValue_ - return -1; - } - - // private data members -------------------------------------------- - - /** - * Default value - */ - private char m_initialValue_; - /** - * Array of char data - */ - private char m_data_[]; -} +/* + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ****************************************************************************** + * Copyright (C) 1996-2014, International Business Machines Corporation and + * others. All Rights Reserved. + ****************************************************************************** + */ + +package jdk_internal.icu.impl; + +import java.io.DataInputStream; +import java.io.InputStream; + +import jdk_internal.icu.text.UTF16; + +import java.io.IOException; + +/** + * Trie implementation which stores data in char, 16 bits. + * + * @author synwee + * @see com.ibm.icu.impl.Trie + * @since release 2.1, Jan 01 2002 + */ + +// note that i need to handle the block calculations later, since chartrie +// in icu4c uses the same index array. +public class CharTrie extends Trie { + // public constructors --------------------------------------------- + + /** + *

+ * Creates a new Trie with the settings for the trie data. + *

+ *

+ * Unserialize the 32-bit-aligned input stream and use the data for the trie. + *

+ * + * @param inputStream file input stream to a ICU data file, containing the + * trie + * @param dataManipulate object which provides methods to parse the char data + * @throws IOException thrown when data reading fails + * @draft 2.1 + */ + public CharTrie(InputStream inputStream, DataManipulate dataManipulate) throws IOException { + super(inputStream, dataManipulate); + + if (!isCharTrie()) { + throw new IllegalArgumentException("Data given does not belong to a char trie."); + } + } + + // public methods -------------------------------------------------- + + /** + * Gets the value associated with the codepoint. If no value is associated with + * the codepoint, a default value will be returned. + * + * @param ch codepoint + * @return offset to data + */ + public final char getCodePointValue(int ch) { + int offset; + + // fastpath for U+0000..U+D7FF + if (0 <= ch && ch < UTF16.LEAD_SURROGATE_MIN_VALUE) { + // copy of getRawOffset() + offset = (m_index_[ch >> INDEX_STAGE_1_SHIFT_] << INDEX_STAGE_2_SHIFT_) + (ch & INDEX_STAGE_3_MASK_); + return m_data_[offset]; + } + + // handle U+D800..U+10FFFF + offset = getCodePointOffset(ch); + + // return -1 if there is an error, in this case we return the default + // value: m_initialValue_ + return (offset >= 0) ? m_data_[offset] : m_initialValue_; + } + + /** + * Gets the value to the data which this lead surrogate character points to. + * Returned data may contain folding offset information for the next trailing + * surrogate character. This method does not guarantee correct results for trail + * surrogates. + * + * @param ch lead surrogate character + * @return data value + */ + public final char getLeadValue(char ch) { + return m_data_[getLeadOffset(ch)]; + } + + // protected methods ----------------------------------------------- + + /** + *

+ * Parses the input stream and stores its trie content into a index and data + * array + *

+ * + * @param inputStream data input stream containing trie data + * @exception IOException thrown when data reading fails + */ + protected final void unserialize(InputStream inputStream) throws IOException { + DataInputStream input = new DataInputStream(inputStream); + int indexDataLength = m_dataOffset_ + m_dataLength_; + m_index_ = new char[indexDataLength]; + for (int i = 0; i < indexDataLength; i++) { + m_index_[i] = input.readChar(); + } + m_data_ = m_index_; + m_initialValue_ = m_data_[m_dataOffset_]; + } + + /** + * Gets the offset to the data which the surrogate pair points to. + * + * @param lead lead surrogate + * @param trail trailing surrogate + * @return offset to data + * @draft 2.1 + */ + protected final int getSurrogateOffset(char lead, char trail) { + if (m_dataManipulate_ == null) { + throw new NullPointerException("The field DataManipulate in this Trie is null"); + } + + // get fold position for the next trail surrogate + int offset = m_dataManipulate_.getFoldingOffset(getLeadValue(lead)); + + // get the real data from the folded lead/trail units + if (offset > 0) { + return getRawOffset(offset, (char) (trail & SURROGATE_MASK_)); + } + + // return -1 if there is an error, in this case we return the default + // value: m_initialValue_ + return -1; + } + + // private data members -------------------------------------------- + + /** + * Default value + */ + private char m_initialValue_; + /** + * Array of char data + */ + private char m_data_[]; +} diff --git a/src/main/java/jdk_internal/icu/impl/CharacterIteratorWrapper.java b/src/main/java/jdk_internal/icu/impl/CharacterIteratorWrapper.java index 72d0a79b..ce20a740 100755 --- a/src/main/java/jdk_internal/icu/impl/CharacterIteratorWrapper.java +++ b/src/main/java/jdk_internal/icu/impl/CharacterIteratorWrapper.java @@ -1,148 +1,148 @@ -/* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ******************************************************************************* - * (C) Copyright IBM Corp. 1996-2005 - All Rights Reserved * - * * - * The original version of this source code and documentation is copyrighted * - * and owned by IBM, These materials are provided under terms of a License * - * Agreement between IBM and Sun. This technology is protected by multiple * - * US and International patents. This notice and attribution to IBM may not * - * to removed. * - ******************************************************************************* - */ - -package jdk_internal.icu.impl; - -import jdk_internal.bidi.CharacterIterator; -import jdk_internal.icu.text.UCharacterIterator; - -/** - * This class is a wrapper around CharacterIterator and implements the - * UCharacterIterator protocol - * - * @author ram - */ - -public class CharacterIteratorWrapper extends UCharacterIterator { - - private CharacterIterator iterator; - - public CharacterIteratorWrapper(CharacterIterator iter) { - if (iter == null) { - throw new IllegalArgumentException(); - } - iterator = iter; - } - - /** - * @see UCharacterIterator#current() - */ - public int current() { - int c = iterator.current(); - if (c == CharacterIterator.DONE) { - return DONE; - } - return c; - } - - /** - * @see UCharacterIterator#getLength() - */ - public int getLength() { - return (iterator.getEndIndex() - iterator.getBeginIndex()); - } - - /** - * @see UCharacterIterator#getIndex() - */ - public int getIndex() { - return iterator.getIndex(); - } - - /** - * @see UCharacterIterator#next() - */ - public int next() { - int i = iterator.current(); - iterator.next(); - if (i == CharacterIterator.DONE) { - return DONE; - } - return i; - } - - /** - * @see UCharacterIterator#previous() - */ - public int previous() { - int i = iterator.previous(); - if (i == CharacterIterator.DONE) { - return DONE; - } - return i; - } - - /** - * @see UCharacterIterator#setIndex(int) - */ - public void setIndex(int index) { - iterator.setIndex(index); - } - - /** - * @see UCharacterIterator#getText(char[]) - */ - public int getText(char[] fillIn, int offset) { - int length = iterator.getEndIndex() - iterator.getBeginIndex(); - int currentIndex = iterator.getIndex(); - if (offset < 0 || offset + length > fillIn.length) { - throw new IndexOutOfBoundsException(Integer.toString(length)); - } - - for (char ch = iterator.first(); ch != CharacterIterator.DONE; ch = iterator.next()) { - fillIn[offset++] = ch; - } - iterator.setIndex(currentIndex); - - return length; - } - - /** - * Creates a clone of this iterator. Clones the underlying character iterator. - * - * @see UCharacterIterator#clone() - */ - public Object clone() { - try { - CharacterIteratorWrapper result = (CharacterIteratorWrapper) super.clone(); - result.iterator = (CharacterIterator) this.iterator.clone(); - return result; - } catch (CloneNotSupportedException e) { - return null; // only invoked if bad underlying character iterator - } - } -} +/* + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ******************************************************************************* + * (C) Copyright IBM Corp. 1996-2005 - All Rights Reserved * + * * + * The original version of this source code and documentation is copyrighted * + * and owned by IBM, These materials are provided under terms of a License * + * Agreement between IBM and Sun. This technology is protected by multiple * + * US and International patents. This notice and attribution to IBM may not * + * to removed. * + ******************************************************************************* + */ + +package jdk_internal.icu.impl; + +import jdk_internal.bidi.CharacterIterator; +import jdk_internal.icu.text.UCharacterIterator; + +/** + * This class is a wrapper around CharacterIterator and implements the + * UCharacterIterator protocol + * + * @author ram + */ + +public class CharacterIteratorWrapper extends UCharacterIterator { + + private CharacterIterator iterator; + + public CharacterIteratorWrapper(CharacterIterator iter) { + if (iter == null) { + throw new IllegalArgumentException(); + } + iterator = iter; + } + + /** + * @see UCharacterIterator#current() + */ + public int current() { + int c = iterator.current(); + if (c == CharacterIterator.DONE) { + return DONE; + } + return c; + } + + /** + * @see UCharacterIterator#getLength() + */ + public int getLength() { + return (iterator.getEndIndex() - iterator.getBeginIndex()); + } + + /** + * @see UCharacterIterator#getIndex() + */ + public int getIndex() { + return iterator.getIndex(); + } + + /** + * @see UCharacterIterator#next() + */ + public int next() { + int i = iterator.current(); + iterator.next(); + if (i == CharacterIterator.DONE) { + return DONE; + } + return i; + } + + /** + * @see UCharacterIterator#previous() + */ + public int previous() { + int i = iterator.previous(); + if (i == CharacterIterator.DONE) { + return DONE; + } + return i; + } + + /** + * @see UCharacterIterator#setIndex(int) + */ + public void setIndex(int index) { + iterator.setIndex(index); + } + + /** + * @see UCharacterIterator#getText(char[]) + */ + public int getText(char[] fillIn, int offset) { + int length = iterator.getEndIndex() - iterator.getBeginIndex(); + int currentIndex = iterator.getIndex(); + if (offset < 0 || offset + length > fillIn.length) { + throw new IndexOutOfBoundsException(Integer.toString(length)); + } + + for (char ch = iterator.first(); ch != CharacterIterator.DONE; ch = iterator.next()) { + fillIn[offset++] = ch; + } + iterator.setIndex(currentIndex); + + return length; + } + + /** + * Creates a clone of this iterator. Clones the underlying character iterator. + * + * @see UCharacterIterator#clone() + */ + public Object clone() { + try { + CharacterIteratorWrapper result = (CharacterIteratorWrapper) super.clone(); + result.iterator = (CharacterIterator) this.iterator.clone(); + return result; + } catch (CloneNotSupportedException e) { + return null; // only invoked if bad underlying character iterator + } + } +} diff --git a/src/main/java/jdk_internal/icu/impl/ICUBinary.java b/src/main/java/jdk_internal/icu/impl/ICUBinary.java index c1a3e472..ed376eef 100755 --- a/src/main/java/jdk_internal/icu/impl/ICUBinary.java +++ b/src/main/java/jdk_internal/icu/impl/ICUBinary.java @@ -1,303 +1,304 @@ -/* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ******************************************************************************* - * Copyright (C) 1996-2014, International Business Machines Corporation and - * others. All Rights Reserved. - ******************************************************************************* - */ - -package jdk_internal.icu.impl; - -import java.io.DataInputStream; -import java.io.InputStream; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Arrays; - -import jdk_internal.icu.util.VersionInfo; -import net.lax1dude.eaglercraft.v1_8.EagRuntime; - -public final class ICUBinary { - - private static final class IsAcceptable implements Authenticate { - @Override - public boolean isDataVersionAcceptable(byte version[]) { - return version[0] == 1; - } - } - - // public inner interface ------------------------------------------------ - - /** - * Special interface for data authentication - */ - public static interface Authenticate { - /** - * Method used in ICUBinary.readHeader() to provide data format authentication. - * - * @param version version of the current data - * @return true if dataformat is an acceptable version, false otherwise - */ - public boolean isDataVersionAcceptable(byte version[]); - } - - // public methods -------------------------------------------------------- - - /** - * Loads an ICU binary data file and returns it as a ByteBuffer. The buffer - * contents is normally read-only, but its position etc. can be modified. - * - * @param itemPath Relative ICU data item path, for example "root.res" or - * "coll/ucadata.icu". - * @return The data as a read-only ByteBuffer. - */ - public static ByteBuffer getRequiredData(String itemPath) { - try (InputStream is = EagRuntime.getRequiredResourceStream(itemPath)) { - - // is.available() may return 0, or 1, or the total number of bytes in the - // stream, - // or some other number. - // Do not try to use is.available() == 0 to find the end of the stream! - byte[] bytes; - int avail = is.available(); - if (avail > 32) { - // There are more bytes available than just the ICU data header length. - // With luck, it is the total number of bytes. - bytes = new byte[avail]; - } else { - bytes = new byte[128]; // empty .res files are even smaller - } - // Call is.read(...) until one returns a negative value. - int length = 0; - for (;;) { - if (length < bytes.length) { - int numRead = is.read(bytes, length, bytes.length - length); - if (numRead < 0) { - break; // end of stream - } - length += numRead; - } else { - // See if we are at the end of the stream before we grow the array. - int nextByte = is.read(); - if (nextByte < 0) { - break; - } - int capacity = 2 * bytes.length; - if (capacity < 128) { - capacity = 128; - } else if (capacity < 0x4000) { - capacity *= 2; // Grow faster until we reach 16kB. - } - bytes = Arrays.copyOf(bytes, capacity); - bytes[length++] = (byte) nextByte; - } - } - return ByteBuffer.wrap(bytes, 0, length); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - /** - * Same as readHeader(), but returns a VersionInfo rather than a compact int. - */ - public static VersionInfo readHeaderAndDataVersion(ByteBuffer bytes, int dataFormat, Authenticate authenticate) - throws IOException { - return getVersionInfoFromCompactInt(readHeader(bytes, dataFormat, authenticate)); - } - - private static final byte BIG_ENDIAN_ = 1; - - public static final byte[] readHeader(InputStream inputStream, byte dataFormatIDExpected[], - Authenticate authenticate) throws IOException { - DataInputStream input = new DataInputStream(inputStream); - char headersize = input.readChar(); - int readcount = 2; - // reading the header format - byte magic1 = input.readByte(); - readcount++; - byte magic2 = input.readByte(); - readcount++; - if (magic1 != MAGIC1 || magic2 != MAGIC2) { - throw new IOException(MAGIC_NUMBER_AUTHENTICATION_FAILED_); - } - - input.readChar(); // reading size - readcount += 2; - input.readChar(); // reading reserved word - readcount += 2; - byte bigendian = input.readByte(); - readcount++; - byte charset = input.readByte(); - readcount++; - byte charsize = input.readByte(); - readcount++; - input.readByte(); // reading reserved byte - readcount++; - - byte dataFormatID[] = new byte[4]; - input.readFully(dataFormatID); - readcount += 4; - byte dataVersion[] = new byte[4]; - input.readFully(dataVersion); - readcount += 4; - byte unicodeVersion[] = new byte[4]; - input.readFully(unicodeVersion); - readcount += 4; - if (headersize < readcount) { - throw new IOException("Internal Error: Header size error"); - } - input.skipBytes(headersize - readcount); - - if (bigendian != BIG_ENDIAN_ || charset != CHAR_SET_ || charsize != CHAR_SIZE_ - || !Arrays.equals(dataFormatIDExpected, dataFormatID) - || (authenticate != null && !authenticate.isDataVersionAcceptable(dataVersion))) { - throw new IOException(HEADER_AUTHENTICATION_FAILED_); - } - return unicodeVersion; - } - - /** - * Reads an ICU data header, checks the data format, and returns the data - * version. - * - *

- * Assumes that the ByteBuffer position is 0 on input. The buffer byte order is - * set according to the data. The buffer position is advanced past the header - * (including UDataInfo and comment). - * - *

- * See C++ ucmndata.h and unicode/udata.h. - * - * @return dataVersion - * @throws IOException if this is not a valid ICU data item of the expected - * dataFormat - */ - public static int readHeader(ByteBuffer bytes, int dataFormat, Authenticate authenticate) throws IOException { - assert bytes.position() == 0; - byte magic1 = bytes.get(2); - byte magic2 = bytes.get(3); - if (magic1 != MAGIC1 || magic2 != MAGIC2) { - throw new IOException(MAGIC_NUMBER_AUTHENTICATION_FAILED_); - } - - byte isBigEndian = bytes.get(8); - byte charsetFamily = bytes.get(9); - byte sizeofUChar = bytes.get(10); - if (isBigEndian < 0 || 1 < isBigEndian || charsetFamily != CHAR_SET_ || sizeofUChar != CHAR_SIZE_) { - throw new IOException(HEADER_AUTHENTICATION_FAILED_); - } - bytes.order(isBigEndian != 0 ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); - - int headerSize = bytes.getChar(0); - int sizeofUDataInfo = bytes.getChar(4); - if (sizeofUDataInfo < 20 || headerSize < (sizeofUDataInfo + 4)) { - throw new IOException("Internal Error: Header size error"); - } - // TODO: Change Authenticate to take int major, int minor, int milli, int micro - // to avoid array allocation. - byte[] formatVersion = new byte[] { bytes.get(16), bytes.get(17), bytes.get(18), bytes.get(19) }; - if (bytes.get(12) != (byte) (dataFormat >> 24) || bytes.get(13) != (byte) (dataFormat >> 16) - || bytes.get(14) != (byte) (dataFormat >> 8) || bytes.get(15) != (byte) dataFormat - || (authenticate != null && !authenticate.isDataVersionAcceptable(formatVersion))) { - throw new IOException(HEADER_AUTHENTICATION_FAILED_ - + String.format("; data format %02x%02x%02x%02x, format version %d.%d.%d.%d", bytes.get(12), - bytes.get(13), bytes.get(14), bytes.get(15), formatVersion[0] & 0xff, - formatVersion[1] & 0xff, formatVersion[2] & 0xff, formatVersion[3] & 0xff)); - } - - bytes.position(headerSize); - return // dataVersion - ((int) bytes.get(20) << 24) | ((bytes.get(21) & 0xff) << 16) | ((bytes.get(22) & 0xff) << 8) - | (bytes.get(23) & 0xff); - } - - public static void skipBytes(ByteBuffer bytes, int skipLength) { - if (skipLength > 0) { - bytes.position(bytes.position() + skipLength); - } - } - - public static byte[] getBytes(ByteBuffer bytes, int length, int additionalSkipLength) { - byte[] dest = new byte[length]; - bytes.get(dest); - if (additionalSkipLength > 0) { - skipBytes(bytes, additionalSkipLength); - } - return dest; - } - - public static String getString(ByteBuffer bytes, int length, int additionalSkipLength) { - CharSequence cs = bytes.asCharBuffer(); - String s = cs.subSequence(0, length).toString(); - skipBytes(bytes, length * 2 + additionalSkipLength); - return s; - } - - public static char[] getChars(ByteBuffer bytes, int length, int additionalSkipLength) { - char[] dest = new char[length]; - bytes.asCharBuffer().get(dest); - skipBytes(bytes, length * 2 + additionalSkipLength); - return dest; - } - - public static int[] getInts(ByteBuffer bytes, int length, int additionalSkipLength) { - int[] dest = new int[length]; - bytes.asIntBuffer().get(dest); - skipBytes(bytes, length * 4 + additionalSkipLength); - return dest; - } - - /** - * Returns a VersionInfo for the bytes in the compact version integer. - */ - public static VersionInfo getVersionInfoFromCompactInt(int version) { - return VersionInfo.getInstance(version >>> 24, (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff); - } - - // private variables ------------------------------------------------- - - /** - * Magic numbers to authenticate the data file - */ - private static final byte MAGIC1 = (byte) 0xda; - private static final byte MAGIC2 = (byte) 0x27; - - /** - * File format authentication values - */ - private static final byte CHAR_SET_ = 0; - private static final byte CHAR_SIZE_ = 2; - - /** - * Error messages - */ - private static final String MAGIC_NUMBER_AUTHENTICATION_FAILED_ = "ICUBinary data file error: Magic number authentication failed"; - private static final String HEADER_AUTHENTICATION_FAILED_ = "ICUBinary data file error: Header authentication failed"; -} +/* + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ******************************************************************************* + * Copyright (C) 1996-2014, International Business Machines Corporation and + * others. All Rights Reserved. + ******************************************************************************* + */ + +package jdk_internal.icu.impl; + +import java.io.DataInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Arrays; + +import jdk_internal.icu.util.VersionInfo; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.HString; + +public final class ICUBinary { + + private static final class IsAcceptable implements Authenticate { + @Override + public boolean isDataVersionAcceptable(byte version[]) { + return version[0] == 1; + } + } + + // public inner interface ------------------------------------------------ + + /** + * Special interface for data authentication + */ + public static interface Authenticate { + /** + * Method used in ICUBinary.readHeader() to provide data format authentication. + * + * @param version version of the current data + * @return true if dataformat is an acceptable version, false otherwise + */ + public boolean isDataVersionAcceptable(byte version[]); + } + + // public methods -------------------------------------------------------- + + /** + * Loads an ICU binary data file and returns it as a ByteBuffer. The buffer + * contents is normally read-only, but its position etc. can be modified. + * + * @param itemPath Relative ICU data item path, for example "root.res" or + * "coll/ucadata.icu". + * @return The data as a read-only ByteBuffer. + */ + public static ByteBuffer getRequiredData(String itemPath) { + try (InputStream is = EagRuntime.getRequiredResourceStream(itemPath)) { + + // is.available() may return 0, or 1, or the total number of bytes in the + // stream, + // or some other number. + // Do not try to use is.available() == 0 to find the end of the stream! + byte[] bytes; + int avail = is.available(); + if (avail > 32) { + // There are more bytes available than just the ICU data header length. + // With luck, it is the total number of bytes. + bytes = new byte[avail]; + } else { + bytes = new byte[128]; // empty .res files are even smaller + } + // Call is.read(...) until one returns a negative value. + int length = 0; + for (;;) { + if (length < bytes.length) { + int numRead = is.read(bytes, length, bytes.length - length); + if (numRead < 0) { + break; // end of stream + } + length += numRead; + } else { + // See if we are at the end of the stream before we grow the array. + int nextByte = is.read(); + if (nextByte < 0) { + break; + } + int capacity = 2 * bytes.length; + if (capacity < 128) { + capacity = 128; + } else if (capacity < 0x4000) { + capacity *= 2; // Grow faster until we reach 16kB. + } + bytes = Arrays.copyOf(bytes, capacity); + bytes[length++] = (byte) nextByte; + } + } + return ByteBuffer.wrap(bytes, 0, length); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** + * Same as readHeader(), but returns a VersionInfo rather than a compact int. + */ + public static VersionInfo readHeaderAndDataVersion(ByteBuffer bytes, int dataFormat, Authenticate authenticate) + throws IOException { + return getVersionInfoFromCompactInt(readHeader(bytes, dataFormat, authenticate)); + } + + private static final byte BIG_ENDIAN_ = 1; + + public static final byte[] readHeader(InputStream inputStream, byte dataFormatIDExpected[], + Authenticate authenticate) throws IOException { + DataInputStream input = new DataInputStream(inputStream); + char headersize = input.readChar(); + int readcount = 2; + // reading the header format + byte magic1 = input.readByte(); + readcount++; + byte magic2 = input.readByte(); + readcount++; + if (magic1 != MAGIC1 || magic2 != MAGIC2) { + throw new IOException(MAGIC_NUMBER_AUTHENTICATION_FAILED_); + } + + input.readChar(); // reading size + readcount += 2; + input.readChar(); // reading reserved word + readcount += 2; + byte bigendian = input.readByte(); + readcount++; + byte charset = input.readByte(); + readcount++; + byte charsize = input.readByte(); + readcount++; + input.readByte(); // reading reserved byte + readcount++; + + byte dataFormatID[] = new byte[4]; + input.readFully(dataFormatID); + readcount += 4; + byte dataVersion[] = new byte[4]; + input.readFully(dataVersion); + readcount += 4; + byte unicodeVersion[] = new byte[4]; + input.readFully(unicodeVersion); + readcount += 4; + if (headersize < readcount) { + throw new IOException("Internal Error: Header size error"); + } + input.skipBytes(headersize - readcount); + + if (bigendian != BIG_ENDIAN_ || charset != CHAR_SET_ || charsize != CHAR_SIZE_ + || !Arrays.equals(dataFormatIDExpected, dataFormatID) + || (authenticate != null && !authenticate.isDataVersionAcceptable(dataVersion))) { + throw new IOException(HEADER_AUTHENTICATION_FAILED_); + } + return unicodeVersion; + } + + /** + * Reads an ICU data header, checks the data format, and returns the data + * version. + * + *

+ * Assumes that the ByteBuffer position is 0 on input. The buffer byte order is + * set according to the data. The buffer position is advanced past the header + * (including UDataInfo and comment). + * + *

+ * See C++ ucmndata.h and unicode/udata.h. + * + * @return dataVersion + * @throws IOException if this is not a valid ICU data item of the expected + * dataFormat + */ + public static int readHeader(ByteBuffer bytes, int dataFormat, Authenticate authenticate) throws IOException { + assert bytes.position() == 0; + byte magic1 = bytes.get(2); + byte magic2 = bytes.get(3); + if (magic1 != MAGIC1 || magic2 != MAGIC2) { + throw new IOException(MAGIC_NUMBER_AUTHENTICATION_FAILED_); + } + + byte isBigEndian = bytes.get(8); + byte charsetFamily = bytes.get(9); + byte sizeofUChar = bytes.get(10); + if (isBigEndian < 0 || 1 < isBigEndian || charsetFamily != CHAR_SET_ || sizeofUChar != CHAR_SIZE_) { + throw new IOException(HEADER_AUTHENTICATION_FAILED_); + } + bytes.order(isBigEndian != 0 ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); + + int headerSize = bytes.getChar(0); + int sizeofUDataInfo = bytes.getChar(4); + if (sizeofUDataInfo < 20 || headerSize < (sizeofUDataInfo + 4)) { + throw new IOException("Internal Error: Header size error"); + } + // TODO: Change Authenticate to take int major, int minor, int milli, int micro + // to avoid array allocation. + byte[] formatVersion = new byte[] { bytes.get(16), bytes.get(17), bytes.get(18), bytes.get(19) }; + if (bytes.get(12) != (byte) (dataFormat >> 24) || bytes.get(13) != (byte) (dataFormat >> 16) + || bytes.get(14) != (byte) (dataFormat >> 8) || bytes.get(15) != (byte) dataFormat + || (authenticate != null && !authenticate.isDataVersionAcceptable(formatVersion))) { + throw new IOException(HEADER_AUTHENTICATION_FAILED_ + + HString.format("; data format %02x%02x%02x%02x, format version %d.%d.%d.%d", bytes.get(12), + bytes.get(13), bytes.get(14), bytes.get(15), formatVersion[0] & 0xff, + formatVersion[1] & 0xff, formatVersion[2] & 0xff, formatVersion[3] & 0xff)); + } + + bytes.position(headerSize); + return // dataVersion + ((int) bytes.get(20) << 24) | ((bytes.get(21) & 0xff) << 16) | ((bytes.get(22) & 0xff) << 8) + | (bytes.get(23) & 0xff); + } + + public static void skipBytes(ByteBuffer bytes, int skipLength) { + if (skipLength > 0) { + bytes.position(bytes.position() + skipLength); + } + } + + public static byte[] getBytes(ByteBuffer bytes, int length, int additionalSkipLength) { + byte[] dest = new byte[length]; + bytes.get(dest); + if (additionalSkipLength > 0) { + skipBytes(bytes, additionalSkipLength); + } + return dest; + } + + public static String getString(ByteBuffer bytes, int length, int additionalSkipLength) { + CharSequence cs = bytes.asCharBuffer(); + String s = cs.subSequence(0, length).toString(); + skipBytes(bytes, length * 2 + additionalSkipLength); + return s; + } + + public static char[] getChars(ByteBuffer bytes, int length, int additionalSkipLength) { + char[] dest = new char[length]; + bytes.asCharBuffer().get(dest); + skipBytes(bytes, length * 2 + additionalSkipLength); + return dest; + } + + public static int[] getInts(ByteBuffer bytes, int length, int additionalSkipLength) { + int[] dest = new int[length]; + bytes.asIntBuffer().get(dest); + skipBytes(bytes, length * 4 + additionalSkipLength); + return dest; + } + + /** + * Returns a VersionInfo for the bytes in the compact version integer. + */ + public static VersionInfo getVersionInfoFromCompactInt(int version) { + return VersionInfo.getInstance(version >>> 24, (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff); + } + + // private variables ------------------------------------------------- + + /** + * Magic numbers to authenticate the data file + */ + private static final byte MAGIC1 = (byte) 0xda; + private static final byte MAGIC2 = (byte) 0x27; + + /** + * File format authentication values + */ + private static final byte CHAR_SET_ = 0; + private static final byte CHAR_SIZE_ = 2; + + /** + * Error messages + */ + private static final String MAGIC_NUMBER_AUTHENTICATION_FAILED_ = "ICUBinary data file error: Magic number authentication failed"; + private static final String HEADER_AUTHENTICATION_FAILED_ = "ICUBinary data file error: Header authentication failed"; +} diff --git a/src/main/java/jdk_internal/icu/impl/Norm2AllModes.java b/src/main/java/jdk_internal/icu/impl/Norm2AllModes.java index 58293e43..69975e3b 100755 --- a/src/main/java/jdk_internal/icu/impl/Norm2AllModes.java +++ b/src/main/java/jdk_internal/icu/impl/Norm2AllModes.java @@ -1,296 +1,296 @@ -/* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ******************************************************************************* - * Copyright (C) 2009-2014, International Business Machines - * Corporation and others. All Rights Reserved. - ******************************************************************************* - */ - -package jdk_internal.icu.impl; - -import java.io.IOException; - -import jdk_internal.icu.text.Normalizer2; - -public final class Norm2AllModes { - // Public API dispatch via Normalizer2 subclasses -------------------------- *** - - // Normalizer2 implementation for the old UNORM_NONE. - public static final class NoopNormalizer2 extends Normalizer2 { - @Override - public StringBuilder normalize(CharSequence src, StringBuilder dest) { - if (dest != src) { - dest.setLength(0); - return dest.append(src); - } else { - throw new IllegalArgumentException(); - } - } - - @Override - public Appendable normalize(CharSequence src, Appendable dest) { - if (dest != src) { - try { - return dest.append(src); - } catch (IOException e) { - throw new InternalError(e.toString(), e); - } - } else { - throw new IllegalArgumentException(); - } - } - - @Override - public StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second) { - if (first != second) { - return first.append(second); - } else { - throw new IllegalArgumentException(); - } - } - - @Override - public StringBuilder append(StringBuilder first, CharSequence second) { - if (first != second) { - return first.append(second); - } else { - throw new IllegalArgumentException(); - } - } - - @Override - public String getDecomposition(int c) { - return null; - } - - // No need to override the default getRawDecomposition(). - @Override - public boolean isNormalized(CharSequence s) { - return true; - } - - @Override - public int spanQuickCheckYes(CharSequence s) { - return s.length(); - } - - @Override - public boolean hasBoundaryBefore(int c) { - return true; - } - } - - // Intermediate class: - // Has NormalizerImpl and does boilerplate argument checking and setup. - public abstract static class Normalizer2WithImpl extends Normalizer2 { - public Normalizer2WithImpl(NormalizerImpl ni) { - impl = ni; - } - - // normalize - @Override - public StringBuilder normalize(CharSequence src, StringBuilder dest) { - if (dest == src) { - throw new IllegalArgumentException(); - } - dest.setLength(0); - normalize(src, new NormalizerImpl.ReorderingBuffer(impl, dest, src.length())); - return dest; - } - - @Override - public Appendable normalize(CharSequence src, Appendable dest) { - if (dest == src) { - throw new IllegalArgumentException(); - } - NormalizerImpl.ReorderingBuffer buffer = new NormalizerImpl.ReorderingBuffer(impl, dest, src.length()); - normalize(src, buffer); - buffer.flush(); - return dest; - } - - protected abstract void normalize(CharSequence src, NormalizerImpl.ReorderingBuffer buffer); - - // normalize and append - @Override - public StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second) { - return normalizeSecondAndAppend(first, second, true); - } - - @Override - public StringBuilder append(StringBuilder first, CharSequence second) { - return normalizeSecondAndAppend(first, second, false); - } - - public StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second, boolean doNormalize) { - if (first == second) { - throw new IllegalArgumentException(); - } - normalizeAndAppend(second, doNormalize, - new NormalizerImpl.ReorderingBuffer(impl, first, first.length() + second.length())); - return first; - } - - protected abstract void normalizeAndAppend(CharSequence src, boolean doNormalize, - NormalizerImpl.ReorderingBuffer buffer); - - @Override - public String getDecomposition(int c) { - return impl.getDecomposition(c); - } - - @Override - public int getCombiningClass(int c) { - return impl.getCC(impl.getNorm16(c)); - } - - // quick checks - @Override - public boolean isNormalized(CharSequence s) { - return s.length() == spanQuickCheckYes(s); - } - - public final NormalizerImpl impl; - } - - public static final class DecomposeNormalizer2 extends Normalizer2WithImpl { - public DecomposeNormalizer2(NormalizerImpl ni) { - super(ni); - } - - @Override - protected void normalize(CharSequence src, NormalizerImpl.ReorderingBuffer buffer) { - impl.decompose(src, 0, src.length(), buffer); - } - - @Override - protected void normalizeAndAppend(CharSequence src, boolean doNormalize, - NormalizerImpl.ReorderingBuffer buffer) { - impl.decomposeAndAppend(src, doNormalize, buffer); - } - - @Override - public int spanQuickCheckYes(CharSequence s) { - return impl.decompose(s, 0, s.length(), null); - } - - @Override - public boolean hasBoundaryBefore(int c) { - return impl.hasDecompBoundaryBefore(c); - } - } - - public static final class ComposeNormalizer2 extends Normalizer2WithImpl { - public ComposeNormalizer2(NormalizerImpl ni, boolean fcc) { - super(ni); - onlyContiguous = fcc; - } - - @Override - protected void normalize(CharSequence src, NormalizerImpl.ReorderingBuffer buffer) { - impl.compose(src, 0, src.length(), onlyContiguous, true, buffer); - } - - @Override - protected void normalizeAndAppend(CharSequence src, boolean doNormalize, - NormalizerImpl.ReorderingBuffer buffer) { - impl.composeAndAppend(src, doNormalize, onlyContiguous, buffer); - } - - @Override - public boolean isNormalized(CharSequence s) { - // 5: small destCapacity for substring normalization - return impl.compose(s, 0, s.length(), onlyContiguous, false, - new NormalizerImpl.ReorderingBuffer(impl, new StringBuilder(), 5)); - } - - @Override - public int spanQuickCheckYes(CharSequence s) { - return impl.composeQuickCheck(s, 0, s.length(), onlyContiguous, true) >>> 1; - } - - @Override - public boolean hasBoundaryBefore(int c) { - return impl.hasCompBoundaryBefore(c); - } - - private final boolean onlyContiguous; - } - - // instance cache ---------------------------------------------------------- *** - - private Norm2AllModes(NormalizerImpl ni) { - impl = ni; - comp = new ComposeNormalizer2(ni, false); - decomp = new DecomposeNormalizer2(ni); - } - - public final NormalizerImpl impl; - public final ComposeNormalizer2 comp; - public final DecomposeNormalizer2 decomp; - - private static Norm2AllModes getInstanceFromSingleton(Norm2AllModesSingleton singleton) { - if (singleton.exception != null) { - throw singleton.exception; - } - return singleton.allModes; - } - - public static Norm2AllModes getNFCInstance() { - return getInstanceFromSingleton(NFCSingleton.INSTANCE); - } - - public static Norm2AllModes getNFKCInstance() { - return getInstanceFromSingleton(NFKCSingleton.INSTANCE); - } - - public static final NoopNormalizer2 NOOP_NORMALIZER2 = new NoopNormalizer2(); - - private static final class Norm2AllModesSingleton { - private Norm2AllModesSingleton(String name) { - try { - @SuppressWarnings("deprecation") - String DATA_FILE_NAME = "/assets/eagler/icudt/" + name + ".nrm"; - NormalizerImpl impl = new NormalizerImpl().load(DATA_FILE_NAME); - allModes = new Norm2AllModes(impl); - } catch (RuntimeException e) { - exception = e; - } - } - - private Norm2AllModes allModes; - private RuntimeException exception; - } - - private static final class NFCSingleton { - private static final Norm2AllModesSingleton INSTANCE = new Norm2AllModesSingleton("nfc"); - } - - private static final class NFKCSingleton { - private static final Norm2AllModesSingleton INSTANCE = new Norm2AllModesSingleton("nfkc"); - } -} +/* + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ******************************************************************************* + * Copyright (C) 2009-2014, International Business Machines + * Corporation and others. All Rights Reserved. + ******************************************************************************* + */ + +package jdk_internal.icu.impl; + +import java.io.IOException; + +import jdk_internal.icu.text.Normalizer2; + +public final class Norm2AllModes { + // Public API dispatch via Normalizer2 subclasses -------------------------- *** + + // Normalizer2 implementation for the old UNORM_NONE. + public static final class NoopNormalizer2 extends Normalizer2 { + @Override + public StringBuilder normalize(CharSequence src, StringBuilder dest) { + if (dest != src) { + dest.setLength(0); + return dest.append(src); + } else { + throw new IllegalArgumentException(); + } + } + + @Override + public Appendable normalize(CharSequence src, Appendable dest) { + if (dest != src) { + try { + return dest.append(src); + } catch (IOException e) { + throw new InternalError(e.toString(), e); + } + } else { + throw new IllegalArgumentException(); + } + } + + @Override + public StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second) { + if (first != second) { + return first.append(second); + } else { + throw new IllegalArgumentException(); + } + } + + @Override + public StringBuilder append(StringBuilder first, CharSequence second) { + if (first != second) { + return first.append(second); + } else { + throw new IllegalArgumentException(); + } + } + + @Override + public String getDecomposition(int c) { + return null; + } + + // No need to override the default getRawDecomposition(). + @Override + public boolean isNormalized(CharSequence s) { + return true; + } + + @Override + public int spanQuickCheckYes(CharSequence s) { + return s.length(); + } + + @Override + public boolean hasBoundaryBefore(int c) { + return true; + } + } + + // Intermediate class: + // Has NormalizerImpl and does boilerplate argument checking and setup. + public abstract static class Normalizer2WithImpl extends Normalizer2 { + public Normalizer2WithImpl(NormalizerImpl ni) { + impl = ni; + } + + // normalize + @Override + public StringBuilder normalize(CharSequence src, StringBuilder dest) { + if (dest == src) { + throw new IllegalArgumentException(); + } + dest.setLength(0); + normalize(src, new NormalizerImpl.ReorderingBuffer(impl, dest, src.length())); + return dest; + } + + @Override + public Appendable normalize(CharSequence src, Appendable dest) { + if (dest == src) { + throw new IllegalArgumentException(); + } + NormalizerImpl.ReorderingBuffer buffer = new NormalizerImpl.ReorderingBuffer(impl, dest, src.length()); + normalize(src, buffer); + buffer.flush(); + return dest; + } + + protected abstract void normalize(CharSequence src, NormalizerImpl.ReorderingBuffer buffer); + + // normalize and append + @Override + public StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second) { + return normalizeSecondAndAppend(first, second, true); + } + + @Override + public StringBuilder append(StringBuilder first, CharSequence second) { + return normalizeSecondAndAppend(first, second, false); + } + + public StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second, boolean doNormalize) { + if (first == second) { + throw new IllegalArgumentException(); + } + normalizeAndAppend(second, doNormalize, + new NormalizerImpl.ReorderingBuffer(impl, first, first.length() + second.length())); + return first; + } + + protected abstract void normalizeAndAppend(CharSequence src, boolean doNormalize, + NormalizerImpl.ReorderingBuffer buffer); + + @Override + public String getDecomposition(int c) { + return impl.getDecomposition(c); + } + + @Override + public int getCombiningClass(int c) { + return impl.getCC(impl.getNorm16(c)); + } + + // quick checks + @Override + public boolean isNormalized(CharSequence s) { + return s.length() == spanQuickCheckYes(s); + } + + public final NormalizerImpl impl; + } + + public static final class DecomposeNormalizer2 extends Normalizer2WithImpl { + public DecomposeNormalizer2(NormalizerImpl ni) { + super(ni); + } + + @Override + protected void normalize(CharSequence src, NormalizerImpl.ReorderingBuffer buffer) { + impl.decompose(src, 0, src.length(), buffer); + } + + @Override + protected void normalizeAndAppend(CharSequence src, boolean doNormalize, + NormalizerImpl.ReorderingBuffer buffer) { + impl.decomposeAndAppend(src, doNormalize, buffer); + } + + @Override + public int spanQuickCheckYes(CharSequence s) { + return impl.decompose(s, 0, s.length(), null); + } + + @Override + public boolean hasBoundaryBefore(int c) { + return impl.hasDecompBoundaryBefore(c); + } + } + + public static final class ComposeNormalizer2 extends Normalizer2WithImpl { + public ComposeNormalizer2(NormalizerImpl ni, boolean fcc) { + super(ni); + onlyContiguous = fcc; + } + + @Override + protected void normalize(CharSequence src, NormalizerImpl.ReorderingBuffer buffer) { + impl.compose(src, 0, src.length(), onlyContiguous, true, buffer); + } + + @Override + protected void normalizeAndAppend(CharSequence src, boolean doNormalize, + NormalizerImpl.ReorderingBuffer buffer) { + impl.composeAndAppend(src, doNormalize, onlyContiguous, buffer); + } + + @Override + public boolean isNormalized(CharSequence s) { + // 5: small destCapacity for substring normalization + return impl.compose(s, 0, s.length(), onlyContiguous, false, + new NormalizerImpl.ReorderingBuffer(impl, new StringBuilder(), 5)); + } + + @Override + public int spanQuickCheckYes(CharSequence s) { + return impl.composeQuickCheck(s, 0, s.length(), onlyContiguous, true) >>> 1; + } + + @Override + public boolean hasBoundaryBefore(int c) { + return impl.hasCompBoundaryBefore(c); + } + + private final boolean onlyContiguous; + } + + // instance cache ---------------------------------------------------------- *** + + private Norm2AllModes(NormalizerImpl ni) { + impl = ni; + comp = new ComposeNormalizer2(ni, false); + decomp = new DecomposeNormalizer2(ni); + } + + public final NormalizerImpl impl; + public final ComposeNormalizer2 comp; + public final DecomposeNormalizer2 decomp; + + private static Norm2AllModes getInstanceFromSingleton(Norm2AllModesSingleton singleton) { + if (singleton.exception != null) { + throw singleton.exception; + } + return singleton.allModes; + } + + public static Norm2AllModes getNFCInstance() { + return getInstanceFromSingleton(NFCSingleton.INSTANCE); + } + + public static Norm2AllModes getNFKCInstance() { + return getInstanceFromSingleton(NFKCSingleton.INSTANCE); + } + + public static final NoopNormalizer2 NOOP_NORMALIZER2 = new NoopNormalizer2(); + + private static final class Norm2AllModesSingleton { + private Norm2AllModesSingleton(String name) { + try { + @SuppressWarnings("deprecation") + String DATA_FILE_NAME = "/assets/eagler/icudt/" + name + ".nrm"; + NormalizerImpl impl = new NormalizerImpl().load(DATA_FILE_NAME); + allModes = new Norm2AllModes(impl); + } catch (RuntimeException e) { + exception = e; + } + } + + private Norm2AllModes allModes; + private RuntimeException exception; + } + + private static final class NFCSingleton { + private static final Norm2AllModesSingleton INSTANCE = new Norm2AllModesSingleton("nfc"); + } + + private static final class NFKCSingleton { + private static final Norm2AllModesSingleton INSTANCE = new Norm2AllModesSingleton("nfkc"); + } +} diff --git a/src/main/java/jdk_internal/icu/impl/NormalizerImpl.java b/src/main/java/jdk_internal/icu/impl/NormalizerImpl.java index 00884aa7..18325d15 100755 --- a/src/main/java/jdk_internal/icu/impl/NormalizerImpl.java +++ b/src/main/java/jdk_internal/icu/impl/NormalizerImpl.java @@ -1,2261 +1,2261 @@ -/* - * Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ******************************************************************************* - * Copyright (C) 2009-2014, International Business Machines - * Corporation and others. All Rights Reserved. - ******************************************************************************* - */ -package jdk_internal.icu.impl; - -import java.io.IOException; -import java.nio.ByteBuffer; - -import jdk_internal.icu.lang.UCharacter; -import jdk_internal.icu.text.Normalizer2; -import jdk_internal.icu.text.UTF16; -import jdk_internal.icu.util.CodePointTrie; -import jdk_internal.icu.util.VersionInfo; - -// Original filename in ICU4J: Normalizer2Impl.java -public final class NormalizerImpl { - public static final class Hangul { - /* Korean Hangul and Jamo constants */ - public static final int JAMO_L_BASE = 0x1100; /* "lead" jamo */ - public static final int JAMO_V_BASE = 0x1161; /* "vowel" jamo */ - public static final int JAMO_T_BASE = 0x11a7; /* "trail" jamo */ - - public static final int HANGUL_BASE = 0xac00; - public static final int HANGUL_END = 0xd7a3; - - public static final int JAMO_L_COUNT = 19; - public static final int JAMO_V_COUNT = 21; - public static final int JAMO_T_COUNT = 28; - - public static final int HANGUL_COUNT = JAMO_L_COUNT * JAMO_V_COUNT * JAMO_T_COUNT; - public static final int HANGUL_LIMIT = HANGUL_BASE + HANGUL_COUNT; - - public static boolean isHangul(int c) { - return HANGUL_BASE <= c && c < HANGUL_LIMIT; - } - - public static boolean isHangulLV(int c) { - c -= HANGUL_BASE; - return 0 <= c && c < HANGUL_COUNT && c % JAMO_T_COUNT == 0; - } - - /** - * Decomposes c, which must be a Hangul syllable, into buffer and returns the - * length of the decomposition (2 or 3). - */ - public static int decompose(int c, Appendable buffer) { - try { - c -= HANGUL_BASE; - int c2 = c % JAMO_T_COUNT; - c /= JAMO_T_COUNT; - buffer.append((char) (JAMO_L_BASE + c / JAMO_V_COUNT)); - buffer.append((char) (JAMO_V_BASE + c % JAMO_V_COUNT)); - if (c2 == 0) { - return 2; - } else { - buffer.append((char) (JAMO_T_BASE + c2)); - return 3; - } - } catch (IOException e) { - throw new InternalError(e); - } - } - } - - /** - * Writable buffer that takes care of canonical ordering. Its Appendable methods - * behave like the C++ implementation's appendZeroCC() methods. - *

- * If dest is a StringBuilder, then the buffer writes directly to it. Otherwise, - * the buffer maintains a StringBuilder for intermediate text segments until no - * further changes are necessary and whole segments are appended. append() - * methods that take combining-class values always write to the StringBuilder. - * Other append() methods flush and append to the Appendable. - */ - public static final class ReorderingBuffer implements Appendable { - public ReorderingBuffer(NormalizerImpl ni, Appendable dest, int destCapacity) { - impl = ni; - app = dest; - if (app instanceof StringBuilder) { - appIsStringBuilder = true; - str = (StringBuilder) dest; - // In Java, the constructor subsumes public void init(int destCapacity) - str.ensureCapacity(destCapacity); - reorderStart = 0; - if (str.length() == 0) { - 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; - } - } else { - appIsStringBuilder = false; - str = new StringBuilder(); - reorderStart = 0; - lastCC = 0; - } - } - - public boolean isEmpty() { - return str.length() == 0; - } - - public int length() { - return str.length(); - } - - public int getLastCC() { - return lastCC; - } - - public StringBuilder getStringBuilder() { - return str; - } - - public boolean equals(CharSequence s, int start, int limit) { - return UTF16Plus.equal(str, 0, str.length(), s, start, limit); - } - - public void append(int c, int cc) { - if (lastCC <= cc || cc == 0) { - str.appendCodePoint(c); - lastCC = cc; - if (cc <= 1) { - reorderStart = str.length(); - } - } else { - insert(c, cc); - } - } - - public void append(CharSequence s, int start, int limit, boolean isNFD, int leadCC, int trailCC) { - if (start == limit) { - return; - } - if (lastCC <= leadCC || leadCC == 0) { - if (trailCC <= 1) { - reorderStart = str.length() + (limit - start); - } else if (leadCC <= 1) { - reorderStart = str.length() + 1; // Ok if not a code point boundary. - } - str.append(s, start, limit); - lastCC = trailCC; - } else { - int c = Character.codePointAt(s, start); - start += Character.charCount(c); - insert(c, leadCC); // insert first code point - while (start < limit) { - c = Character.codePointAt(s, start); - start += Character.charCount(c); - if (start < limit) { - if (isNFD) { - leadCC = getCCFromYesOrMaybe(impl.getNorm16(c)); - } else { - leadCC = impl.getCC(impl.getNorm16(c)); - } - } else { - leadCC = trailCC; - } - append(c, leadCC); - } - } - } - - // The following append() methods work like C++ appendZeroCC(). - // They assume that the cc or trailCC of their input is 0. - // Most of them implement Appendable interface methods. - @Override - public ReorderingBuffer append(char c) { - str.append(c); - lastCC = 0; - reorderStart = str.length(); - return this; - } - - public void appendZeroCC(int c) { - str.appendCodePoint(c); - lastCC = 0; - reorderStart = str.length(); - } - - @Override - public ReorderingBuffer append(CharSequence s) { - if (s.length() != 0) { - str.append(s); - lastCC = 0; - reorderStart = str.length(); - } - return this; - } - - @Override - public ReorderingBuffer append(CharSequence s, int start, int limit) { - if (start != limit) { - str.append(s, start, limit); - lastCC = 0; - reorderStart = str.length(); - } - return this; - } - - /** - * Flushes from the intermediate StringBuilder to the Appendable, if they are - * different objects. Used after recomposition. Must be called at the end when - * writing to a non-StringBuilder Appendable. - */ - public void flush() { - if (appIsStringBuilder) { - reorderStart = str.length(); - } else { - try { - app.append(str); - str.setLength(0); - reorderStart = 0; - } catch (IOException e) { - throw new InternalError(e); // Avoid declaring "throws IOException". - } - } - lastCC = 0; - } - - /** - * Flushes from the intermediate StringBuilder to the Appendable, if they are - * different objects. Then appends the new text to the Appendable or - * StringBuilder. Normally used after quick check loops find a non-empty - * sequence. - */ - public ReorderingBuffer flushAndAppendZeroCC(CharSequence s, int start, int limit) { - if (appIsStringBuilder) { - str.append(s, start, limit); - reorderStart = str.length(); - } else { - try { - app.append(str).append(s, start, limit); - str.setLength(0); - reorderStart = 0; - } catch (IOException e) { - throw new InternalError(e); // Avoid declaring "throws IOException". - } - } - lastCC = 0; - return this; - } - - public void remove() { - str.setLength(0); - lastCC = 0; - reorderStart = 0; - } - - public void removeSuffix(int suffixLength) { - int oldLength = str.length(); - str.delete(oldLength - suffixLength, oldLength); - lastCC = 0; - reorderStart = str.length(); - } - - // Inserts c somewhere before the last character. - // Requires 0 cc;) { - } - // insert c at codePointLimit, after the character with prevCC<=cc - if (c <= 0xffff) { - str.insert(codePointLimit, (char) c); - if (cc <= 1) { - reorderStart = codePointLimit + 1; - } - } else { - str.insert(codePointLimit, Character.toChars(c)); - if (cc <= 1) { - reorderStart = codePointLimit + 2; - } - } - } - - private final NormalizerImpl impl; - private final Appendable app; - private final StringBuilder str; - private final boolean appIsStringBuilder; - private int reorderStart; - private int lastCC; - - // private backward iterator - private void setIterator() { - codePointStart = str.length(); - } - - private void skipPrevious() { // Requires 0= codePointStart) { - return 0; - } - int c = str.codePointBefore(codePointStart); - codePointStart -= Character.charCount(c); - return impl.getCCFromYesOrMaybeCP(c); - } - - private int codePointStart, codePointLimit; - } - - // TODO: Propose as public API on the UTF16 class. - // TODO: Propose widening UTF16 methods that take char to take int. - // TODO: Propose widening UTF16 methods that take String to take CharSequence. - public static final class UTF16Plus { - /** - * Is this code point a lead surrogate (U+d800..U+dbff)? - * - * @param c code unit or code point - * @return true or false - */ - public static boolean isLeadSurrogate(int c) { - return (c & 0xfffffc00) == 0xd800; - } - - /** - * Assuming c is a surrogate code point (UTF16.isSurrogate(c)), is it a lead - * surrogate? - * - * @param c code unit or code point - * @return true or false - */ - public static boolean isSurrogateLead(int c) { - return (c & 0x400) == 0; - } - - /** - * Compares two CharSequence subsequences for binary equality. - * - * @param s1 first sequence - * @param start1 start offset in first sequence - * @param limit1 limit offset in first sequence - * @param s2 second sequence - * @param start2 start offset in second sequence - * @param limit2 limit offset in second sequence - * @return true if s1.subSequence(start1, limit1) contains the same text as - * s2.subSequence(start2, limit2) - */ - public static boolean equal(CharSequence s1, int start1, int limit1, CharSequence s2, int start2, int limit2) { - if ((limit1 - start1) != (limit2 - start2)) { - return false; - } - if (s1 == s2 && start1 == start2) { - return true; - } - while (start1 < limit1) { - if (s1.charAt(start1++) != s2.charAt(start2++)) { - return false; - } - } - return true; - } - } - - public NormalizerImpl() { - } - - private static final class IsAcceptable implements ICUBinary.Authenticate { - public boolean isDataVersionAcceptable(byte version[]) { - return version[0] == 4; - } - } - - private static final IsAcceptable IS_ACCEPTABLE = new IsAcceptable(); - private static final int DATA_FORMAT = 0x4e726d32; // "Nrm2" - - public NormalizerImpl load(ByteBuffer bytes) { - try { - dataVersion = ICUBinary.readHeaderAndDataVersion(bytes, DATA_FORMAT, IS_ACCEPTABLE); - int indexesLength = bytes.getInt() / 4; // inIndexes[IX_NORM_TRIE_OFFSET]/4 - if (indexesLength <= IX_MIN_LCCC_CP) { - throw new InternalError("Normalizer2 data: not enough indexes"); - } - int[] inIndexes = new int[indexesLength]; - inIndexes[0] = indexesLength * 4; - for (int i = 1; i < indexesLength; ++i) { - inIndexes[i] = bytes.getInt(); - } - - minDecompNoCP = inIndexes[IX_MIN_DECOMP_NO_CP]; - minCompNoMaybeCP = inIndexes[IX_MIN_COMP_NO_MAYBE_CP]; - minLcccCP = inIndexes[IX_MIN_LCCC_CP]; - - minYesNo = inIndexes[IX_MIN_YES_NO]; - minYesNoMappingsOnly = inIndexes[IX_MIN_YES_NO_MAPPINGS_ONLY]; - minNoNo = inIndexes[IX_MIN_NO_NO]; - minNoNoCompBoundaryBefore = inIndexes[IX_MIN_NO_NO_COMP_BOUNDARY_BEFORE]; - minNoNoCompNoMaybeCC = inIndexes[IX_MIN_NO_NO_COMP_NO_MAYBE_CC]; - minNoNoEmpty = inIndexes[IX_MIN_NO_NO_EMPTY]; - limitNoNo = inIndexes[IX_LIMIT_NO_NO]; - minMaybeYes = inIndexes[IX_MIN_MAYBE_YES]; - assert ((minMaybeYes & 7) == 0); // 8-aligned for noNoDelta bit fields - centerNoNoDelta = (minMaybeYes >> DELTA_SHIFT) - MAX_DELTA - 1; - - // Read the normTrie. - int offset = inIndexes[IX_NORM_TRIE_OFFSET]; - int nextOffset = inIndexes[IX_EXTRA_DATA_OFFSET]; - int triePosition = bytes.position(); - normTrie = CodePointTrie.Fast16.fromBinary(bytes); - int trieLength = bytes.position() - triePosition; - if (trieLength > (nextOffset - offset)) { - throw new InternalError("Normalizer2 data: not enough bytes for normTrie"); - } - ICUBinary.skipBytes(bytes, (nextOffset - offset) - trieLength); // skip padding after trie bytes - - // Read the composition and mapping data. - offset = nextOffset; - nextOffset = inIndexes[IX_SMALL_FCD_OFFSET]; - int numChars = (nextOffset - offset) / 2; - if (numChars != 0) { - maybeYesCompositions = ICUBinary.getString(bytes, numChars, 0); - extraData = maybeYesCompositions.substring((MIN_NORMAL_MAYBE_YES - minMaybeYes) >> OFFSET_SHIFT); - } - - // smallFCD: new in formatVersion 2 - offset = nextOffset; - smallFCD = new byte[0x100]; - bytes.get(smallFCD); - - return this; - } catch (IOException e) { - throw new InternalError(e); - } - } - - public NormalizerImpl load(String name) { - return load(ICUBinary.getRequiredData(name)); - } - - // The trie stores values for lead surrogate code *units*. - // Surrogate code *points* are inert. - public int getNorm16(int c) { - return UTF16Plus.isLeadSurrogate(c) ? INERT : normTrie.get(c); - } - - public int getRawNorm16(int c) { - return normTrie.get(c); - } - - public boolean isAlgorithmicNoNo(int norm16) { - return limitNoNo <= norm16 && norm16 < minMaybeYes; - } - - public boolean isCompNo(int norm16) { - return minNoNo <= norm16 && norm16 < minMaybeYes; - } - - public boolean isDecompYes(int norm16) { - return norm16 < minYesNo || minMaybeYes <= norm16; - } - - public int getCC(int norm16) { - if (norm16 >= MIN_NORMAL_MAYBE_YES) { - return getCCFromNormalYesOrMaybe(norm16); - } - if (norm16 < minNoNo || limitNoNo <= norm16) { - return 0; - } - return getCCFromNoNo(norm16); - } - - public static int getCCFromNormalYesOrMaybe(int norm16) { - return (norm16 >> OFFSET_SHIFT) & 0xff; - } - - public static int getCCFromYesOrMaybe(int norm16) { - return norm16 >= MIN_NORMAL_MAYBE_YES ? getCCFromNormalYesOrMaybe(norm16) : 0; - } - - public int getCCFromYesOrMaybeCP(int c) { - 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. - */ - public int getFCD16(int c) { - if (c < minDecompNoCP) { - return 0; - } else if (c <= 0xffff) { - if (!singleLeadMightHaveNonZeroFCD16(c)) { - return 0; - } - } - return getFCD16FromNormData(c); - } - - /** - * Returns true if the single-or-lead code unit c might have non-zero FCD data. - */ - public boolean singleLeadMightHaveNonZeroFCD16(int lead) { - // 0<=lead<=0xffff - byte bits = smallFCD[lead >> 8]; - if (bits == 0) { - return false; - } - return ((bits >> ((lead >> 5) & 7)) & 1) != 0; - } - - /** Gets the FCD value from the regular normalization data. */ - public int getFCD16FromNormData(int c) { - int 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) - int 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 - int mapping = norm16 >> OFFSET_SHIFT; - int firstUnit = extraData.charAt(mapping); - int fcd16 = firstUnit >> 8; // tccc - if ((firstUnit & MAPPING_HAS_CCC_LCCC_WORD) != 0) { - fcd16 |= extraData.charAt(mapping - 1) & 0xff00; // lccc - } - return fcd16; - } - - /** - * Gets the decomposition for one code point. - * - * @param c code point - * @return c's decomposition, if it has one; returns null if it does not have a - * decomposition - */ - public String getDecomposition(int c) { - int norm16; - if (c < minDecompNoCP || isMaybeOrNonZeroCC(norm16 = getNorm16(c))) { - // c does not decompose - return null; - } - int decomp = -1; - if (isDecompNoAlgorithmic(norm16)) { - // Maps to an isCompYesAndZeroCC. - decomp = c = mapAlgorithmic(c, norm16); - // The mapping might decompose further. - norm16 = getRawNorm16(c); - } - if (norm16 < minYesNo) { - if (decomp < 0) { - return null; - } else { - return UTF16.valueOf(decomp); - } - } else if (isHangulLV(norm16) || isHangulLVT(norm16)) { - // Hangul syllable: decompose algorithmically - StringBuilder buffer = new StringBuilder(); - Hangul.decompose(c, buffer); - return buffer.toString(); - } - // c decomposes, get everything from the variable-length extra data - int mapping = norm16 >> OFFSET_SHIFT; - int length = extraData.charAt(mapping++) & MAPPING_LENGTH_MASK; - return extraData.substring(mapping, mapping + length); - } - - // Fixed norm16 values. - public static final int MIN_YES_YES_WITH_CC = 0xfe02; - public static final int JAMO_VT = 0xfe00; - public static final int MIN_NORMAL_MAYBE_YES = 0xfc00; - public static final int JAMO_L = 2; // offset=1 hasCompBoundaryAfter=FALSE - public static final int INERT = 1; // offset=0 hasCompBoundaryAfter=TRUE - - // norm16 bit 0 is comp-boundary-after. - public static final int HAS_COMP_BOUNDARY_AFTER = 1; - public static final int OFFSET_SHIFT = 1; - - // For algorithmic one-way mappings, norm16 bits 2..1 indicate the - // tccc (0, 1, >1) for quick FCC boundary-after tests. - public static final int DELTA_TCCC_0 = 0; - public static final int DELTA_TCCC_1 = 2; - public static final int DELTA_TCCC_GT_1 = 4; - public static final int DELTA_TCCC_MASK = 6; - public static final int DELTA_SHIFT = 3; - - public static final int MAX_DELTA = 0x40; - - // Byte offsets from the start of the data, after the generic header. - public static final int IX_NORM_TRIE_OFFSET = 0; - public static final int IX_EXTRA_DATA_OFFSET = 1; - public static final int IX_SMALL_FCD_OFFSET = 2; - public static final int IX_RESERVED3_OFFSET = 3; - public static final int IX_TOTAL_SIZE = 7; - public static final int MIN_CCC_LCCC_CP = 0x300; - // Code point thresholds for quick check codes. - public static final int IX_MIN_DECOMP_NO_CP = 8; - public static final int IX_MIN_COMP_NO_MAYBE_CP = 9; - - // Norm16 value thresholds for quick check combinations and types of extra data. - - /** Mappings & compositions in [minYesNo..minYesNoMappingsOnly[. */ - public static final int IX_MIN_YES_NO = 10; - /** Mappings are comp-normalized. */ - public static final int IX_MIN_NO_NO = 11; - public static final int IX_LIMIT_NO_NO = 12; - public static final int IX_MIN_MAYBE_YES = 13; - - /** Mappings only in [minYesNoMappingsOnly..minNoNo[. */ - public static final int IX_MIN_YES_NO_MAPPINGS_ONLY = 14; - /** Mappings are not comp-normalized but have a comp boundary before. */ - public static final int IX_MIN_NO_NO_COMP_BOUNDARY_BEFORE = 15; - /** Mappings do not have a comp boundary before. */ - public static final int IX_MIN_NO_NO_COMP_NO_MAYBE_CC = 16; - /** Mappings to the empty string. */ - public static final int IX_MIN_NO_NO_EMPTY = 17; - - public static final int IX_MIN_LCCC_CP = 18; - public static final int IX_COUNT = 20; - - public static final int MAPPING_HAS_CCC_LCCC_WORD = 0x80; - public static final int MAPPING_HAS_RAW_MAPPING = 0x40; - // unused bit 0x20; - public static final int MAPPING_LENGTH_MASK = 0x1f; - - public static final int COMP_1_LAST_TUPLE = 0x8000; - public static final int COMP_1_TRIPLE = 1; - public static final int COMP_1_TRAIL_LIMIT = 0x3400; - public static final int COMP_1_TRAIL_MASK = 0x7ffe; - public static final int COMP_1_TRAIL_SHIFT = 9; // 10-1 for the "triple" bit - public static final int COMP_2_TRAIL_SHIFT = 6; - public static final int COMP_2_TRAIL_MASK = 0xffc0; - - // higher-level functionality ------------------------------------------ *** - - /** - * Decomposes s[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. - */ - public void decompose(CharSequence s, int src, int limit, StringBuilder dest, int destLengthEstimate) { - if (destLengthEstimate < 0) { - destLengthEstimate = limit - src; - } - dest.setLength(0); - ReorderingBuffer buffer = new ReorderingBuffer(this, dest, destLengthEstimate); - decompose(s, src, limit, buffer); - } - - // Dual functionality: - // buffer!=NULL: normalize - // buffer==NULL: isNormalized/quickCheck/spanQuickCheckYes - public int decompose(CharSequence s, int src, int limit, ReorderingBuffer buffer) { - int minNoCP = minDecompNoCP; - - int prevSrc; - int c = 0; - int norm16 = 0; - - // only for quick check - int prevBoundary = src; - int prevCC = 0; - - for (;;) { - // count code units below the minimum or with irrelevant data for the quick - // check - for (prevSrc = src; src != limit;) { - if ((c = s.charAt(src)) < minNoCP || isMostDecompYesAndZeroCC(norm16 = normTrie.bmpGet(c))) { - ++src; - } else if (!UTF16Plus.isLeadSurrogate(c)) { - break; - } else { - char c2; - if ((src + 1) != limit && Character.isLowSurrogate(c2 = s.charAt(src + 1))) { - c = Character.toCodePoint((char) c, c2); - norm16 = normTrie.suppGet(c); - if (isMostDecompYesAndZeroCC(norm16)) { - src += 2; - } else { - break; - } - } else { - ++src; // unpaired lead surrogate: inert - } - } - } - // copy these code units all at once - if (src != prevSrc) { - if (buffer != null) { - buffer.flushAndAppendZeroCC(s, prevSrc, src); - } else { - prevCC = 0; - prevBoundary = src; - } - } - if (src == limit) { - break; - } - - // Check one above-minimum, relevant code point. - src += Character.charCount(c); - if (buffer != null) { - decompose(c, norm16, buffer); - } else { - if (isDecompYes(norm16)) { - int 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; - } - - public void decomposeAndAppend(CharSequence s, boolean doDecompose, ReorderingBuffer buffer) { - int limit = s.length(); - if (limit == 0) { - return; - } - if (doDecompose) { - decompose(s, 0, limit, buffer); - return; - } - // Just merge the strings at the boundary. - int c = Character.codePointAt(s, 0); - int src = 0; - int firstCC, prevCC, cc; - firstCC = prevCC = cc = getCC(getNorm16(c)); - while (cc != 0) { - prevCC = cc; - src += Character.charCount(c); - if (src >= limit) { - break; - } - c = Character.codePointAt(s, src); - cc = getCC(getNorm16(c)); - } - ; - buffer.append(s, 0, src, false, firstCC, prevCC); - buffer.append(s, src, limit); - } - - // Very similar to composeQuickCheck(): Make the same changes in both places if - // relevant. - // doCompose: normalize - // !doCompose: isNormalized (buffer must be empty and initialized) - public boolean compose(CharSequence s, int src, int limit, boolean onlyContiguous, boolean doCompose, - ReorderingBuffer buffer) { - int prevBoundary = src; - int minNoMaybeCP = minCompNoMaybeCP; - - for (;;) { - // Fast path: Scan over a sequence of characters below the minimum "no or maybe" - // code point, - // or with (compYes && ccc==0) properties. - int prevSrc; - int c = 0; - int norm16 = 0; - for (;;) { - if (src == limit) { - if (prevBoundary != limit && doCompose) { - buffer.append(s, prevBoundary, limit); - } - return true; - } - if ((c = s.charAt(src)) < minNoMaybeCP || isCompYesAndZeroCC(norm16 = normTrie.bmpGet(c))) { - ++src; - } else { - prevSrc = src++; - if (!UTF16Plus.isLeadSurrogate(c)) { - break; - } else { - char c2; - if (src != limit && Character.isLowSurrogate(c2 = s.charAt(src))) { - ++src; - c = Character.toCodePoint((char) c, c2); - norm16 = normTrie.suppGet(c); - 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 (!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(s, src, limit)) { - if (prevBoundary != prevSrc) { - buffer.append(s, prevBoundary, prevSrc); - } - buffer.append(mapAlgorithmic(c, norm16), 0); - prevBoundary = src; - continue; - } - } else if (norm16 < minNoNoCompBoundaryBefore) { - // The mapping is comp-normalized which also implies hasCompBoundaryBefore. - if (norm16HasCompBoundaryAfter(norm16, onlyContiguous) || hasCompBoundaryBefore(s, src, limit)) { - if (prevBoundary != prevSrc) { - buffer.append(s, prevBoundary, prevSrc); - } - int mapping = norm16 >> OFFSET_SHIFT; - int length = extraData.charAt(mapping++) & MAPPING_LENGTH_MASK; - buffer.append(extraData, mapping, mapping + length); - 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(s, src, limit) - || hasCompBoundaryAfter(s, prevBoundary, prevSrc, onlyContiguous)) { - if (prevBoundary != prevSrc) { - buffer.append(s, prevBoundary, prevSrc); - } - 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) { - char prev = s.charAt(prevSrc - 1); - if (c < Hangul.JAMO_T_BASE) { - // The current character is a Jamo Vowel, - // compose with previous Jamo L and following Jamo T. - char l = (char) (prev - Hangul.JAMO_L_BASE); - if (l < Hangul.JAMO_L_COUNT) { - if (!doCompose) { - return false; - } - int t; - if (src != limit && 0 < (t = (s.charAt(src) - Hangul.JAMO_T_BASE)) && t < Hangul.JAMO_T_COUNT) { - // The next character is a Jamo T. - ++src; - } else if (hasCompBoundaryBefore(s, src, limit)) { - // No Jamo T follows, not even via decomposition. - t = 0; - } else { - t = -1; - } - if (t >= 0) { - int 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.append(s, prevBoundary, prevSrc); - } - buffer.append((char) syllable); - 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; - } - int syllable = prev + c - Hangul.JAMO_T_BASE; - --prevSrc; // Replace the Hangul LV as well. - if (prevBoundary != prevSrc) { - buffer.append(s, prevBoundary, prevSrc); - } - buffer.append((char) syllable); - 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. - int cc = getCCFromNormalYesOrMaybe(norm16); // cc!=0 - if (onlyContiguous /* FCC */ && getPreviousTrailCC(s, 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. - int n16; - for (;;) { - if (src == limit) { - if (doCompose) { - buffer.append(s, prevBoundary, limit); - } - return true; - } - int prevCC = cc; - c = Character.codePointAt(s, src); - n16 = normTrie.get(c); - if (n16 >= MIN_YES_YES_WITH_CC) { - cc = getCCFromNormalYesOrMaybe(n16); - if (prevCC > cc) { - if (!doCompose) { - return false; - } - break; - } - } else { - break; - } - src += Character.charCount(c); - } - // p 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 += Character.charCount(c); - } - 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)) { - c = Character.codePointBefore(s, prevSrc); - norm16 = normTrie.get(c); - if (!norm16HasCompBoundaryAfter(norm16, onlyContiguous)) { - prevSrc -= Character.charCount(c); - } - } - if (doCompose && prevBoundary != prevSrc) { - buffer.append(s, prevBoundary, prevSrc); - } - int recomposeStartIndex = buffer.length(); - // We know there is not a boundary here. - decomposeShort(s, prevSrc, src, false /* !stopAtCompBoundary */, onlyContiguous, buffer); - // Decompose until the next boundary. - src = decomposeShort(s, src, limit, true /* stopAtCompBoundary */, onlyContiguous, buffer); - recompose(buffer, recomposeStartIndex, onlyContiguous); - if (!doCompose) { - if (!buffer.equals(s, prevSrc, src)) { - return false; - } - buffer.remove(); - } - prevBoundary = src; - } - } - - /** - * Very similar to compose(): Make the same changes in both places if relevant. - * doSpan: spanQuickCheckYes (ignore bit 0 of the return value) !doSpan: - * quickCheck - * - * @return bits 31..1: spanQuickCheckYes (==s.length() if "yes") and bit 0: set - * if "maybe"; otherwise, if the span length<s.length() then the - * quick check result is "no" - */ - public int composeQuickCheck(CharSequence s, int src, int limit, boolean onlyContiguous, boolean doSpan) { - int qcResult = 0; - int prevBoundary = src; - int minNoMaybeCP = minCompNoMaybeCP; - - for (;;) { - // Fast path: Scan over a sequence of characters below the minimum "no or maybe" - // code point, - // or with (compYes && ccc==0) properties. - int prevSrc; - int c = 0; - int norm16 = 0; - for (;;) { - if (src == limit) { - return (src << 1) | qcResult; // "yes" or "maybe" - } - if ((c = s.charAt(src)) < minNoMaybeCP || isCompYesAndZeroCC(norm16 = normTrie.bmpGet(c))) { - ++src; - } else { - prevSrc = src++; - if (!UTF16Plus.isLeadSurrogate(c)) { - break; - } else { - char c2; - if (src != limit && Character.isLowSurrogate(c2 = s.charAt(src))) { - ++src; - c = Character.toCodePoint((char) c, c2); - norm16 = normTrie.suppGet(c); - 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. - - int prevNorm16 = INERT; - if (prevBoundary != prevSrc) { - prevBoundary = prevSrc; - if (!norm16HasCompBoundaryBefore(norm16)) { - c = Character.codePointBefore(s, prevSrc); - int n16 = getNorm16(c); - if (!norm16HasCompBoundaryAfter(n16, onlyContiguous)) { - prevBoundary -= Character.charCount(c); - prevNorm16 = n16; - } - } - } - - if (isMaybeOrNonZeroCC(norm16)) { - int 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. - for (;;) { - if (norm16 < MIN_YES_YES_WITH_CC) { - if (!doSpan) { - qcResult = 1; - } else { - return prevBoundary << 1; // spanYes does not care to know it's "maybe" - } - } - if (src == limit) { - return (src << 1) | qcResult; // "yes" or "maybe" - } - int prevCC = cc; - c = Character.codePointAt(s, src); - norm16 = getNorm16(c); - if (isMaybeOrNonZeroCC(norm16)) { - cc = getCCFromYesOrMaybe(norm16); - if (!(prevCC <= cc || cc == 0)) { - break; - } - } else { - break; - } - src += Character.charCount(c); - } - // src is after the last in-order combining mark. - if (isCompYesAndZeroCC(norm16)) { - prevBoundary = src; - src += Character.charCount(c); - continue; - } - } - } - return prevBoundary << 1; // "no" - } - } - - public void composeAndAppend(CharSequence s, boolean doCompose, boolean onlyContiguous, ReorderingBuffer buffer) { - int src = 0, limit = s.length(); - if (!buffer.isEmpty()) { - int firstStarterInSrc = findNextCompBoundary(s, 0, limit, onlyContiguous); - if (0 != firstStarterInSrc) { - int lastStarterInDest = findPreviousCompBoundary(buffer.getStringBuilder(), buffer.length(), - onlyContiguous); - StringBuilder middle = new StringBuilder( - (buffer.length() - lastStarterInDest) + firstStarterInSrc + 16); - middle.append(buffer.getStringBuilder(), lastStarterInDest, buffer.length()); - buffer.removeSuffix(buffer.length() - lastStarterInDest); - middle.append(s, 0, firstStarterInSrc); - compose(middle, 0, middle.length(), onlyContiguous, true, buffer); - src = firstStarterInSrc; - } - } - if (doCompose) { - compose(s, src, limit, onlyContiguous, true, buffer); - } else { - buffer.append(s, src, limit); - } - } - - // Dual functionality: - // buffer!=NULL: normalize - // buffer==NULL: isNormalized/quickCheck/spanQuickCheckYes - public int makeFCD(CharSequence s, int src, int limit, ReorderingBuffer buffer) { - // 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. - - // Tracks the last FCD-safe boundary, before lccc=0 or after properly-ordered - // tccc<=1. - // Similar to the prevBoundary in the compose() implementation. - int prevBoundary = src; - int prevSrc; - int c = 0; - int prevFCD16 = 0; - int fcd16 = 0; - - for (;;) { - // count code units with lccc==0 - for (prevSrc = src; src != limit;) { - if ((c = s.charAt(src)) < minLcccCP) { - prevFCD16 = ~c; - ++src; - } else if (!singleLeadMightHaveNonZeroFCD16(c)) { - prevFCD16 = 0; - ++src; - } else { - if (UTF16Plus.isLeadSurrogate(c)) { - char c2; - if ((src + 1) != limit && Character.isLowSurrogate(c2 = s.charAt(src + 1))) { - c = Character.toCodePoint((char) c, c2); - } - } - if ((fcd16 = getFCD16FromNormData(c)) <= 0xff) { - prevFCD16 = fcd16; - src += Character.charCount(c); - } else { - break; - } - } - } - // copy these code units all at once - if (src != prevSrc) { - if (src == limit) { - if (buffer != null) { - buffer.flushAndAppendZeroCC(s, prevSrc, src); - } - 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. - int prev = ~prevFCD16; - if (prev < minDecompNoCP) { - prevFCD16 = 0; - } else { - prevFCD16 = getFCD16FromNormData(prev); - if (prevFCD16 > 1) { - --prevBoundary; - } - } - } else { - int p = src - 1; - if (Character.isLowSurrogate(s.charAt(p)) && prevSrc < p - && Character.isHighSurrogate(s.charAt(p - 1))) { - --p; - // Need to fetch the previous character's FCD value because - // prevFCD16 was just for the trail surrogate code point. - prevFCD16 = getFCD16FromNormData(Character.toCodePoint(s.charAt(p), s.charAt(p + 1))); - // Still known to have lccc==0 because its lead surrogate unit had lccc==0. - } - if (prevFCD16 > 1) { - prevBoundary = p; - } - } - if (buffer != null) { - // The last lccc==0 character is excluded from the - // flush-and-append call in case it needs to be modified. - buffer.flushAndAppendZeroCC(s, prevSrc, prevBoundary); - buffer.append(s, prevBoundary, src); - } - // The start of the current character (c). - prevSrc = src; - } else if (src == limit) { - break; - } - - src += Character.charCount(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); - } - 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(prevSrc - prevBoundary); - /* - * Find the part of the source that needs to be decomposed, up to the next safe - * boundary. - */ - src = findNextFCDBoundary(s, src, limit); - /* - * The source text does not fulfill the conditions for FCD. Decompose and - * reorder a limited piece of the text. - */ - decomposeShort(s, prevBoundary, src, false, false, buffer); - prevBoundary = src; - prevFCD16 = 0; - } - } - return src; - } - - public boolean hasDecompBoundaryBefore(int c) { - return c < minLcccCP || (c <= 0xffff && !singleLeadMightHaveNonZeroFCD16(c)) - || norm16HasDecompBoundaryBefore(getNorm16(c)); - } - - public boolean norm16HasDecompBoundaryBefore(int norm16) { - 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 - int mapping = norm16 >> OFFSET_SHIFT; - int firstUnit = extraData.charAt(mapping); - // true if leadCC==0 (hasFCDBoundaryBefore()) - return (firstUnit & MAPPING_HAS_CCC_LCCC_WORD) == 0 || (extraData.charAt(mapping - 1) & 0xff00) == 0; - } - - public boolean hasDecompBoundaryAfter(int c) { - if (c < minDecompNoCP) { - return true; - } - if (c <= 0xffff && !singleLeadMightHaveNonZeroFCD16(c)) { - return true; - } - return norm16HasDecompBoundaryAfter(getNorm16(c)); - } - - public boolean norm16HasDecompBoundaryAfter(int norm16) { - 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 - int mapping = norm16 >> OFFSET_SHIFT; - int firstUnit = extraData.charAt(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 || (extraData.charAt(mapping - 1) & 0xff00) == 0; - } - - public boolean isDecompInert(int c) { - return isDecompYesAndZeroCC(getNorm16(c)); - } - - public boolean hasCompBoundaryBefore(int c) { - return c < minCompNoMaybeCP || norm16HasCompBoundaryBefore(getNorm16(c)); - } - - public boolean hasCompBoundaryAfter(int c, boolean onlyContiguous) { - return norm16HasCompBoundaryAfter(getNorm16(c), onlyContiguous); - } - - private boolean isMaybe(int norm16) { - return minMaybeYes <= norm16 && norm16 <= JAMO_VT; - } - - private boolean isMaybeOrNonZeroCC(int norm16) { - return norm16 >= minMaybeYes; - } - - private static boolean isInert(int norm16) { - return norm16 == INERT; - } - - private static boolean isJamoVT(int norm16) { - return norm16 == JAMO_VT; - } - - private int hangulLVT() { - return minYesNoMappingsOnly | HAS_COMP_BOUNDARY_AFTER; - } - - private boolean isHangulLV(int norm16) { - return norm16 == minYesNo; - } - - private boolean isHangulLVT(int norm16) { - return norm16 == hangulLVT(); - } - - private boolean isCompYesAndZeroCC(int norm16) { - return norm16 < minNoNo; - } - - // UBool isCompYes(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; - // } - private int getCCFromNoNo(int norm16) { - int mapping = norm16 >> OFFSET_SHIFT; - if ((extraData.charAt(mapping) & MAPPING_HAS_CCC_LCCC_WORD) != 0) { - return extraData.charAt(mapping - 1) & 0xff; - } else { - return 0; - } - } - - int getTrailCCFromCompYesAndZeroCC(int norm16) { - 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 extraData.charAt(norm16 >> OFFSET_SHIFT) >> 8; // tccc from yesNo - } - } - - // Requires algorithmic-NoNo. - private int mapAlgorithmic(int c, int norm16) { - return c + (norm16 >> DELTA_SHIFT) - centerNoNoDelta; - } - - // Requires minYesNo>OFFSET_SHIFT); - // } - - /** - * @return index into maybeYesCompositions, or -1 - */ - private int getCompositionsListForDecompYes(int norm16) { - if (norm16 < JAMO_L || MIN_NORMAL_MAYBE_YES <= norm16) { - return -1; - } else { - if ((norm16 -= minMaybeYes) < 0) { - // norm16> OFFSET_SHIFT; - } - } - - /** - * @return index into maybeYesCompositions - */ - private int getCompositionsListForComposite(int norm16) { - // A composite has both mapping & compositions list. - int list = ((MIN_NORMAL_MAYBE_YES - minMaybeYes) + norm16) >> OFFSET_SHIFT; - int firstUnit = maybeYesCompositions.charAt(list); - return list + // mapping in maybeYesCompositions - 1 + // +1 to skip the first unit with the mapping length - (firstUnit & MAPPING_LENGTH_MASK); // + mapping length - } - - // 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. - // Public in Java for collation implementation code. - private int decomposeShort(CharSequence s, int src, int limit, boolean stopAtCompBoundary, boolean onlyContiguous, - ReorderingBuffer buffer) { - while (src < limit) { - int c = Character.codePointAt(s, src); - if (stopAtCompBoundary && c < minCompNoMaybeCP) { - return src; - } - int norm16 = getNorm16(c); - if (stopAtCompBoundary && norm16HasCompBoundaryBefore(norm16)) { - return src; - } - src += Character.charCount(c); - decompose(c, norm16, buffer); - if (stopAtCompBoundary && norm16HasCompBoundaryAfter(norm16, onlyContiguous)) { - return src; - } - } - return src; - } - - private void decompose(int c, int norm16, ReorderingBuffer buffer) { - // get the decomposition and the lead and trail cc's - if (norm16 >= limitNoNo) { - if (isMaybeOrNonZeroCC(norm16)) { - buffer.append(c, getCCFromYesOrMaybe(norm16)); - return; - } - // Maps to an isCompYesAndZeroCC. - c = mapAlgorithmic(c, norm16); - norm16 = getRawNorm16(c); - } - if (norm16 < minYesNo) { - // c does not decompose - buffer.append(c, 0); - } else if (isHangulLV(norm16) || isHangulLVT(norm16)) { - // Hangul syllable: decompose algorithmically - Hangul.decompose(c, buffer); - } else { - // c decomposes, get everything from the variable-length extra data - int mapping = norm16 >> OFFSET_SHIFT; - int firstUnit = extraData.charAt(mapping); - int length = firstUnit & MAPPING_LENGTH_MASK; - int leadCC, trailCC; - trailCC = firstUnit >> 8; - if ((firstUnit & MAPPING_HAS_CCC_LCCC_WORD) != 0) { - leadCC = extraData.charAt(mapping - 1) >> 8; - } else { - leadCC = 0; - } - ++mapping; // skip over the firstUnit - buffer.append(extraData, mapping, mapping + length, true, leadCC, trailCC); - } - } - - /** - * 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. - */ - private static int combine(String compositions, int list, int trail) { - int key1, firstUnit; - if (trail < COMP_1_TRAIL_LIMIT) { - // trail character is 0..33FF - // result entry may have 2 or 3 units - key1 = (trail << 1); - while (key1 > (firstUnit = compositions.charAt(list))) { - list += 2 + (firstUnit & COMP_1_TRIPLE); - } - if (key1 == (firstUnit & COMP_1_TRAIL_MASK)) { - if ((firstUnit & COMP_1_TRIPLE) != 0) { - return (compositions.charAt(list + 1) << 16) | compositions.charAt(list + 2); - } else { - return compositions.charAt(list + 1); - } - } - } else { - // trail character is 3400..10FFFF - // result entry has 3 units - key1 = COMP_1_TRAIL_LIMIT + (((trail >> COMP_1_TRAIL_SHIFT)) & ~COMP_1_TRIPLE); - int key2 = (trail << COMP_2_TRAIL_SHIFT) & 0xffff; - int secondUnit; - for (;;) { - if (key1 > (firstUnit = compositions.charAt(list))) { - list += 2 + (firstUnit & COMP_1_TRIPLE); - } else if (key1 == (firstUnit & COMP_1_TRAIL_MASK)) { - if (key2 > (secondUnit = compositions.charAt(list + 1))) { - if ((firstUnit & COMP_1_LAST_TUPLE) != 0) { - break; - } else { - list += 3; - } - } else if (key2 == (secondUnit & COMP_2_TRAIL_MASK)) { - return ((secondUnit & ~COMP_2_TRAIL_MASK) << 16) | compositions.charAt(list + 2); - } else { - break; - } - } else { - break; - } - } - } - return -1; - } - - /* - * 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. - */ - private void recompose(ReorderingBuffer buffer, int recomposeStartIndex, boolean onlyContiguous) { - StringBuilder sb = buffer.getStringBuilder(); - int p = recomposeStartIndex; - if (p == sb.length()) { - return; - } - - int starter, pRemove; - int compositionsList; - int c, compositeAndFwd; - int norm16; - int cc, prevCC; - boolean 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 = -1; // used as indicator for whether we have a forward-combining starter - starter = -1; - starterIsSupplementary = false; - prevCC = 0; - - for (;;) { - c = sb.codePointAt(p); - p += Character.charCount(c); - norm16 = getNorm16(c); - cc = getCCFromYesOrMaybe(norm16); - if ( // this character combines backward and - isMaybe(norm16) && - // we have seen a starter that combines forward and - compositionsList >= 0 && - // the backward-combining character is not blocked - (prevCC < cc || prevCC == 0)) { - if (isJamoVT(norm16)) { - // c is a Jamo V/T, see if we can compose it with the previous character. - if (c < Hangul.JAMO_T_BASE) { - // c is a Jamo Vowel, compose with previous Jamo L and following Jamo T. - char prev = (char) (sb.charAt(starter) - Hangul.JAMO_L_BASE); - if (prev < Hangul.JAMO_L_COUNT) { - pRemove = p - 1; - char syllable = (char) (Hangul.HANGUL_BASE - + (prev * Hangul.JAMO_V_COUNT + (c - Hangul.JAMO_V_BASE)) * Hangul.JAMO_T_COUNT); - char t; - if (p != sb.length() - && (t = (char) (sb.charAt(p) - Hangul.JAMO_T_BASE)) < Hangul.JAMO_T_COUNT) { - ++p; - syllable += t; // The next character was a Jamo T. - } - sb.setCharAt(starter, syllable); - // remove the Jamo V/T - sb.delete(pRemove, p); - p = pRemove; - } - } - /* - * No "else" for Jamo T: Since the input is in NFD, there are no Hangul LV - * syllables that a Jamo T could combine with. All Jamo Ts are combined above - * when handling Jamo Vs. - */ - if (p == sb.length()) { - break; - } - compositionsList = -1; - continue; - } else if ((compositeAndFwd = combine(maybeYesCompositions, compositionsList, c)) >= 0) { - // The starter and the combining mark (c) do combine. - int composite = compositeAndFwd >> 1; - - // Remove the combining mark. - pRemove = p - Character.charCount(c); // pRemove & p: start & limit of the combining mark - sb.delete(pRemove, p); - p = pRemove; - // Replace the starter with the composite. - if (starterIsSupplementary) { - if (composite > 0xffff) { - // both are supplementary - sb.setCharAt(starter, UTF16.getLeadSurrogate(composite)); - sb.setCharAt(starter + 1, UTF16.getTrailSurrogate(composite)); - } else { - sb.setCharAt(starter, (char) c); - sb.deleteCharAt(starter + 1); - // The composite is shorter than the starter, - // move the intermediate characters forward one. - starterIsSupplementary = false; - --p; - } - } else if (composite > 0xffff) { - // The composite is longer than the starter, - // move the intermediate characters back one. - starterIsSupplementary = true; - sb.setCharAt(starter, UTF16.getLeadSurrogate(composite)); - sb.insert(starter + 1, UTF16.getTrailSurrogate(composite)); - ++p; - } else { - // both are on the BMP - sb.setCharAt(starter, (char) composite); - } - - // Keep prevCC because we removed the combining mark. - - if (p == sb.length()) { - break; - } - // Is the composite a starter that combines forward? - if ((compositeAndFwd & 1) != 0) { - compositionsList = getCompositionsListForComposite(getRawNorm16(composite)); - } else { - compositionsList = -1; - } - - // We combined; continue with looking for compositions. - continue; - } - } - - // no combination this time - prevCC = cc; - if (p == sb.length()) { - break; - } - - // If c did not combine, then check if it is a starter. - if (cc == 0) { - // Found a new starter. - if ((compositionsList = getCompositionsListForDecompYes(norm16)) >= 0) { - // It may combine with something, prepare for it. - if (c <= 0xffff) { - starterIsSupplementary = false; - starter = p - 1; - } else { - starterIsSupplementary = true; - starter = p - 2; - } - } - } else if (onlyContiguous) { - // FCC: no discontiguous compositions; any intervening character blocks. - compositionsList = -1; - } - } - buffer.flush(); - } - - /** - * Does c have a composition boundary before it? True if its decomposition - * begins with a character that has ccc=0 && NFC_QC=Yes (isCompYesAndZeroCC()). - * As a shortcut, this is true if c itself has ccc=0 && NFC_QC=Yes - * (isCompYesAndZeroCC()) so we need not decompose. - */ - private boolean hasCompBoundaryBefore(int c, int norm16) { - return c < minCompNoMaybeCP || norm16HasCompBoundaryBefore(norm16); - } - - private boolean norm16HasCompBoundaryBefore(int norm16) { - return norm16 < minNoNoCompNoMaybeCC || isAlgorithmicNoNo(norm16); - } - - private boolean hasCompBoundaryBefore(CharSequence s, int src, int limit) { - return src == limit || hasCompBoundaryBefore(Character.codePointAt(s, src)); - } - - private boolean norm16HasCompBoundaryAfter(int norm16, boolean onlyContiguous) { - return (norm16 & HAS_COMP_BOUNDARY_AFTER) != 0 && (!onlyContiguous || isTrailCC01ForCompBoundaryAfter(norm16)); - } - - private boolean hasCompBoundaryAfter(CharSequence s, int start, int p, boolean onlyContiguous) { - return start == p || hasCompBoundaryAfter(Character.codePointBefore(s, p), onlyContiguous); - } - - /** For FCC: Given norm16 HAS_COMP_BOUNDARY_AFTER, does it have tccc<=1? */ - private boolean isTrailCC01ForCompBoundaryAfter(int norm16) { - return isInert(norm16) || (isDecompNoAlgorithmic(norm16) ? (norm16 & DELTA_TCCC_MASK) <= DELTA_TCCC_1 - : extraData.charAt(norm16 >> OFFSET_SHIFT) <= 0x1ff); - } - - private int findPreviousCompBoundary(CharSequence s, int p, boolean onlyContiguous) { - while (p > 0) { - int c = Character.codePointBefore(s, p); - int norm16 = getNorm16(c); - if (norm16HasCompBoundaryAfter(norm16, onlyContiguous)) { - break; - } - p -= Character.charCount(c); - if (hasCompBoundaryBefore(c, norm16)) { - break; - } - } - return p; - } - - private int findNextCompBoundary(CharSequence s, int p, int limit, boolean onlyContiguous) { - while (p < limit) { - int c = Character.codePointAt(s, p); - int norm16 = normTrie.get(c); - if (hasCompBoundaryBefore(c, norm16)) { - break; - } - p += Character.charCount(c); - if (norm16HasCompBoundaryAfter(norm16, onlyContiguous)) { - break; - } - } - return p; - } - - private int findNextFCDBoundary(CharSequence s, int p, int limit) { - while (p < limit) { - int c = Character.codePointAt(s, p); - int norm16; - if (c < minLcccCP || norm16HasDecompBoundaryBefore(norm16 = getNorm16(c))) { - break; - } - p += Character.charCount(c); - if (norm16HasDecompBoundaryAfter(norm16)) { - break; - } - } - return p; - } - - /** - * Get the canonical decomposition sherman for ComposedCharIter - */ - public static int getDecompose(int chars[], String decomps[]) { - Normalizer2 impl = Normalizer2.getNFDInstance(); - - int length = 0; - int norm16 = 0; - int ch = -1; - int i = 0; - - while (++ch < 0x2fa1e) { // no cannoical above 0x3ffff - // TBD !!!! the hack code heres save us about 50ms for startup - // need a better solution/lookup - if (ch == 0x30ff) - ch = 0xf900; - else if (ch == 0x115bc) - ch = 0x1d15e; - else if (ch == 0x1d1c1) - ch = 0x2f800; - - String s = impl.getDecomposition(ch); - - if (s != null && i < chars.length) { - chars[i] = ch; - decomps[i++] = s; - } - } - return i; - } - - // ------------------------------------------------------ - // special method for Collation (RBTableBuilder.build()) - // ------------------------------------------------------ - private static boolean needSingleQuotation(char c) { - return (c >= 0x0009 && c <= 0x000D) || (c >= 0x0020 && c <= 0x002F) || (c >= 0x003A && c <= 0x0040) - || (c >= 0x005B && c <= 0x0060) || (c >= 0x007B && c <= 0x007E); - } - - public static String canonicalDecomposeWithSingleQuotation(String string) { - Normalizer2 impl = Normalizer2.getNFDInstance(); - char[] src = string.toCharArray(); - int srcIndex = 0; - int srcLimit = src.length; - char[] dest = new char[src.length * 3]; // MAX_BUF_SIZE_DECOMPOSE = 3 - int destIndex = 0; - int destLimit = dest.length; - - int prevSrc; - String norm; - int reorderStartIndex, length; - char c1, c2; - int cp; - int minNoMaybe = 0x00c0; - int cc, prevCC, trailCC; - char[] p; - int pStart; - - // initialize - reorderStartIndex = 0; - prevCC = 0; - norm = null; - cp = 0; - pStart = 0; - - cc = trailCC = -1; // initialize to bogus value - c1 = 0; - for (;;) { - prevSrc = srcIndex; - // quick check (1)less than minNoMaybe (2)no decomp (3)hangual - while (srcIndex != srcLimit && ((c1 = src[srcIndex]) < minNoMaybe - || (norm = impl.getDecomposition(cp = string.codePointAt(srcIndex))) == null - || (c1 >= '\uac00' && c1 <= '\ud7a3'))) { // Hangul Syllables - prevCC = 0; - srcIndex += (cp < 0x10000) ? 1 : 2; - } - - // copy these code units all at once - if (srcIndex != prevSrc) { - length = srcIndex - prevSrc; - if ((destIndex + length) <= destLimit) { - System.arraycopy(src, prevSrc, dest, destIndex, length); - } - - destIndex += length; - reorderStartIndex = destIndex; - } - - // end of source reached? - if (srcIndex == srcLimit) { - break; - } - - // cp already contains *src and norm32 is set for it, increment src - srcIndex += (cp < 0x10000) ? 1 : 2; - - if (cp < Character.MIN_SUPPLEMENTARY_CODE_POINT) { - c2 = 0; - length = 1; - - if (Character.isHighSurrogate(c1) || Character.isLowSurrogate(c1)) { - norm = null; - } - } else { - length = 2; - c2 = src[srcIndex - 1]; - } - - // get the decomposition and the lead and trail cc's - if (norm == null) { - // cp does not decompose - cc = trailCC = UCharacter.getCombiningClass(cp); - p = null; - pStart = -1; - } else { - - pStart = 0; - p = norm.toCharArray(); - length = p.length; - int cpNum = norm.codePointCount(0, length); - cc = UCharacter.getCombiningClass(norm.codePointAt(0)); - trailCC = UCharacter.getCombiningClass(norm.codePointAt(cpNum - 1)); - if (length == 1) { - // fastpath a single code unit from decomposition - c1 = p[pStart]; - c2 = 0; - p = null; - pStart = -1; - } - } - - if ((destIndex + length * 3) >= destLimit) { // 2 SingleQuotations - // buffer overflow - char[] tmpBuf = new char[destLimit * 2]; - System.arraycopy(dest, 0, tmpBuf, 0, destIndex); - dest = tmpBuf; - destLimit = dest.length; - } - - // append the decomposition to the destination buffer, assume length>0 - { - int reorderSplit = destIndex; - if (p == null) { - // fastpath: single code point - if (needSingleQuotation(c1)) { - // if we need single quotation, no need to consider "prevCC" - // and it must NOT be a supplementary pair - dest[destIndex++] = '\''; - dest[destIndex++] = c1; - dest[destIndex++] = '\''; - trailCC = 0; - } else if (cc != 0 && cc < prevCC) { - // (c1, c2) is out of order with respect to the preceding - // text - destIndex += length; - trailCC = insertOrdered(dest, reorderStartIndex, reorderSplit, destIndex, c1, c2, cc); - } else { - // just append (c1, c2) - dest[destIndex++] = c1; - if (c2 != 0) { - dest[destIndex++] = c2; - } - } - } else { - // general: multiple code points (ordered by themselves) - // from decomposition - if (needSingleQuotation(p[pStart])) { - dest[destIndex++] = '\''; - dest[destIndex++] = p[pStart++]; - dest[destIndex++] = '\''; - length--; - do { - dest[destIndex++] = p[pStart++]; - } while (--length > 0); - } else if (cc != 0 && cc < prevCC) { - destIndex += length; - trailCC = mergeOrdered(dest, reorderStartIndex, reorderSplit, p, pStart, pStart + length); - } else { - // just append the decomposition - do { - dest[destIndex++] = p[pStart++]; - } while (--length > 0); - } - } - } - prevCC = trailCC; - if (prevCC == 0) { - reorderStartIndex = destIndex; - } - } - - return new String(dest, 0, destIndex); - } - - /** - * simpler, single-character version of mergeOrdered() - bubble-insert one - * single code point into the preceding string which is already canonically - * ordered (c, c2) may or may not yet have been inserted at src[current]..src[p] - * - * it must be p=current+lengthof(c, c2) i.e. p=current+(c2==0 ? 1 : 2) - * - * before: src[start]..src[current] is already ordered, and src[current]..src[p] - * may or may not hold (c, c2) but must be exactly the same length as (c, c2) - * after: src[start]..src[p] is ordered - * - * @return the trailing combining class - */ - private static int/* unsigned byte */ insertOrdered(char[] source, int start, int current, int p, char c1, char c2, - int/* unsigned byte */ cc) { - int back, preBack; - int r; - int prevCC, trailCC = cc; - - if (start < current && cc != 0) { - // search for the insertion point where cc>=prevCC - preBack = back = current; - - PrevArgs prevArgs = new PrevArgs(); - prevArgs.current = current; - prevArgs.start = start; - prevArgs.src = source; - prevArgs.c1 = c1; - prevArgs.c2 = c2; - - // get the prevCC - prevCC = getPrevCC(prevArgs); - preBack = prevArgs.current; - - if (cc < prevCC) { - // this will be the last code point, so keep its cc - trailCC = prevCC; - back = preBack; - while (start < preBack) { - prevCC = getPrevCC(prevArgs); - preBack = prevArgs.current; - if (cc >= prevCC) { - break; - } - back = preBack; - } - - // this is where we are right now with all these indicies: - // [start]..[pPreBack] 0..? code points that we can ignore - // [pPreBack]..[pBack] 0..1 code points with prevCC<=cc - // [pBack]..[current] 0..n code points with >cc, move up to insert (c, c2) - // [current]..[p] 1 code point (c, c2) with cc - - // move the code units in between up - r = p; - do { - source[--r] = source[--current]; - } while (back != current); - } - } - - // insert (c1, c2) - source[current] = c1; - if (c2 != 0) { - source[(current + 1)] = c2; - } - - // we know the cc of the last code point - return trailCC; - } - - /** - * merge two UTF-16 string parts together to canonically order (order by - * combining classes) their concatenation - * - * the two strings may already be adjacent, so that the merging is done in-place - * if the two strings are not adjacent, then the buffer holding the first one - * must be large enough the second string may or may not be ordered in itself - * - * before: [start]..[current] is already ordered, and [next]..[limit] may be - * ordered in itself, but is not in relation to [start..current[ after: - * [start..current+(limit-next)[ is ordered - * - * the algorithm is a simple bubble-sort that takes the characters from - * src[next++] and inserts them in correct combining class order into the - * preceding part of the string - * - * since this function is called much less often than the single-code point - * insertOrdered(), it just uses that for easier maintenance - * - * @return the trailing combining class - */ - private static int /* unsigned byte */ mergeOrdered(char[] source, int start, int current, char[] data, int next, - int limit) { - int r; - int /* unsigned byte */ cc, trailCC = 0; - boolean adjacent; - - adjacent = current == next; - NextCCArgs ncArgs = new NextCCArgs(); - ncArgs.source = data; - ncArgs.next = next; - ncArgs.limit = limit; - - if (start != current) { - - while (ncArgs.next < ncArgs.limit) { - cc = getNextCC(ncArgs); - if (cc == 0) { - // does not bubble back - trailCC = 0; - if (adjacent) { - current = ncArgs.next; - } else { - data[current++] = ncArgs.c1; - if (ncArgs.c2 != 0) { - data[current++] = ncArgs.c2; - } - } - break; - } else { - r = current + (ncArgs.c2 == 0 ? 1 : 2); - trailCC = insertOrdered(source, start, current, r, ncArgs.c1, ncArgs.c2, cc); - current = r; - } - } - } - - if (ncArgs.next == ncArgs.limit) { - // we know the cc of the last code point - return trailCC; - } else { - if (!adjacent) { - // copy the second string part - do { - source[current++] = data[ncArgs.next++]; - } while (ncArgs.next != ncArgs.limit); - ncArgs.limit = current; - } - PrevArgs prevArgs = new PrevArgs(); - prevArgs.src = data; - prevArgs.start = start; - prevArgs.current = ncArgs.limit; - return getPrevCC(prevArgs); - } - - } - - private static final class PrevArgs { - char[] src; - int start; - int current; - char c1; - char c2; - } - - private static final class NextCCArgs { - char[] source; - int next; - int limit; - char c1; - char c2; - } - - private static int /* unsigned byte */ getNextCC(NextCCArgs args) { - args.c1 = args.source[args.next++]; - args.c2 = 0; - - if (UTF16.isTrailSurrogate(args.c1)) { - /* unpaired second surrogate */ - return 0; - } else if (!UTF16.isLeadSurrogate(args.c1)) { - return UCharacter.getCombiningClass(args.c1); - } else if (args.next != args.limit && UTF16.isTrailSurrogate(args.c2 = args.source[args.next])) { - ++args.next; - return UCharacter.getCombiningClass(Character.toCodePoint(args.c1, args.c2)); - } else { - /* unpaired first surrogate */ - args.c2 = 0; - return 0; - } - } - - private static int /* unsigned */ getPrevCC(PrevArgs args) { - args.c1 = args.src[--args.current]; - args.c2 = 0; - - if (args.c1 < MIN_CCC_LCCC_CP) { - return 0; - } else if (UTF16.isLeadSurrogate(args.c1)) { - /* unpaired first surrogate */ - return 0; - } else if (!UTF16.isTrailSurrogate(args.c1)) { - return UCharacter.getCombiningClass(args.c1); - } else if (args.current != args.start && UTF16.isLeadSurrogate(args.c2 = args.src[args.current - 1])) { - --args.current; - return UCharacter.getCombiningClass(Character.toCodePoint(args.c2, args.c1)); - } else { - /* unpaired second surrogate */ - args.c2 = 0; - return 0; - } - } - - private int getPreviousTrailCC(CharSequence s, int start, int p) { - if (start == p) { - return 0; - } - return getFCD16(Character.codePointBefore(s, p)); - } - - private VersionInfo dataVersion; - - // BMP code point thresholds for quick check loops looking at single UTF-16 code - // units. - private int minDecompNoCP; - private int minCompNoMaybeCP; - private int minLcccCP; - - // Norm16 value thresholds for quick check combinations and types of extra data. - private int minYesNo; - private int minYesNoMappingsOnly; - private int minNoNo; - private int minNoNoCompBoundaryBefore; - private int minNoNoCompNoMaybeCC; - private int minNoNoEmpty; - private int limitNoNo; - private int centerNoNoDelta; - private int minMaybeYes; - - private CodePointTrie.Fast16 normTrie; - private String maybeYesCompositions; - private String extraData; // mappings and/or compositions for yesYes, yesNo & noNo characters - private byte[] smallFCD; // [0x100] one bit per 32 BMP code points, set if any FCD!=0 -} +/* + * Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ******************************************************************************* + * Copyright (C) 2009-2014, International Business Machines + * Corporation and others. All Rights Reserved. + ******************************************************************************* + */ +package jdk_internal.icu.impl; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import jdk_internal.icu.lang.UCharacter; +import jdk_internal.icu.text.Normalizer2; +import jdk_internal.icu.text.UTF16; +import jdk_internal.icu.util.CodePointTrie; +import jdk_internal.icu.util.VersionInfo; + +// Original filename in ICU4J: Normalizer2Impl.java +public final class NormalizerImpl { + public static final class Hangul { + /* Korean Hangul and Jamo constants */ + public static final int JAMO_L_BASE = 0x1100; /* "lead" jamo */ + public static final int JAMO_V_BASE = 0x1161; /* "vowel" jamo */ + public static final int JAMO_T_BASE = 0x11a7; /* "trail" jamo */ + + public static final int HANGUL_BASE = 0xac00; + public static final int HANGUL_END = 0xd7a3; + + public static final int JAMO_L_COUNT = 19; + public static final int JAMO_V_COUNT = 21; + public static final int JAMO_T_COUNT = 28; + + public static final int HANGUL_COUNT = JAMO_L_COUNT * JAMO_V_COUNT * JAMO_T_COUNT; + public static final int HANGUL_LIMIT = HANGUL_BASE + HANGUL_COUNT; + + public static boolean isHangul(int c) { + return HANGUL_BASE <= c && c < HANGUL_LIMIT; + } + + public static boolean isHangulLV(int c) { + c -= HANGUL_BASE; + return 0 <= c && c < HANGUL_COUNT && c % JAMO_T_COUNT == 0; + } + + /** + * Decomposes c, which must be a Hangul syllable, into buffer and returns the + * length of the decomposition (2 or 3). + */ + public static int decompose(int c, Appendable buffer) { + try { + c -= HANGUL_BASE; + int c2 = c % JAMO_T_COUNT; + c /= JAMO_T_COUNT; + buffer.append((char) (JAMO_L_BASE + c / JAMO_V_COUNT)); + buffer.append((char) (JAMO_V_BASE + c % JAMO_V_COUNT)); + if (c2 == 0) { + return 2; + } else { + buffer.append((char) (JAMO_T_BASE + c2)); + return 3; + } + } catch (IOException e) { + throw new InternalError(e); + } + } + } + + /** + * Writable buffer that takes care of canonical ordering. Its Appendable methods + * behave like the C++ implementation's appendZeroCC() methods. + *

+ * If dest is a StringBuilder, then the buffer writes directly to it. Otherwise, + * the buffer maintains a StringBuilder for intermediate text segments until no + * further changes are necessary and whole segments are appended. append() + * methods that take combining-class values always write to the StringBuilder. + * Other append() methods flush and append to the Appendable. + */ + public static final class ReorderingBuffer implements Appendable { + public ReorderingBuffer(NormalizerImpl ni, Appendable dest, int destCapacity) { + impl = ni; + app = dest; + if (app instanceof StringBuilder) { + appIsStringBuilder = true; + str = (StringBuilder) dest; + // In Java, the constructor subsumes public void init(int destCapacity) + str.ensureCapacity(destCapacity); + reorderStart = 0; + if (str.length() == 0) { + 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; + } + } else { + appIsStringBuilder = false; + str = new StringBuilder(); + reorderStart = 0; + lastCC = 0; + } + } + + public boolean isEmpty() { + return str.length() == 0; + } + + public int length() { + return str.length(); + } + + public int getLastCC() { + return lastCC; + } + + public StringBuilder getStringBuilder() { + return str; + } + + public boolean equals(CharSequence s, int start, int limit) { + return UTF16Plus.equal(str, 0, str.length(), s, start, limit); + } + + public void append(int c, int cc) { + if (lastCC <= cc || cc == 0) { + str.appendCodePoint(c); + lastCC = cc; + if (cc <= 1) { + reorderStart = str.length(); + } + } else { + insert(c, cc); + } + } + + public void append(CharSequence s, int start, int limit, boolean isNFD, int leadCC, int trailCC) { + if (start == limit) { + return; + } + if (lastCC <= leadCC || leadCC == 0) { + if (trailCC <= 1) { + reorderStart = str.length() + (limit - start); + } else if (leadCC <= 1) { + reorderStart = str.length() + 1; // Ok if not a code point boundary. + } + str.append(s, start, limit); + lastCC = trailCC; + } else { + int c = Character.codePointAt(s, start); + start += Character.charCount(c); + insert(c, leadCC); // insert first code point + while (start < limit) { + c = Character.codePointAt(s, start); + start += Character.charCount(c); + if (start < limit) { + if (isNFD) { + leadCC = getCCFromYesOrMaybe(impl.getNorm16(c)); + } else { + leadCC = impl.getCC(impl.getNorm16(c)); + } + } else { + leadCC = trailCC; + } + append(c, leadCC); + } + } + } + + // The following append() methods work like C++ appendZeroCC(). + // They assume that the cc or trailCC of their input is 0. + // Most of them implement Appendable interface methods. + @Override + public ReorderingBuffer append(char c) { + str.append(c); + lastCC = 0; + reorderStart = str.length(); + return this; + } + + public void appendZeroCC(int c) { + str.appendCodePoint(c); + lastCC = 0; + reorderStart = str.length(); + } + + @Override + public ReorderingBuffer append(CharSequence s) { + if (s.length() != 0) { + str.append(s); + lastCC = 0; + reorderStart = str.length(); + } + return this; + } + + @Override + public ReorderingBuffer append(CharSequence s, int start, int limit) { + if (start != limit) { + str.append(s, start, limit); + lastCC = 0; + reorderStart = str.length(); + } + return this; + } + + /** + * Flushes from the intermediate StringBuilder to the Appendable, if they are + * different objects. Used after recomposition. Must be called at the end when + * writing to a non-StringBuilder Appendable. + */ + public void flush() { + if (appIsStringBuilder) { + reorderStart = str.length(); + } else { + try { + app.append(str); + str.setLength(0); + reorderStart = 0; + } catch (IOException e) { + throw new InternalError(e); // Avoid declaring "throws IOException". + } + } + lastCC = 0; + } + + /** + * Flushes from the intermediate StringBuilder to the Appendable, if they are + * different objects. Then appends the new text to the Appendable or + * StringBuilder. Normally used after quick check loops find a non-empty + * sequence. + */ + public ReorderingBuffer flushAndAppendZeroCC(CharSequence s, int start, int limit) { + if (appIsStringBuilder) { + str.append(s, start, limit); + reorderStart = str.length(); + } else { + try { + app.append(str).append(s, start, limit); + str.setLength(0); + reorderStart = 0; + } catch (IOException e) { + throw new InternalError(e); // Avoid declaring "throws IOException". + } + } + lastCC = 0; + return this; + } + + public void remove() { + str.setLength(0); + lastCC = 0; + reorderStart = 0; + } + + public void removeSuffix(int suffixLength) { + int oldLength = str.length(); + str.delete(oldLength - suffixLength, oldLength); + lastCC = 0; + reorderStart = str.length(); + } + + // Inserts c somewhere before the last character. + // Requires 0 cc;) { + } + // insert c at codePointLimit, after the character with prevCC<=cc + if (c <= 0xffff) { + str.insert(codePointLimit, (char) c); + if (cc <= 1) { + reorderStart = codePointLimit + 1; + } + } else { + str.insert(codePointLimit, Character.toChars(c)); + if (cc <= 1) { + reorderStart = codePointLimit + 2; + } + } + } + + private final NormalizerImpl impl; + private final Appendable app; + private final StringBuilder str; + private final boolean appIsStringBuilder; + private int reorderStart; + private int lastCC; + + // private backward iterator + private void setIterator() { + codePointStart = str.length(); + } + + private void skipPrevious() { // Requires 0= codePointStart) { + return 0; + } + int c = str.codePointBefore(codePointStart); + codePointStart -= Character.charCount(c); + return impl.getCCFromYesOrMaybeCP(c); + } + + private int codePointStart, codePointLimit; + } + + // TODO: Propose as public API on the UTF16 class. + // TODO: Propose widening UTF16 methods that take char to take int. + // TODO: Propose widening UTF16 methods that take String to take CharSequence. + public static final class UTF16Plus { + /** + * Is this code point a lead surrogate (U+d800..U+dbff)? + * + * @param c code unit or code point + * @return true or false + */ + public static boolean isLeadSurrogate(int c) { + return (c & 0xfffffc00) == 0xd800; + } + + /** + * Assuming c is a surrogate code point (UTF16.isSurrogate(c)), is it a lead + * surrogate? + * + * @param c code unit or code point + * @return true or false + */ + public static boolean isSurrogateLead(int c) { + return (c & 0x400) == 0; + } + + /** + * Compares two CharSequence subsequences for binary equality. + * + * @param s1 first sequence + * @param start1 start offset in first sequence + * @param limit1 limit offset in first sequence + * @param s2 second sequence + * @param start2 start offset in second sequence + * @param limit2 limit offset in second sequence + * @return true if s1.subSequence(start1, limit1) contains the same text as + * s2.subSequence(start2, limit2) + */ + public static boolean equal(CharSequence s1, int start1, int limit1, CharSequence s2, int start2, int limit2) { + if ((limit1 - start1) != (limit2 - start2)) { + return false; + } + if (s1 == s2 && start1 == start2) { + return true; + } + while (start1 < limit1) { + if (s1.charAt(start1++) != s2.charAt(start2++)) { + return false; + } + } + return true; + } + } + + public NormalizerImpl() { + } + + private static final class IsAcceptable implements ICUBinary.Authenticate { + public boolean isDataVersionAcceptable(byte version[]) { + return version[0] == 4; + } + } + + private static final IsAcceptable IS_ACCEPTABLE = new IsAcceptable(); + private static final int DATA_FORMAT = 0x4e726d32; // "Nrm2" + + public NormalizerImpl load(ByteBuffer bytes) { + try { + dataVersion = ICUBinary.readHeaderAndDataVersion(bytes, DATA_FORMAT, IS_ACCEPTABLE); + int indexesLength = bytes.getInt() / 4; // inIndexes[IX_NORM_TRIE_OFFSET]/4 + if (indexesLength <= IX_MIN_LCCC_CP) { + throw new InternalError("Normalizer2 data: not enough indexes"); + } + int[] inIndexes = new int[indexesLength]; + inIndexes[0] = indexesLength * 4; + for (int i = 1; i < indexesLength; ++i) { + inIndexes[i] = bytes.getInt(); + } + + minDecompNoCP = inIndexes[IX_MIN_DECOMP_NO_CP]; + minCompNoMaybeCP = inIndexes[IX_MIN_COMP_NO_MAYBE_CP]; + minLcccCP = inIndexes[IX_MIN_LCCC_CP]; + + minYesNo = inIndexes[IX_MIN_YES_NO]; + minYesNoMappingsOnly = inIndexes[IX_MIN_YES_NO_MAPPINGS_ONLY]; + minNoNo = inIndexes[IX_MIN_NO_NO]; + minNoNoCompBoundaryBefore = inIndexes[IX_MIN_NO_NO_COMP_BOUNDARY_BEFORE]; + minNoNoCompNoMaybeCC = inIndexes[IX_MIN_NO_NO_COMP_NO_MAYBE_CC]; + minNoNoEmpty = inIndexes[IX_MIN_NO_NO_EMPTY]; + limitNoNo = inIndexes[IX_LIMIT_NO_NO]; + minMaybeYes = inIndexes[IX_MIN_MAYBE_YES]; + assert ((minMaybeYes & 7) == 0); // 8-aligned for noNoDelta bit fields + centerNoNoDelta = (minMaybeYes >> DELTA_SHIFT) - MAX_DELTA - 1; + + // Read the normTrie. + int offset = inIndexes[IX_NORM_TRIE_OFFSET]; + int nextOffset = inIndexes[IX_EXTRA_DATA_OFFSET]; + int triePosition = bytes.position(); + normTrie = CodePointTrie.Fast16.fromBinary(bytes); + int trieLength = bytes.position() - triePosition; + if (trieLength > (nextOffset - offset)) { + throw new InternalError("Normalizer2 data: not enough bytes for normTrie"); + } + ICUBinary.skipBytes(bytes, (nextOffset - offset) - trieLength); // skip padding after trie bytes + + // Read the composition and mapping data. + offset = nextOffset; + nextOffset = inIndexes[IX_SMALL_FCD_OFFSET]; + int numChars = (nextOffset - offset) / 2; + if (numChars != 0) { + maybeYesCompositions = ICUBinary.getString(bytes, numChars, 0); + extraData = maybeYesCompositions.substring((MIN_NORMAL_MAYBE_YES - minMaybeYes) >> OFFSET_SHIFT); + } + + // smallFCD: new in formatVersion 2 + offset = nextOffset; + smallFCD = new byte[0x100]; + bytes.get(smallFCD); + + return this; + } catch (IOException e) { + throw new InternalError(e); + } + } + + public NormalizerImpl load(String name) { + return load(ICUBinary.getRequiredData(name)); + } + + // The trie stores values for lead surrogate code *units*. + // Surrogate code *points* are inert. + public int getNorm16(int c) { + return UTF16Plus.isLeadSurrogate(c) ? INERT : normTrie.get(c); + } + + public int getRawNorm16(int c) { + return normTrie.get(c); + } + + public boolean isAlgorithmicNoNo(int norm16) { + return limitNoNo <= norm16 && norm16 < minMaybeYes; + } + + public boolean isCompNo(int norm16) { + return minNoNo <= norm16 && norm16 < minMaybeYes; + } + + public boolean isDecompYes(int norm16) { + return norm16 < minYesNo || minMaybeYes <= norm16; + } + + public int getCC(int norm16) { + if (norm16 >= MIN_NORMAL_MAYBE_YES) { + return getCCFromNormalYesOrMaybe(norm16); + } + if (norm16 < minNoNo || limitNoNo <= norm16) { + return 0; + } + return getCCFromNoNo(norm16); + } + + public static int getCCFromNormalYesOrMaybe(int norm16) { + return (norm16 >> OFFSET_SHIFT) & 0xff; + } + + public static int getCCFromYesOrMaybe(int norm16) { + return norm16 >= MIN_NORMAL_MAYBE_YES ? getCCFromNormalYesOrMaybe(norm16) : 0; + } + + public int getCCFromYesOrMaybeCP(int c) { + 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. + */ + public int getFCD16(int c) { + if (c < minDecompNoCP) { + return 0; + } else if (c <= 0xffff) { + if (!singleLeadMightHaveNonZeroFCD16(c)) { + return 0; + } + } + return getFCD16FromNormData(c); + } + + /** + * Returns true if the single-or-lead code unit c might have non-zero FCD data. + */ + public boolean singleLeadMightHaveNonZeroFCD16(int lead) { + // 0<=lead<=0xffff + byte bits = smallFCD[lead >> 8]; + if (bits == 0) { + return false; + } + return ((bits >> ((lead >> 5) & 7)) & 1) != 0; + } + + /** Gets the FCD value from the regular normalization data. */ + public int getFCD16FromNormData(int c) { + int 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) + int 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 + int mapping = norm16 >> OFFSET_SHIFT; + int firstUnit = extraData.charAt(mapping); + int fcd16 = firstUnit >> 8; // tccc + if ((firstUnit & MAPPING_HAS_CCC_LCCC_WORD) != 0) { + fcd16 |= extraData.charAt(mapping - 1) & 0xff00; // lccc + } + return fcd16; + } + + /** + * Gets the decomposition for one code point. + * + * @param c code point + * @return c's decomposition, if it has one; returns null if it does not have a + * decomposition + */ + public String getDecomposition(int c) { + int norm16; + if (c < minDecompNoCP || isMaybeOrNonZeroCC(norm16 = getNorm16(c))) { + // c does not decompose + return null; + } + int decomp = -1; + if (isDecompNoAlgorithmic(norm16)) { + // Maps to an isCompYesAndZeroCC. + decomp = c = mapAlgorithmic(c, norm16); + // The mapping might decompose further. + norm16 = getRawNorm16(c); + } + if (norm16 < minYesNo) { + if (decomp < 0) { + return null; + } else { + return UTF16.valueOf(decomp); + } + } else if (isHangulLV(norm16) || isHangulLVT(norm16)) { + // Hangul syllable: decompose algorithmically + StringBuilder buffer = new StringBuilder(); + Hangul.decompose(c, buffer); + return buffer.toString(); + } + // c decomposes, get everything from the variable-length extra data + int mapping = norm16 >> OFFSET_SHIFT; + int length = extraData.charAt(mapping++) & MAPPING_LENGTH_MASK; + return extraData.substring(mapping, mapping + length); + } + + // Fixed norm16 values. + public static final int MIN_YES_YES_WITH_CC = 0xfe02; + public static final int JAMO_VT = 0xfe00; + public static final int MIN_NORMAL_MAYBE_YES = 0xfc00; + public static final int JAMO_L = 2; // offset=1 hasCompBoundaryAfter=FALSE + public static final int INERT = 1; // offset=0 hasCompBoundaryAfter=TRUE + + // norm16 bit 0 is comp-boundary-after. + public static final int HAS_COMP_BOUNDARY_AFTER = 1; + public static final int OFFSET_SHIFT = 1; + + // For algorithmic one-way mappings, norm16 bits 2..1 indicate the + // tccc (0, 1, >1) for quick FCC boundary-after tests. + public static final int DELTA_TCCC_0 = 0; + public static final int DELTA_TCCC_1 = 2; + public static final int DELTA_TCCC_GT_1 = 4; + public static final int DELTA_TCCC_MASK = 6; + public static final int DELTA_SHIFT = 3; + + public static final int MAX_DELTA = 0x40; + + // Byte offsets from the start of the data, after the generic header. + public static final int IX_NORM_TRIE_OFFSET = 0; + public static final int IX_EXTRA_DATA_OFFSET = 1; + public static final int IX_SMALL_FCD_OFFSET = 2; + public static final int IX_RESERVED3_OFFSET = 3; + public static final int IX_TOTAL_SIZE = 7; + public static final int MIN_CCC_LCCC_CP = 0x300; + // Code point thresholds for quick check codes. + public static final int IX_MIN_DECOMP_NO_CP = 8; + public static final int IX_MIN_COMP_NO_MAYBE_CP = 9; + + // Norm16 value thresholds for quick check combinations and types of extra data. + + /** Mappings & compositions in [minYesNo..minYesNoMappingsOnly[. */ + public static final int IX_MIN_YES_NO = 10; + /** Mappings are comp-normalized. */ + public static final int IX_MIN_NO_NO = 11; + public static final int IX_LIMIT_NO_NO = 12; + public static final int IX_MIN_MAYBE_YES = 13; + + /** Mappings only in [minYesNoMappingsOnly..minNoNo[. */ + public static final int IX_MIN_YES_NO_MAPPINGS_ONLY = 14; + /** Mappings are not comp-normalized but have a comp boundary before. */ + public static final int IX_MIN_NO_NO_COMP_BOUNDARY_BEFORE = 15; + /** Mappings do not have a comp boundary before. */ + public static final int IX_MIN_NO_NO_COMP_NO_MAYBE_CC = 16; + /** Mappings to the empty string. */ + public static final int IX_MIN_NO_NO_EMPTY = 17; + + public static final int IX_MIN_LCCC_CP = 18; + public static final int IX_COUNT = 20; + + public static final int MAPPING_HAS_CCC_LCCC_WORD = 0x80; + public static final int MAPPING_HAS_RAW_MAPPING = 0x40; + // unused bit 0x20; + public static final int MAPPING_LENGTH_MASK = 0x1f; + + public static final int COMP_1_LAST_TUPLE = 0x8000; + public static final int COMP_1_TRIPLE = 1; + public static final int COMP_1_TRAIL_LIMIT = 0x3400; + public static final int COMP_1_TRAIL_MASK = 0x7ffe; + public static final int COMP_1_TRAIL_SHIFT = 9; // 10-1 for the "triple" bit + public static final int COMP_2_TRAIL_SHIFT = 6; + public static final int COMP_2_TRAIL_MASK = 0xffc0; + + // higher-level functionality ------------------------------------------ *** + + /** + * Decomposes s[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. + */ + public void decompose(CharSequence s, int src, int limit, StringBuilder dest, int destLengthEstimate) { + if (destLengthEstimate < 0) { + destLengthEstimate = limit - src; + } + dest.setLength(0); + ReorderingBuffer buffer = new ReorderingBuffer(this, dest, destLengthEstimate); + decompose(s, src, limit, buffer); + } + + // Dual functionality: + // buffer!=NULL: normalize + // buffer==NULL: isNormalized/quickCheck/spanQuickCheckYes + public int decompose(CharSequence s, int src, int limit, ReorderingBuffer buffer) { + int minNoCP = minDecompNoCP; + + int prevSrc; + int c = 0; + int norm16 = 0; + + // only for quick check + int prevBoundary = src; + int prevCC = 0; + + for (;;) { + // count code units below the minimum or with irrelevant data for the quick + // check + for (prevSrc = src; src != limit;) { + if ((c = s.charAt(src)) < minNoCP || isMostDecompYesAndZeroCC(norm16 = normTrie.bmpGet(c))) { + ++src; + } else if (!UTF16Plus.isLeadSurrogate(c)) { + break; + } else { + char c2; + if ((src + 1) != limit && Character.isLowSurrogate(c2 = s.charAt(src + 1))) { + c = Character.toCodePoint((char) c, c2); + norm16 = normTrie.suppGet(c); + if (isMostDecompYesAndZeroCC(norm16)) { + src += 2; + } else { + break; + } + } else { + ++src; // unpaired lead surrogate: inert + } + } + } + // copy these code units all at once + if (src != prevSrc) { + if (buffer != null) { + buffer.flushAndAppendZeroCC(s, prevSrc, src); + } else { + prevCC = 0; + prevBoundary = src; + } + } + if (src == limit) { + break; + } + + // Check one above-minimum, relevant code point. + src += Character.charCount(c); + if (buffer != null) { + decompose(c, norm16, buffer); + } else { + if (isDecompYes(norm16)) { + int 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; + } + + public void decomposeAndAppend(CharSequence s, boolean doDecompose, ReorderingBuffer buffer) { + int limit = s.length(); + if (limit == 0) { + return; + } + if (doDecompose) { + decompose(s, 0, limit, buffer); + return; + } + // Just merge the strings at the boundary. + int c = Character.codePointAt(s, 0); + int src = 0; + int firstCC, prevCC, cc; + firstCC = prevCC = cc = getCC(getNorm16(c)); + while (cc != 0) { + prevCC = cc; + src += Character.charCount(c); + if (src >= limit) { + break; + } + c = Character.codePointAt(s, src); + cc = getCC(getNorm16(c)); + } + ; + buffer.append(s, 0, src, false, firstCC, prevCC); + buffer.append(s, src, limit); + } + + // Very similar to composeQuickCheck(): Make the same changes in both places if + // relevant. + // doCompose: normalize + // !doCompose: isNormalized (buffer must be empty and initialized) + public boolean compose(CharSequence s, int src, int limit, boolean onlyContiguous, boolean doCompose, + ReorderingBuffer buffer) { + int prevBoundary = src; + int minNoMaybeCP = minCompNoMaybeCP; + + for (;;) { + // Fast path: Scan over a sequence of characters below the minimum "no or maybe" + // code point, + // or with (compYes && ccc==0) properties. + int prevSrc; + int c = 0; + int norm16 = 0; + for (;;) { + if (src == limit) { + if (prevBoundary != limit && doCompose) { + buffer.append(s, prevBoundary, limit); + } + return true; + } + if ((c = s.charAt(src)) < minNoMaybeCP || isCompYesAndZeroCC(norm16 = normTrie.bmpGet(c))) { + ++src; + } else { + prevSrc = src++; + if (!UTF16Plus.isLeadSurrogate(c)) { + break; + } else { + char c2; + if (src != limit && Character.isLowSurrogate(c2 = s.charAt(src))) { + ++src; + c = Character.toCodePoint((char) c, c2); + norm16 = normTrie.suppGet(c); + 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 (!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(s, src, limit)) { + if (prevBoundary != prevSrc) { + buffer.append(s, prevBoundary, prevSrc); + } + buffer.append(mapAlgorithmic(c, norm16), 0); + prevBoundary = src; + continue; + } + } else if (norm16 < minNoNoCompBoundaryBefore) { + // The mapping is comp-normalized which also implies hasCompBoundaryBefore. + if (norm16HasCompBoundaryAfter(norm16, onlyContiguous) || hasCompBoundaryBefore(s, src, limit)) { + if (prevBoundary != prevSrc) { + buffer.append(s, prevBoundary, prevSrc); + } + int mapping = norm16 >> OFFSET_SHIFT; + int length = extraData.charAt(mapping++) & MAPPING_LENGTH_MASK; + buffer.append(extraData, mapping, mapping + length); + 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(s, src, limit) + || hasCompBoundaryAfter(s, prevBoundary, prevSrc, onlyContiguous)) { + if (prevBoundary != prevSrc) { + buffer.append(s, prevBoundary, prevSrc); + } + 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) { + char prev = s.charAt(prevSrc - 1); + if (c < Hangul.JAMO_T_BASE) { + // The current character is a Jamo Vowel, + // compose with previous Jamo L and following Jamo T. + char l = (char) (prev - Hangul.JAMO_L_BASE); + if (l < Hangul.JAMO_L_COUNT) { + if (!doCompose) { + return false; + } + int t; + if (src != limit && 0 < (t = (s.charAt(src) - Hangul.JAMO_T_BASE)) && t < Hangul.JAMO_T_COUNT) { + // The next character is a Jamo T. + ++src; + } else if (hasCompBoundaryBefore(s, src, limit)) { + // No Jamo T follows, not even via decomposition. + t = 0; + } else { + t = -1; + } + if (t >= 0) { + int 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.append(s, prevBoundary, prevSrc); + } + buffer.append((char) syllable); + 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; + } + int syllable = prev + c - Hangul.JAMO_T_BASE; + --prevSrc; // Replace the Hangul LV as well. + if (prevBoundary != prevSrc) { + buffer.append(s, prevBoundary, prevSrc); + } + buffer.append((char) syllable); + 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. + int cc = getCCFromNormalYesOrMaybe(norm16); // cc!=0 + if (onlyContiguous /* FCC */ && getPreviousTrailCC(s, 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. + int n16; + for (;;) { + if (src == limit) { + if (doCompose) { + buffer.append(s, prevBoundary, limit); + } + return true; + } + int prevCC = cc; + c = Character.codePointAt(s, src); + n16 = normTrie.get(c); + if (n16 >= MIN_YES_YES_WITH_CC) { + cc = getCCFromNormalYesOrMaybe(n16); + if (prevCC > cc) { + if (!doCompose) { + return false; + } + break; + } + } else { + break; + } + src += Character.charCount(c); + } + // p 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 += Character.charCount(c); + } + 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)) { + c = Character.codePointBefore(s, prevSrc); + norm16 = normTrie.get(c); + if (!norm16HasCompBoundaryAfter(norm16, onlyContiguous)) { + prevSrc -= Character.charCount(c); + } + } + if (doCompose && prevBoundary != prevSrc) { + buffer.append(s, prevBoundary, prevSrc); + } + int recomposeStartIndex = buffer.length(); + // We know there is not a boundary here. + decomposeShort(s, prevSrc, src, false /* !stopAtCompBoundary */, onlyContiguous, buffer); + // Decompose until the next boundary. + src = decomposeShort(s, src, limit, true /* stopAtCompBoundary */, onlyContiguous, buffer); + recompose(buffer, recomposeStartIndex, onlyContiguous); + if (!doCompose) { + if (!buffer.equals(s, prevSrc, src)) { + return false; + } + buffer.remove(); + } + prevBoundary = src; + } + } + + /** + * Very similar to compose(): Make the same changes in both places if relevant. + * doSpan: spanQuickCheckYes (ignore bit 0 of the return value) !doSpan: + * quickCheck + * + * @return bits 31..1: spanQuickCheckYes (==s.length() if "yes") and bit 0: set + * if "maybe"; otherwise, if the span length<s.length() then the + * quick check result is "no" + */ + public int composeQuickCheck(CharSequence s, int src, int limit, boolean onlyContiguous, boolean doSpan) { + int qcResult = 0; + int prevBoundary = src; + int minNoMaybeCP = minCompNoMaybeCP; + + for (;;) { + // Fast path: Scan over a sequence of characters below the minimum "no or maybe" + // code point, + // or with (compYes && ccc==0) properties. + int prevSrc; + int c = 0; + int norm16 = 0; + for (;;) { + if (src == limit) { + return (src << 1) | qcResult; // "yes" or "maybe" + } + if ((c = s.charAt(src)) < minNoMaybeCP || isCompYesAndZeroCC(norm16 = normTrie.bmpGet(c))) { + ++src; + } else { + prevSrc = src++; + if (!UTF16Plus.isLeadSurrogate(c)) { + break; + } else { + char c2; + if (src != limit && Character.isLowSurrogate(c2 = s.charAt(src))) { + ++src; + c = Character.toCodePoint((char) c, c2); + norm16 = normTrie.suppGet(c); + 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. + + int prevNorm16 = INERT; + if (prevBoundary != prevSrc) { + prevBoundary = prevSrc; + if (!norm16HasCompBoundaryBefore(norm16)) { + c = Character.codePointBefore(s, prevSrc); + int n16 = getNorm16(c); + if (!norm16HasCompBoundaryAfter(n16, onlyContiguous)) { + prevBoundary -= Character.charCount(c); + prevNorm16 = n16; + } + } + } + + if (isMaybeOrNonZeroCC(norm16)) { + int 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. + for (;;) { + if (norm16 < MIN_YES_YES_WITH_CC) { + if (!doSpan) { + qcResult = 1; + } else { + return prevBoundary << 1; // spanYes does not care to know it's "maybe" + } + } + if (src == limit) { + return (src << 1) | qcResult; // "yes" or "maybe" + } + int prevCC = cc; + c = Character.codePointAt(s, src); + norm16 = getNorm16(c); + if (isMaybeOrNonZeroCC(norm16)) { + cc = getCCFromYesOrMaybe(norm16); + if (!(prevCC <= cc || cc == 0)) { + break; + } + } else { + break; + } + src += Character.charCount(c); + } + // src is after the last in-order combining mark. + if (isCompYesAndZeroCC(norm16)) { + prevBoundary = src; + src += Character.charCount(c); + continue; + } + } + } + return prevBoundary << 1; // "no" + } + } + + public void composeAndAppend(CharSequence s, boolean doCompose, boolean onlyContiguous, ReorderingBuffer buffer) { + int src = 0, limit = s.length(); + if (!buffer.isEmpty()) { + int firstStarterInSrc = findNextCompBoundary(s, 0, limit, onlyContiguous); + if (0 != firstStarterInSrc) { + int lastStarterInDest = findPreviousCompBoundary(buffer.getStringBuilder(), buffer.length(), + onlyContiguous); + StringBuilder middle = new StringBuilder( + (buffer.length() - lastStarterInDest) + firstStarterInSrc + 16); + middle.append(buffer.getStringBuilder(), lastStarterInDest, buffer.length()); + buffer.removeSuffix(buffer.length() - lastStarterInDest); + middle.append(s, 0, firstStarterInSrc); + compose(middle, 0, middle.length(), onlyContiguous, true, buffer); + src = firstStarterInSrc; + } + } + if (doCompose) { + compose(s, src, limit, onlyContiguous, true, buffer); + } else { + buffer.append(s, src, limit); + } + } + + // Dual functionality: + // buffer!=NULL: normalize + // buffer==NULL: isNormalized/quickCheck/spanQuickCheckYes + public int makeFCD(CharSequence s, int src, int limit, ReorderingBuffer buffer) { + // 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. + + // Tracks the last FCD-safe boundary, before lccc=0 or after properly-ordered + // tccc<=1. + // Similar to the prevBoundary in the compose() implementation. + int prevBoundary = src; + int prevSrc; + int c = 0; + int prevFCD16 = 0; + int fcd16 = 0; + + for (;;) { + // count code units with lccc==0 + for (prevSrc = src; src != limit;) { + if ((c = s.charAt(src)) < minLcccCP) { + prevFCD16 = ~c; + ++src; + } else if (!singleLeadMightHaveNonZeroFCD16(c)) { + prevFCD16 = 0; + ++src; + } else { + if (UTF16Plus.isLeadSurrogate(c)) { + char c2; + if ((src + 1) != limit && Character.isLowSurrogate(c2 = s.charAt(src + 1))) { + c = Character.toCodePoint((char) c, c2); + } + } + if ((fcd16 = getFCD16FromNormData(c)) <= 0xff) { + prevFCD16 = fcd16; + src += Character.charCount(c); + } else { + break; + } + } + } + // copy these code units all at once + if (src != prevSrc) { + if (src == limit) { + if (buffer != null) { + buffer.flushAndAppendZeroCC(s, prevSrc, src); + } + 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. + int prev = ~prevFCD16; + if (prev < minDecompNoCP) { + prevFCD16 = 0; + } else { + prevFCD16 = getFCD16FromNormData(prev); + if (prevFCD16 > 1) { + --prevBoundary; + } + } + } else { + int p = src - 1; + if (Character.isLowSurrogate(s.charAt(p)) && prevSrc < p + && Character.isHighSurrogate(s.charAt(p - 1))) { + --p; + // Need to fetch the previous character's FCD value because + // prevFCD16 was just for the trail surrogate code point. + prevFCD16 = getFCD16FromNormData(Character.toCodePoint(s.charAt(p), s.charAt(p + 1))); + // Still known to have lccc==0 because its lead surrogate unit had lccc==0. + } + if (prevFCD16 > 1) { + prevBoundary = p; + } + } + if (buffer != null) { + // The last lccc==0 character is excluded from the + // flush-and-append call in case it needs to be modified. + buffer.flushAndAppendZeroCC(s, prevSrc, prevBoundary); + buffer.append(s, prevBoundary, src); + } + // The start of the current character (c). + prevSrc = src; + } else if (src == limit) { + break; + } + + src += Character.charCount(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); + } + 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(prevSrc - prevBoundary); + /* + * Find the part of the source that needs to be decomposed, up to the next safe + * boundary. + */ + src = findNextFCDBoundary(s, src, limit); + /* + * The source text does not fulfill the conditions for FCD. Decompose and + * reorder a limited piece of the text. + */ + decomposeShort(s, prevBoundary, src, false, false, buffer); + prevBoundary = src; + prevFCD16 = 0; + } + } + return src; + } + + public boolean hasDecompBoundaryBefore(int c) { + return c < minLcccCP || (c <= 0xffff && !singleLeadMightHaveNonZeroFCD16(c)) + || norm16HasDecompBoundaryBefore(getNorm16(c)); + } + + public boolean norm16HasDecompBoundaryBefore(int norm16) { + 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 + int mapping = norm16 >> OFFSET_SHIFT; + int firstUnit = extraData.charAt(mapping); + // true if leadCC==0 (hasFCDBoundaryBefore()) + return (firstUnit & MAPPING_HAS_CCC_LCCC_WORD) == 0 || (extraData.charAt(mapping - 1) & 0xff00) == 0; + } + + public boolean hasDecompBoundaryAfter(int c) { + if (c < minDecompNoCP) { + return true; + } + if (c <= 0xffff && !singleLeadMightHaveNonZeroFCD16(c)) { + return true; + } + return norm16HasDecompBoundaryAfter(getNorm16(c)); + } + + public boolean norm16HasDecompBoundaryAfter(int norm16) { + 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 + int mapping = norm16 >> OFFSET_SHIFT; + int firstUnit = extraData.charAt(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 || (extraData.charAt(mapping - 1) & 0xff00) == 0; + } + + public boolean isDecompInert(int c) { + return isDecompYesAndZeroCC(getNorm16(c)); + } + + public boolean hasCompBoundaryBefore(int c) { + return c < minCompNoMaybeCP || norm16HasCompBoundaryBefore(getNorm16(c)); + } + + public boolean hasCompBoundaryAfter(int c, boolean onlyContiguous) { + return norm16HasCompBoundaryAfter(getNorm16(c), onlyContiguous); + } + + private boolean isMaybe(int norm16) { + return minMaybeYes <= norm16 && norm16 <= JAMO_VT; + } + + private boolean isMaybeOrNonZeroCC(int norm16) { + return norm16 >= minMaybeYes; + } + + private static boolean isInert(int norm16) { + return norm16 == INERT; + } + + private static boolean isJamoVT(int norm16) { + return norm16 == JAMO_VT; + } + + private int hangulLVT() { + return minYesNoMappingsOnly | HAS_COMP_BOUNDARY_AFTER; + } + + private boolean isHangulLV(int norm16) { + return norm16 == minYesNo; + } + + private boolean isHangulLVT(int norm16) { + return norm16 == hangulLVT(); + } + + private boolean isCompYesAndZeroCC(int norm16) { + return norm16 < minNoNo; + } + + // UBool isCompYes(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; + // } + private int getCCFromNoNo(int norm16) { + int mapping = norm16 >> OFFSET_SHIFT; + if ((extraData.charAt(mapping) & MAPPING_HAS_CCC_LCCC_WORD) != 0) { + return extraData.charAt(mapping - 1) & 0xff; + } else { + return 0; + } + } + + int getTrailCCFromCompYesAndZeroCC(int norm16) { + 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 extraData.charAt(norm16 >> OFFSET_SHIFT) >> 8; // tccc from yesNo + } + } + + // Requires algorithmic-NoNo. + private int mapAlgorithmic(int c, int norm16) { + return c + (norm16 >> DELTA_SHIFT) - centerNoNoDelta; + } + + // Requires minYesNo>OFFSET_SHIFT); + // } + + /** + * @return index into maybeYesCompositions, or -1 + */ + private int getCompositionsListForDecompYes(int norm16) { + if (norm16 < JAMO_L || MIN_NORMAL_MAYBE_YES <= norm16) { + return -1; + } else { + if ((norm16 -= minMaybeYes) < 0) { + // norm16> OFFSET_SHIFT; + } + } + + /** + * @return index into maybeYesCompositions + */ + private int getCompositionsListForComposite(int norm16) { + // A composite has both mapping & compositions list. + int list = ((MIN_NORMAL_MAYBE_YES - minMaybeYes) + norm16) >> OFFSET_SHIFT; + int firstUnit = maybeYesCompositions.charAt(list); + return list + // mapping in maybeYesCompositions + 1 + // +1 to skip the first unit with the mapping length + (firstUnit & MAPPING_LENGTH_MASK); // + mapping length + } + + // 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. + // Public in Java for collation implementation code. + private int decomposeShort(CharSequence s, int src, int limit, boolean stopAtCompBoundary, boolean onlyContiguous, + ReorderingBuffer buffer) { + while (src < limit) { + int c = Character.codePointAt(s, src); + if (stopAtCompBoundary && c < minCompNoMaybeCP) { + return src; + } + int norm16 = getNorm16(c); + if (stopAtCompBoundary && norm16HasCompBoundaryBefore(norm16)) { + return src; + } + src += Character.charCount(c); + decompose(c, norm16, buffer); + if (stopAtCompBoundary && norm16HasCompBoundaryAfter(norm16, onlyContiguous)) { + return src; + } + } + return src; + } + + private void decompose(int c, int norm16, ReorderingBuffer buffer) { + // get the decomposition and the lead and trail cc's + if (norm16 >= limitNoNo) { + if (isMaybeOrNonZeroCC(norm16)) { + buffer.append(c, getCCFromYesOrMaybe(norm16)); + return; + } + // Maps to an isCompYesAndZeroCC. + c = mapAlgorithmic(c, norm16); + norm16 = getRawNorm16(c); + } + if (norm16 < minYesNo) { + // c does not decompose + buffer.append(c, 0); + } else if (isHangulLV(norm16) || isHangulLVT(norm16)) { + // Hangul syllable: decompose algorithmically + Hangul.decompose(c, buffer); + } else { + // c decomposes, get everything from the variable-length extra data + int mapping = norm16 >> OFFSET_SHIFT; + int firstUnit = extraData.charAt(mapping); + int length = firstUnit & MAPPING_LENGTH_MASK; + int leadCC, trailCC; + trailCC = firstUnit >> 8; + if ((firstUnit & MAPPING_HAS_CCC_LCCC_WORD) != 0) { + leadCC = extraData.charAt(mapping - 1) >> 8; + } else { + leadCC = 0; + } + ++mapping; // skip over the firstUnit + buffer.append(extraData, mapping, mapping + length, true, leadCC, trailCC); + } + } + + /** + * 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. + */ + private static int combine(String compositions, int list, int trail) { + int key1, firstUnit; + if (trail < COMP_1_TRAIL_LIMIT) { + // trail character is 0..33FF + // result entry may have 2 or 3 units + key1 = (trail << 1); + while (key1 > (firstUnit = compositions.charAt(list))) { + list += 2 + (firstUnit & COMP_1_TRIPLE); + } + if (key1 == (firstUnit & COMP_1_TRAIL_MASK)) { + if ((firstUnit & COMP_1_TRIPLE) != 0) { + return (compositions.charAt(list + 1) << 16) | compositions.charAt(list + 2); + } else { + return compositions.charAt(list + 1); + } + } + } else { + // trail character is 3400..10FFFF + // result entry has 3 units + key1 = COMP_1_TRAIL_LIMIT + (((trail >> COMP_1_TRAIL_SHIFT)) & ~COMP_1_TRIPLE); + int key2 = (trail << COMP_2_TRAIL_SHIFT) & 0xffff; + int secondUnit; + for (;;) { + if (key1 > (firstUnit = compositions.charAt(list))) { + list += 2 + (firstUnit & COMP_1_TRIPLE); + } else if (key1 == (firstUnit & COMP_1_TRAIL_MASK)) { + if (key2 > (secondUnit = compositions.charAt(list + 1))) { + if ((firstUnit & COMP_1_LAST_TUPLE) != 0) { + break; + } else { + list += 3; + } + } else if (key2 == (secondUnit & COMP_2_TRAIL_MASK)) { + return ((secondUnit & ~COMP_2_TRAIL_MASK) << 16) | compositions.charAt(list + 2); + } else { + break; + } + } else { + break; + } + } + } + return -1; + } + + /* + * 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. + */ + private void recompose(ReorderingBuffer buffer, int recomposeStartIndex, boolean onlyContiguous) { + StringBuilder sb = buffer.getStringBuilder(); + int p = recomposeStartIndex; + if (p == sb.length()) { + return; + } + + int starter, pRemove; + int compositionsList; + int c, compositeAndFwd; + int norm16; + int cc, prevCC; + boolean 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 = -1; // used as indicator for whether we have a forward-combining starter + starter = -1; + starterIsSupplementary = false; + prevCC = 0; + + for (;;) { + c = sb.codePointAt(p); + p += Character.charCount(c); + norm16 = getNorm16(c); + cc = getCCFromYesOrMaybe(norm16); + if ( // this character combines backward and + isMaybe(norm16) && + // we have seen a starter that combines forward and + compositionsList >= 0 && + // the backward-combining character is not blocked + (prevCC < cc || prevCC == 0)) { + if (isJamoVT(norm16)) { + // c is a Jamo V/T, see if we can compose it with the previous character. + if (c < Hangul.JAMO_T_BASE) { + // c is a Jamo Vowel, compose with previous Jamo L and following Jamo T. + char prev = (char) (sb.charAt(starter) - Hangul.JAMO_L_BASE); + if (prev < Hangul.JAMO_L_COUNT) { + pRemove = p - 1; + char syllable = (char) (Hangul.HANGUL_BASE + + (prev * Hangul.JAMO_V_COUNT + (c - Hangul.JAMO_V_BASE)) * Hangul.JAMO_T_COUNT); + char t; + if (p != sb.length() + && (t = (char) (sb.charAt(p) - Hangul.JAMO_T_BASE)) < Hangul.JAMO_T_COUNT) { + ++p; + syllable += t; // The next character was a Jamo T. + } + sb.setCharAt(starter, syllable); + // remove the Jamo V/T + sb.delete(pRemove, p); + p = pRemove; + } + } + /* + * No "else" for Jamo T: Since the input is in NFD, there are no Hangul LV + * syllables that a Jamo T could combine with. All Jamo Ts are combined above + * when handling Jamo Vs. + */ + if (p == sb.length()) { + break; + } + compositionsList = -1; + continue; + } else if ((compositeAndFwd = combine(maybeYesCompositions, compositionsList, c)) >= 0) { + // The starter and the combining mark (c) do combine. + int composite = compositeAndFwd >> 1; + + // Remove the combining mark. + pRemove = p - Character.charCount(c); // pRemove & p: start & limit of the combining mark + sb.delete(pRemove, p); + p = pRemove; + // Replace the starter with the composite. + if (starterIsSupplementary) { + if (composite > 0xffff) { + // both are supplementary + sb.setCharAt(starter, UTF16.getLeadSurrogate(composite)); + sb.setCharAt(starter + 1, UTF16.getTrailSurrogate(composite)); + } else { + sb.setCharAt(starter, (char) c); + sb.deleteCharAt(starter + 1); + // The composite is shorter than the starter, + // move the intermediate characters forward one. + starterIsSupplementary = false; + --p; + } + } else if (composite > 0xffff) { + // The composite is longer than the starter, + // move the intermediate characters back one. + starterIsSupplementary = true; + sb.setCharAt(starter, UTF16.getLeadSurrogate(composite)); + sb.insert(starter + 1, UTF16.getTrailSurrogate(composite)); + ++p; + } else { + // both are on the BMP + sb.setCharAt(starter, (char) composite); + } + + // Keep prevCC because we removed the combining mark. + + if (p == sb.length()) { + break; + } + // Is the composite a starter that combines forward? + if ((compositeAndFwd & 1) != 0) { + compositionsList = getCompositionsListForComposite(getRawNorm16(composite)); + } else { + compositionsList = -1; + } + + // We combined; continue with looking for compositions. + continue; + } + } + + // no combination this time + prevCC = cc; + if (p == sb.length()) { + break; + } + + // If c did not combine, then check if it is a starter. + if (cc == 0) { + // Found a new starter. + if ((compositionsList = getCompositionsListForDecompYes(norm16)) >= 0) { + // It may combine with something, prepare for it. + if (c <= 0xffff) { + starterIsSupplementary = false; + starter = p - 1; + } else { + starterIsSupplementary = true; + starter = p - 2; + } + } + } else if (onlyContiguous) { + // FCC: no discontiguous compositions; any intervening character blocks. + compositionsList = -1; + } + } + buffer.flush(); + } + + /** + * Does c have a composition boundary before it? True if its decomposition + * begins with a character that has ccc=0 && NFC_QC=Yes (isCompYesAndZeroCC()). + * As a shortcut, this is true if c itself has ccc=0 && NFC_QC=Yes + * (isCompYesAndZeroCC()) so we need not decompose. + */ + private boolean hasCompBoundaryBefore(int c, int norm16) { + return c < minCompNoMaybeCP || norm16HasCompBoundaryBefore(norm16); + } + + private boolean norm16HasCompBoundaryBefore(int norm16) { + return norm16 < minNoNoCompNoMaybeCC || isAlgorithmicNoNo(norm16); + } + + private boolean hasCompBoundaryBefore(CharSequence s, int src, int limit) { + return src == limit || hasCompBoundaryBefore(Character.codePointAt(s, src)); + } + + private boolean norm16HasCompBoundaryAfter(int norm16, boolean onlyContiguous) { + return (norm16 & HAS_COMP_BOUNDARY_AFTER) != 0 && (!onlyContiguous || isTrailCC01ForCompBoundaryAfter(norm16)); + } + + private boolean hasCompBoundaryAfter(CharSequence s, int start, int p, boolean onlyContiguous) { + return start == p || hasCompBoundaryAfter(Character.codePointBefore(s, p), onlyContiguous); + } + + /** For FCC: Given norm16 HAS_COMP_BOUNDARY_AFTER, does it have tccc<=1? */ + private boolean isTrailCC01ForCompBoundaryAfter(int norm16) { + return isInert(norm16) || (isDecompNoAlgorithmic(norm16) ? (norm16 & DELTA_TCCC_MASK) <= DELTA_TCCC_1 + : extraData.charAt(norm16 >> OFFSET_SHIFT) <= 0x1ff); + } + + private int findPreviousCompBoundary(CharSequence s, int p, boolean onlyContiguous) { + while (p > 0) { + int c = Character.codePointBefore(s, p); + int norm16 = getNorm16(c); + if (norm16HasCompBoundaryAfter(norm16, onlyContiguous)) { + break; + } + p -= Character.charCount(c); + if (hasCompBoundaryBefore(c, norm16)) { + break; + } + } + return p; + } + + private int findNextCompBoundary(CharSequence s, int p, int limit, boolean onlyContiguous) { + while (p < limit) { + int c = Character.codePointAt(s, p); + int norm16 = normTrie.get(c); + if (hasCompBoundaryBefore(c, norm16)) { + break; + } + p += Character.charCount(c); + if (norm16HasCompBoundaryAfter(norm16, onlyContiguous)) { + break; + } + } + return p; + } + + private int findNextFCDBoundary(CharSequence s, int p, int limit) { + while (p < limit) { + int c = Character.codePointAt(s, p); + int norm16; + if (c < minLcccCP || norm16HasDecompBoundaryBefore(norm16 = getNorm16(c))) { + break; + } + p += Character.charCount(c); + if (norm16HasDecompBoundaryAfter(norm16)) { + break; + } + } + return p; + } + + /** + * Get the canonical decomposition sherman for ComposedCharIter + */ + public static int getDecompose(int chars[], String decomps[]) { + Normalizer2 impl = Normalizer2.getNFDInstance(); + + int length = 0; + int norm16 = 0; + int ch = -1; + int i = 0; + + while (++ch < 0x2fa1e) { // no cannoical above 0x3ffff + // TBD !!!! the hack code heres save us about 50ms for startup + // need a better solution/lookup + if (ch == 0x30ff) + ch = 0xf900; + else if (ch == 0x115bc) + ch = 0x1d15e; + else if (ch == 0x1d1c1) + ch = 0x2f800; + + String s = impl.getDecomposition(ch); + + if (s != null && i < chars.length) { + chars[i] = ch; + decomps[i++] = s; + } + } + return i; + } + + // ------------------------------------------------------ + // special method for Collation (RBTableBuilder.build()) + // ------------------------------------------------------ + private static boolean needSingleQuotation(char c) { + return (c >= 0x0009 && c <= 0x000D) || (c >= 0x0020 && c <= 0x002F) || (c >= 0x003A && c <= 0x0040) + || (c >= 0x005B && c <= 0x0060) || (c >= 0x007B && c <= 0x007E); + } + + public static String canonicalDecomposeWithSingleQuotation(String string) { + Normalizer2 impl = Normalizer2.getNFDInstance(); + char[] src = string.toCharArray(); + int srcIndex = 0; + int srcLimit = src.length; + char[] dest = new char[src.length * 3]; // MAX_BUF_SIZE_DECOMPOSE = 3 + int destIndex = 0; + int destLimit = dest.length; + + int prevSrc; + String norm; + int reorderStartIndex, length; + char c1, c2; + int cp; + int minNoMaybe = 0x00c0; + int cc, prevCC, trailCC; + char[] p; + int pStart; + + // initialize + reorderStartIndex = 0; + prevCC = 0; + norm = null; + cp = 0; + pStart = 0; + + cc = trailCC = -1; // initialize to bogus value + c1 = 0; + for (;;) { + prevSrc = srcIndex; + // quick check (1)less than minNoMaybe (2)no decomp (3)hangual + while (srcIndex != srcLimit && ((c1 = src[srcIndex]) < minNoMaybe + || (norm = impl.getDecomposition(cp = string.codePointAt(srcIndex))) == null + || (c1 >= '\uac00' && c1 <= '\ud7a3'))) { // Hangul Syllables + prevCC = 0; + srcIndex += (cp < 0x10000) ? 1 : 2; + } + + // copy these code units all at once + if (srcIndex != prevSrc) { + length = srcIndex - prevSrc; + if ((destIndex + length) <= destLimit) { + System.arraycopy(src, prevSrc, dest, destIndex, length); + } + + destIndex += length; + reorderStartIndex = destIndex; + } + + // end of source reached? + if (srcIndex == srcLimit) { + break; + } + + // cp already contains *src and norm32 is set for it, increment src + srcIndex += (cp < 0x10000) ? 1 : 2; + + if (cp < Character.MIN_SUPPLEMENTARY_CODE_POINT) { + c2 = 0; + length = 1; + + if (Character.isHighSurrogate(c1) || Character.isLowSurrogate(c1)) { + norm = null; + } + } else { + length = 2; + c2 = src[srcIndex - 1]; + } + + // get the decomposition and the lead and trail cc's + if (norm == null) { + // cp does not decompose + cc = trailCC = UCharacter.getCombiningClass(cp); + p = null; + pStart = -1; + } else { + + pStart = 0; + p = norm.toCharArray(); + length = p.length; + int cpNum = norm.codePointCount(0, length); + cc = UCharacter.getCombiningClass(norm.codePointAt(0)); + trailCC = UCharacter.getCombiningClass(norm.codePointAt(cpNum - 1)); + if (length == 1) { + // fastpath a single code unit from decomposition + c1 = p[pStart]; + c2 = 0; + p = null; + pStart = -1; + } + } + + if ((destIndex + length * 3) >= destLimit) { // 2 SingleQuotations + // buffer overflow + char[] tmpBuf = new char[destLimit * 2]; + System.arraycopy(dest, 0, tmpBuf, 0, destIndex); + dest = tmpBuf; + destLimit = dest.length; + } + + // append the decomposition to the destination buffer, assume length>0 + { + int reorderSplit = destIndex; + if (p == null) { + // fastpath: single code point + if (needSingleQuotation(c1)) { + // if we need single quotation, no need to consider "prevCC" + // and it must NOT be a supplementary pair + dest[destIndex++] = '\''; + dest[destIndex++] = c1; + dest[destIndex++] = '\''; + trailCC = 0; + } else if (cc != 0 && cc < prevCC) { + // (c1, c2) is out of order with respect to the preceding + // text + destIndex += length; + trailCC = insertOrdered(dest, reorderStartIndex, reorderSplit, destIndex, c1, c2, cc); + } else { + // just append (c1, c2) + dest[destIndex++] = c1; + if (c2 != 0) { + dest[destIndex++] = c2; + } + } + } else { + // general: multiple code points (ordered by themselves) + // from decomposition + if (needSingleQuotation(p[pStart])) { + dest[destIndex++] = '\''; + dest[destIndex++] = p[pStart++]; + dest[destIndex++] = '\''; + length--; + do { + dest[destIndex++] = p[pStart++]; + } while (--length > 0); + } else if (cc != 0 && cc < prevCC) { + destIndex += length; + trailCC = mergeOrdered(dest, reorderStartIndex, reorderSplit, p, pStart, pStart + length); + } else { + // just append the decomposition + do { + dest[destIndex++] = p[pStart++]; + } while (--length > 0); + } + } + } + prevCC = trailCC; + if (prevCC == 0) { + reorderStartIndex = destIndex; + } + } + + return new String(dest, 0, destIndex); + } + + /** + * simpler, single-character version of mergeOrdered() - bubble-insert one + * single code point into the preceding string which is already canonically + * ordered (c, c2) may or may not yet have been inserted at src[current]..src[p] + * + * it must be p=current+lengthof(c, c2) i.e. p=current+(c2==0 ? 1 : 2) + * + * before: src[start]..src[current] is already ordered, and src[current]..src[p] + * may or may not hold (c, c2) but must be exactly the same length as (c, c2) + * after: src[start]..src[p] is ordered + * + * @return the trailing combining class + */ + private static int/* unsigned byte */ insertOrdered(char[] source, int start, int current, int p, char c1, char c2, + int/* unsigned byte */ cc) { + int back, preBack; + int r; + int prevCC, trailCC = cc; + + if (start < current && cc != 0) { + // search for the insertion point where cc>=prevCC + preBack = back = current; + + PrevArgs prevArgs = new PrevArgs(); + prevArgs.current = current; + prevArgs.start = start; + prevArgs.src = source; + prevArgs.c1 = c1; + prevArgs.c2 = c2; + + // get the prevCC + prevCC = getPrevCC(prevArgs); + preBack = prevArgs.current; + + if (cc < prevCC) { + // this will be the last code point, so keep its cc + trailCC = prevCC; + back = preBack; + while (start < preBack) { + prevCC = getPrevCC(prevArgs); + preBack = prevArgs.current; + if (cc >= prevCC) { + break; + } + back = preBack; + } + + // this is where we are right now with all these indicies: + // [start]..[pPreBack] 0..? code points that we can ignore + // [pPreBack]..[pBack] 0..1 code points with prevCC<=cc + // [pBack]..[current] 0..n code points with >cc, move up to insert (c, c2) + // [current]..[p] 1 code point (c, c2) with cc + + // move the code units in between up + r = p; + do { + source[--r] = source[--current]; + } while (back != current); + } + } + + // insert (c1, c2) + source[current] = c1; + if (c2 != 0) { + source[(current + 1)] = c2; + } + + // we know the cc of the last code point + return trailCC; + } + + /** + * merge two UTF-16 string parts together to canonically order (order by + * combining classes) their concatenation + * + * the two strings may already be adjacent, so that the merging is done in-place + * if the two strings are not adjacent, then the buffer holding the first one + * must be large enough the second string may or may not be ordered in itself + * + * before: [start]..[current] is already ordered, and [next]..[limit] may be + * ordered in itself, but is not in relation to [start..current[ after: + * [start..current+(limit-next)[ is ordered + * + * the algorithm is a simple bubble-sort that takes the characters from + * src[next++] and inserts them in correct combining class order into the + * preceding part of the string + * + * since this function is called much less often than the single-code point + * insertOrdered(), it just uses that for easier maintenance + * + * @return the trailing combining class + */ + private static int /* unsigned byte */ mergeOrdered(char[] source, int start, int current, char[] data, int next, + int limit) { + int r; + int /* unsigned byte */ cc, trailCC = 0; + boolean adjacent; + + adjacent = current == next; + NextCCArgs ncArgs = new NextCCArgs(); + ncArgs.source = data; + ncArgs.next = next; + ncArgs.limit = limit; + + if (start != current) { + + while (ncArgs.next < ncArgs.limit) { + cc = getNextCC(ncArgs); + if (cc == 0) { + // does not bubble back + trailCC = 0; + if (adjacent) { + current = ncArgs.next; + } else { + data[current++] = ncArgs.c1; + if (ncArgs.c2 != 0) { + data[current++] = ncArgs.c2; + } + } + break; + } else { + r = current + (ncArgs.c2 == 0 ? 1 : 2); + trailCC = insertOrdered(source, start, current, r, ncArgs.c1, ncArgs.c2, cc); + current = r; + } + } + } + + if (ncArgs.next == ncArgs.limit) { + // we know the cc of the last code point + return trailCC; + } else { + if (!adjacent) { + // copy the second string part + do { + source[current++] = data[ncArgs.next++]; + } while (ncArgs.next != ncArgs.limit); + ncArgs.limit = current; + } + PrevArgs prevArgs = new PrevArgs(); + prevArgs.src = data; + prevArgs.start = start; + prevArgs.current = ncArgs.limit; + return getPrevCC(prevArgs); + } + + } + + private static final class PrevArgs { + char[] src; + int start; + int current; + char c1; + char c2; + } + + private static final class NextCCArgs { + char[] source; + int next; + int limit; + char c1; + char c2; + } + + private static int /* unsigned byte */ getNextCC(NextCCArgs args) { + args.c1 = args.source[args.next++]; + args.c2 = 0; + + if (UTF16.isTrailSurrogate(args.c1)) { + /* unpaired second surrogate */ + return 0; + } else if (!UTF16.isLeadSurrogate(args.c1)) { + return UCharacter.getCombiningClass(args.c1); + } else if (args.next != args.limit && UTF16.isTrailSurrogate(args.c2 = args.source[args.next])) { + ++args.next; + return UCharacter.getCombiningClass(Character.toCodePoint(args.c1, args.c2)); + } else { + /* unpaired first surrogate */ + args.c2 = 0; + return 0; + } + } + + private static int /* unsigned */ getPrevCC(PrevArgs args) { + args.c1 = args.src[--args.current]; + args.c2 = 0; + + if (args.c1 < MIN_CCC_LCCC_CP) { + return 0; + } else if (UTF16.isLeadSurrogate(args.c1)) { + /* unpaired first surrogate */ + return 0; + } else if (!UTF16.isTrailSurrogate(args.c1)) { + return UCharacter.getCombiningClass(args.c1); + } else if (args.current != args.start && UTF16.isLeadSurrogate(args.c2 = args.src[args.current - 1])) { + --args.current; + return UCharacter.getCombiningClass(Character.toCodePoint(args.c2, args.c1)); + } else { + /* unpaired second surrogate */ + args.c2 = 0; + return 0; + } + } + + private int getPreviousTrailCC(CharSequence s, int start, int p) { + if (start == p) { + return 0; + } + return getFCD16(Character.codePointBefore(s, p)); + } + + private VersionInfo dataVersion; + + // BMP code point thresholds for quick check loops looking at single UTF-16 code + // units. + private int minDecompNoCP; + private int minCompNoMaybeCP; + private int minLcccCP; + + // Norm16 value thresholds for quick check combinations and types of extra data. + private int minYesNo; + private int minYesNoMappingsOnly; + private int minNoNo; + private int minNoNoCompBoundaryBefore; + private int minNoNoCompNoMaybeCC; + private int minNoNoEmpty; + private int limitNoNo; + private int centerNoNoDelta; + private int minMaybeYes; + + private CodePointTrie.Fast16 normTrie; + private String maybeYesCompositions; + private String extraData; // mappings and/or compositions for yesYes, yesNo & noNo characters + private byte[] smallFCD; // [0x100] one bit per 32 BMP code points, set if any FCD!=0 +} diff --git a/src/main/java/jdk_internal/icu/impl/Punycode.java b/src/main/java/jdk_internal/icu/impl/Punycode.java index 5fe701a4..7e4794ae 100755 --- a/src/main/java/jdk_internal/icu/impl/Punycode.java +++ b/src/main/java/jdk_internal/icu/impl/Punycode.java @@ -1,500 +1,500 @@ -/* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -/* - ******************************************************************************* - * Copyright (C) 2003-2004, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - */ -// -// CHANGELOG -// 2005-05-19 Edward Wang -// - copy this file from icu4jsrc_3_2/src/com/ibm/icu/text/Punycode.java -// - move from package com.ibm.icu.text to package sun.net.idn -// - use ParseException instead of StringPrepParseException -// 2007-08-14 Martin Buchholz -// - remove redundant casts -// -package jdk_internal.icu.impl; - -import java.text.ParseException; - -import jdk_internal.icu.lang.UCharacter; -import jdk_internal.icu.text.UTF16; - -/** - * Ported code from ICU punycode.c - * - * @author ram - */ - -/* Package Private class */ -public final class Punycode { - - /* Punycode parameters for Bootstring */ - private static final int BASE = 36; - private static final int TMIN = 1; - private static final int TMAX = 26; - private static final int SKEW = 38; - private static final int DAMP = 700; - private static final int INITIAL_BIAS = 72; - private static final int INITIAL_N = 0x80; - - /* "Basic" Unicode/ASCII code points */ - private static final int HYPHEN = 0x2d; - private static final int DELIMITER = HYPHEN; - - private static final int ZERO = 0x30; - private static final int NINE = 0x39; - - private static final int SMALL_A = 0x61; - private static final int SMALL_Z = 0x7a; - - private static final int CAPITAL_A = 0x41; - private static final int CAPITAL_Z = 0x5a; - - // TODO: eliminate the 256 limitation - private static final int MAX_CP_COUNT = 256; - - private static final int UINT_MAGIC = 0x80000000; - private static final long ULONG_MAGIC = 0x8000000000000000L; - - private static int adaptBias(int delta, int length, boolean firstTime) { - if (firstTime) { - delta /= DAMP; - } else { - delta /= 2; - } - delta += delta / length; - - int count = 0; - for (; delta > ((BASE - TMIN) * TMAX) / 2; count += BASE) { - delta /= (BASE - TMIN); - } - - return count + (((BASE - TMIN + 1) * delta) / (delta + SKEW)); - } - - /** - * basicToDigit[] contains the numeric value of a basic code point (for use in - * representing integers) in the range 0 to BASE-1, or -1 if b is does not - * represent a value. - */ - static final int[] basicToDigit = new int[] { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, - -1, -1, -1, -1, -1, - - -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, -1, - -1, -1, -1, - - -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, -1, - -1, -1, -1, - - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, - - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, - - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, - - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1 }; - - private static char asciiCaseMap(char b, boolean 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; - } - - /** - * 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. - */ - private static char digitToBasic(int digit, boolean 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); - } - } - - /** - * 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. - * - * @param src - * @param caseFlags - * @return - * @throws ParseException - */ - public static StringBuffer encode(StringBuffer src, boolean[] caseFlags) throws ParseException { - - int[] cpBuffer = new int[MAX_CP_COUNT]; - int n, delta, handledCPCount, basicLength, destLength, bias, j, m, q, k, t, srcCPCount; - char c, c2; - int srcLength = src.length(); - int destCapacity = MAX_CP_COUNT; - char[] dest = new char[destCapacity]; - StringBuffer result = new StringBuffer(); - /* - * Handle the basic code points and convert extended ones to UTF-32 in cpBuffer - * (caseFlag in sign bit): - */ - srcCPCount = destLength = 0; - - for (j = 0; j < srcLength; ++j) { - if (srcCPCount == MAX_CP_COUNT) { - /* too many input code points */ - throw new ParseException("Too many input code points", -1); - } - c = src.charAt(j); - if (isBasic(c)) { - if (destLength < destCapacity) { - cpBuffer[srcCPCount++] = 0; - dest[destLength] = caseFlags != null ? asciiCaseMap(c, caseFlags[j]) : c; - } - ++destLength; - } else { - n = ((caseFlags != null && caseFlags[j]) ? 1 : 0) << 31L; - if (!UTF16.isSurrogate(c)) { - n |= c; - } else if (UTF16.isLeadSurrogate(c) && (j + 1) < srcLength - && UTF16.isTrailSurrogate(c2 = src.charAt(j + 1))) { - ++j; - - n |= UCharacter.getCodePoint(c, c2); - } else { - /* error: unmatched surrogate */ - throw new ParseException("Illegal char found", -1); - } - cpBuffer[srcCPCount++] = n; - } - } - - /* Finish the basic string - if it is not empty - with a delimiter. */ - basicLength = destLength; - if (basicLength > 0) { - if (destLength < destCapacity) { - dest[destLength] = DELIMITER; - } - ++destLength; - } - - /* - * handledCPCount is the number of code points that have been handled - * basicLength is the number of basic code points destLength is the number of - * chars that have been output - */ - - /* Initialize the state: */ - n = INITIAL_N; - delta = 0; - bias = INITIAL_BIAS; - - /* Main encoding loop: */ - for (handledCPCount = basicLength; handledCPCount < srcCPCount; /* no op */) { - /* - * All non-basic code points < n have been handled already. Find the next larger - * one: - */ - for (m = 0x7fffffff, j = 0; j < srcCPCount; ++j) { - q = cpBuffer[j] & 0x7fffffff; /* remove case flag from the sign bit */ - if (n <= q && q < m) { - m = q; - } - } - - /* - * Increase delta enough to advance the decoder's state to , but - * guard against overflow: - */ - if (m - n > (0x7fffffff - MAX_CP_COUNT - delta) / (handledCPCount + 1)) { - throw new RuntimeException("Internal program error"); - } - delta += (m - n) * (handledCPCount + 1); - n = m; - - /* Encode a sequence of same code points n */ - for (j = 0; j < srcCPCount; ++j) { - q = cpBuffer[j] & 0x7fffffff; /* remove case flag from the sign bit */ - if (q < n) { - ++delta; - } else if (q == n) { - /* Represent delta as a generalized variable-length integer: */ - for (q = delta, k = BASE; /* no condition */; k += BASE) { - - /** - * 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 < TMIN) { - t = TMIN; - } else if (k >= (bias + TMAX)) { - t = TMAX; - } - - if (q < t) { - break; - } - - if (destLength < destCapacity) { - dest[destLength++] = digitToBasic(t + (q - t) % (BASE - t), false); - } - q = (q - t) / (BASE - t); - } - - if (destLength < destCapacity) { - dest[destLength++] = digitToBasic(q, (cpBuffer[j] < 0)); - } - bias = adaptBias(delta, handledCPCount + 1, (handledCPCount == basicLength)); - delta = 0; - ++handledCPCount; - } - } - - ++delta; - ++n; - } - - return result.append(dest, 0, destLength); - } - - private static boolean isBasic(int ch) { - return (ch < INITIAL_N); - } - - private static boolean isBasicUpperCase(int ch) { - return (CAPITAL_A <= ch && ch <= CAPITAL_Z); - } - - private static boolean isSurrogate(int ch) { - return (((ch) & 0xfffff800) == 0xd800); - } - - /** - * Converts Punycode to Unicode. The Unicode string will be at most as long as - * the Punycode string. - * - * @param src - * @param caseFlags - * @return - * @throws ParseException - */ - public static StringBuffer decode(StringBuffer src, boolean[] caseFlags) throws ParseException { - int srcLength = src.length(); - StringBuffer result = new StringBuffer(); - int n, destLength, i, bias, basicLength, j, in, oldi, w, k, digit, t, destCPCount, firstSupplementaryIndex, - cpLength; - char b; - int destCapacity = MAX_CP_COUNT; - char[] dest = new char[destCapacity]; - - /* - * 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.charAt(--j) == DELIMITER) { - break; - } - } - destLength = basicLength = destCPCount = j; - - while (j > 0) { - b = src.charAt(--j); - if (!isBasic(b)) { - throw new ParseException("Illegal char found", -1); - } - - if (j < destCapacity) { - dest[j] = b; - - if (caseFlags != null) { - caseFlags[j] = isBasicUpperCase(b); - } - } - } - - /* Initialize the state: */ - n = INITIAL_N; - i = 0; - bias = INITIAL_BIAS; - firstSupplementaryIndex = 1000000000; - - /* - * Main decoding loop: Start just after the last delimiter if any basic code - * points were copied; start at the beginning otherwise. - */ - for (in = basicLength > 0 ? basicLength + 1 : 0; in < srcLength; /* no op */) { - /* - * in is the index of the next character to be consumed, and destCPCount is the - * number of code points in the output array. - * - * Decode a generalized variable-length integer into delta, which gets added to - * i. The overflow checking is easier if we increase i as we go, then subtract - * off its starting value at the end to obtain delta. - */ - for (oldi = i, w = 1, k = BASE; /* no condition */; k += BASE) { - if (in >= srcLength) { - throw new ParseException("Illegal char found", -1); - } - - digit = basicToDigit[(byte) src.charAt(in++)]; - if (digit < 0) { - throw new ParseException("Invalid char found", -1); - } - if (digit > (0x7fffffff - i) / w) { - /* integer overflow */ - throw new ParseException("Illegal char found", -1); - } - - i += digit * w; - t = k - bias; - if (t < TMIN) { - t = TMIN; - } else if (k >= (bias + TMAX)) { - t = TMAX; - } - if (digit < t) { - break; - } - - if (w > 0x7fffffff / (BASE - t)) { - /* integer overflow */ - throw new ParseException("Illegal char found", -1); - } - w *= BASE - t; - } - - /* - * Modification from sample code: Increments destCPCount here, where needed - * instead of in for() loop tail. - */ - ++destCPCount; - bias = adaptBias(i - oldi, destCPCount, (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 */ - throw new ParseException("Illegal char found", -1); - } - - n += i / destCPCount; - i %= destCPCount; - /* not needed for Punycode: */ - /* if (decode_digit(n) <= BASE) return punycode_invalid_input; */ - - if (n > 0x10ffff || isSurrogate(n)) { - /* Unicode code point overflow */ - throw new ParseException("Illegal char found", -1); - } - - /* Insert n at position i of the output: */ - cpLength = UTF16.getCharCount(n); - if ((destLength + cpLength) < destCapacity) { - int 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; - codeUnitIndex = UTF16.moveCodePointOffset(dest, 0, destLength, codeUnitIndex, i - codeUnitIndex); - } - - /* use the UChar index codeUnitIndex instead of the code point index i */ - if (codeUnitIndex < destLength) { - System.arraycopy(dest, codeUnitIndex, dest, codeUnitIndex + cpLength, (destLength - codeUnitIndex)); - if (caseFlags != null) { - System.arraycopy(caseFlags, codeUnitIndex, caseFlags, codeUnitIndex + cpLength, - destLength - codeUnitIndex); - } - } - if (cpLength == 1) { - /* BMP, insert one code unit */ - dest[codeUnitIndex] = (char) n; - } else { - /* supplementary character, insert two code units */ - dest[codeUnitIndex] = UTF16.getLeadSurrogate(n); - dest[codeUnitIndex + 1] = UTF16.getTrailSurrogate(n); - } - if (caseFlags != null) { - /* Case of last character determines uppercase flag: */ - caseFlags[codeUnitIndex] = isBasicUpperCase(src.charAt(in - 1)); - if (cpLength == 2) { - caseFlags[codeUnitIndex + 1] = false; - } - } - } - destLength += cpLength; - ++i; - } - result.append(dest, 0, destLength); - return result; - } -} +/* + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + ******************************************************************************* + * Copyright (C) 2003-2004, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + */ +// +// CHANGELOG +// 2005-05-19 Edward Wang +// - copy this file from icu4jsrc_3_2/src/com/ibm/icu/text/Punycode.java +// - move from package com.ibm.icu.text to package sun.net.idn +// - use ParseException instead of StringPrepParseException +// 2007-08-14 Martin Buchholz +// - remove redundant casts +// +package jdk_internal.icu.impl; + +import java.text.ParseException; + +import jdk_internal.icu.lang.UCharacter; +import jdk_internal.icu.text.UTF16; + +/** + * Ported code from ICU punycode.c + * + * @author ram + */ + +/* Package Private class */ +public final class Punycode { + + /* Punycode parameters for Bootstring */ + private static final int BASE = 36; + private static final int TMIN = 1; + private static final int TMAX = 26; + private static final int SKEW = 38; + private static final int DAMP = 700; + private static final int INITIAL_BIAS = 72; + private static final int INITIAL_N = 0x80; + + /* "Basic" Unicode/ASCII code points */ + private static final int HYPHEN = 0x2d; + private static final int DELIMITER = HYPHEN; + + private static final int ZERO = 0x30; + private static final int NINE = 0x39; + + private static final int SMALL_A = 0x61; + private static final int SMALL_Z = 0x7a; + + private static final int CAPITAL_A = 0x41; + private static final int CAPITAL_Z = 0x5a; + + // TODO: eliminate the 256 limitation + private static final int MAX_CP_COUNT = 256; + + private static final int UINT_MAGIC = 0x80000000; + private static final long ULONG_MAGIC = 0x8000000000000000L; + + private static int adaptBias(int delta, int length, boolean firstTime) { + if (firstTime) { + delta /= DAMP; + } else { + delta /= 2; + } + delta += delta / length; + + int count = 0; + for (; delta > ((BASE - TMIN) * TMAX) / 2; count += BASE) { + delta /= (BASE - TMIN); + } + + return count + (((BASE - TMIN + 1) * delta) / (delta + SKEW)); + } + + /** + * basicToDigit[] contains the numeric value of a basic code point (for use in + * representing integers) in the range 0 to BASE-1, or -1 if b is does not + * represent a value. + */ + static final int[] basicToDigit = new int[] { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, + -1, -1, -1, -1, -1, + + -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, -1, + -1, -1, -1, + + -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, -1, + -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1 }; + + private static char asciiCaseMap(char b, boolean 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; + } + + /** + * 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. + */ + private static char digitToBasic(int digit, boolean 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); + } + } + + /** + * 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. + * + * @param src + * @param caseFlags + * @return + * @throws ParseException + */ + public static StringBuffer encode(StringBuffer src, boolean[] caseFlags) throws ParseException { + + int[] cpBuffer = new int[MAX_CP_COUNT]; + int n, delta, handledCPCount, basicLength, destLength, bias, j, m, q, k, t, srcCPCount; + char c, c2; + int srcLength = src.length(); + int destCapacity = MAX_CP_COUNT; + char[] dest = new char[destCapacity]; + StringBuffer result = new StringBuffer(); + /* + * Handle the basic code points and convert extended ones to UTF-32 in cpBuffer + * (caseFlag in sign bit): + */ + srcCPCount = destLength = 0; + + for (j = 0; j < srcLength; ++j) { + if (srcCPCount == MAX_CP_COUNT) { + /* too many input code points */ + throw new ParseException("Too many input code points", -1); + } + c = src.charAt(j); + if (isBasic(c)) { + if (destLength < destCapacity) { + cpBuffer[srcCPCount++] = 0; + dest[destLength] = caseFlags != null ? asciiCaseMap(c, caseFlags[j]) : c; + } + ++destLength; + } else { + n = ((caseFlags != null && caseFlags[j]) ? 1 : 0) << 31L; + if (!UTF16.isSurrogate(c)) { + n |= c; + } else if (UTF16.isLeadSurrogate(c) && (j + 1) < srcLength + && UTF16.isTrailSurrogate(c2 = src.charAt(j + 1))) { + ++j; + + n |= UCharacter.getCodePoint(c, c2); + } else { + /* error: unmatched surrogate */ + throw new ParseException("Illegal char found", -1); + } + cpBuffer[srcCPCount++] = n; + } + } + + /* Finish the basic string - if it is not empty - with a delimiter. */ + basicLength = destLength; + if (basicLength > 0) { + if (destLength < destCapacity) { + dest[destLength] = DELIMITER; + } + ++destLength; + } + + /* + * handledCPCount is the number of code points that have been handled + * basicLength is the number of basic code points destLength is the number of + * chars that have been output + */ + + /* Initialize the state: */ + n = INITIAL_N; + delta = 0; + bias = INITIAL_BIAS; + + /* Main encoding loop: */ + for (handledCPCount = basicLength; handledCPCount < srcCPCount; /* no op */) { + /* + * All non-basic code points < n have been handled already. Find the next larger + * one: + */ + for (m = 0x7fffffff, j = 0; j < srcCPCount; ++j) { + q = cpBuffer[j] & 0x7fffffff; /* remove case flag from the sign bit */ + if (n <= q && q < m) { + m = q; + } + } + + /* + * Increase delta enough to advance the decoder's state to , but + * guard against overflow: + */ + if (m - n > (0x7fffffff - MAX_CP_COUNT - delta) / (handledCPCount + 1)) { + throw new RuntimeException("Internal program error"); + } + delta += (m - n) * (handledCPCount + 1); + n = m; + + /* Encode a sequence of same code points n */ + for (j = 0; j < srcCPCount; ++j) { + q = cpBuffer[j] & 0x7fffffff; /* remove case flag from the sign bit */ + if (q < n) { + ++delta; + } else if (q == n) { + /* Represent delta as a generalized variable-length integer: */ + for (q = delta, k = BASE; /* no condition */; k += BASE) { + + /** + * 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 < TMIN) { + t = TMIN; + } else if (k >= (bias + TMAX)) { + t = TMAX; + } + + if (q < t) { + break; + } + + if (destLength < destCapacity) { + dest[destLength++] = digitToBasic(t + (q - t) % (BASE - t), false); + } + q = (q - t) / (BASE - t); + } + + if (destLength < destCapacity) { + dest[destLength++] = digitToBasic(q, (cpBuffer[j] < 0)); + } + bias = adaptBias(delta, handledCPCount + 1, (handledCPCount == basicLength)); + delta = 0; + ++handledCPCount; + } + } + + ++delta; + ++n; + } + + return result.append(dest, 0, destLength); + } + + private static boolean isBasic(int ch) { + return (ch < INITIAL_N); + } + + private static boolean isBasicUpperCase(int ch) { + return (CAPITAL_A <= ch && ch <= CAPITAL_Z); + } + + private static boolean isSurrogate(int ch) { + return (((ch) & 0xfffff800) == 0xd800); + } + + /** + * Converts Punycode to Unicode. The Unicode string will be at most as long as + * the Punycode string. + * + * @param src + * @param caseFlags + * @return + * @throws ParseException + */ + public static StringBuffer decode(StringBuffer src, boolean[] caseFlags) throws ParseException { + int srcLength = src.length(); + StringBuffer result = new StringBuffer(); + int n, destLength, i, bias, basicLength, j, in, oldi, w, k, digit, t, destCPCount, firstSupplementaryIndex, + cpLength; + char b; + int destCapacity = MAX_CP_COUNT; + char[] dest = new char[destCapacity]; + + /* + * 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.charAt(--j) == DELIMITER) { + break; + } + } + destLength = basicLength = destCPCount = j; + + while (j > 0) { + b = src.charAt(--j); + if (!isBasic(b)) { + throw new ParseException("Illegal char found", -1); + } + + if (j < destCapacity) { + dest[j] = b; + + if (caseFlags != null) { + caseFlags[j] = isBasicUpperCase(b); + } + } + } + + /* Initialize the state: */ + n = INITIAL_N; + i = 0; + bias = INITIAL_BIAS; + firstSupplementaryIndex = 1000000000; + + /* + * Main decoding loop: Start just after the last delimiter if any basic code + * points were copied; start at the beginning otherwise. + */ + for (in = basicLength > 0 ? basicLength + 1 : 0; in < srcLength; /* no op */) { + /* + * in is the index of the next character to be consumed, and destCPCount is the + * number of code points in the output array. + * + * Decode a generalized variable-length integer into delta, which gets added to + * i. The overflow checking is easier if we increase i as we go, then subtract + * off its starting value at the end to obtain delta. + */ + for (oldi = i, w = 1, k = BASE; /* no condition */; k += BASE) { + if (in >= srcLength) { + throw new ParseException("Illegal char found", -1); + } + + digit = basicToDigit[(byte) src.charAt(in++)]; + if (digit < 0) { + throw new ParseException("Invalid char found", -1); + } + if (digit > (0x7fffffff - i) / w) { + /* integer overflow */ + throw new ParseException("Illegal char found", -1); + } + + i += digit * w; + t = k - bias; + if (t < TMIN) { + t = TMIN; + } else if (k >= (bias + TMAX)) { + t = TMAX; + } + if (digit < t) { + break; + } + + if (w > 0x7fffffff / (BASE - t)) { + /* integer overflow */ + throw new ParseException("Illegal char found", -1); + } + w *= BASE - t; + } + + /* + * Modification from sample code: Increments destCPCount here, where needed + * instead of in for() loop tail. + */ + ++destCPCount; + bias = adaptBias(i - oldi, destCPCount, (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 */ + throw new ParseException("Illegal char found", -1); + } + + n += i / destCPCount; + i %= destCPCount; + /* not needed for Punycode: */ + /* if (decode_digit(n) <= BASE) return punycode_invalid_input; */ + + if (n > 0x10ffff || isSurrogate(n)) { + /* Unicode code point overflow */ + throw new ParseException("Illegal char found", -1); + } + + /* Insert n at position i of the output: */ + cpLength = UTF16.getCharCount(n); + if ((destLength + cpLength) < destCapacity) { + int 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; + codeUnitIndex = UTF16.moveCodePointOffset(dest, 0, destLength, codeUnitIndex, i - codeUnitIndex); + } + + /* use the UChar index codeUnitIndex instead of the code point index i */ + if (codeUnitIndex < destLength) { + System.arraycopy(dest, codeUnitIndex, dest, codeUnitIndex + cpLength, (destLength - codeUnitIndex)); + if (caseFlags != null) { + System.arraycopy(caseFlags, codeUnitIndex, caseFlags, codeUnitIndex + cpLength, + destLength - codeUnitIndex); + } + } + if (cpLength == 1) { + /* BMP, insert one code unit */ + dest[codeUnitIndex] = (char) n; + } else { + /* supplementary character, insert two code units */ + dest[codeUnitIndex] = UTF16.getLeadSurrogate(n); + dest[codeUnitIndex + 1] = UTF16.getTrailSurrogate(n); + } + if (caseFlags != null) { + /* Case of last character determines uppercase flag: */ + caseFlags[codeUnitIndex] = isBasicUpperCase(src.charAt(in - 1)); + if (cpLength == 2) { + caseFlags[codeUnitIndex + 1] = false; + } + } + } + destLength += cpLength; + ++i; + } + result.append(dest, 0, destLength); + return result; + } +} diff --git a/src/main/java/jdk_internal/icu/impl/ReplaceableUCharacterIterator.java b/src/main/java/jdk_internal/icu/impl/ReplaceableUCharacterIterator.java index c1b00bb2..05b6076e 100755 --- a/src/main/java/jdk_internal/icu/impl/ReplaceableUCharacterIterator.java +++ b/src/main/java/jdk_internal/icu/impl/ReplaceableUCharacterIterator.java @@ -1,198 +1,198 @@ -/* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ******************************************************************************* - * (C) Copyright IBM Corp. 1996-2005 - All Rights Reserved * - * * - * The original version of this source code and documentation is copyrighted * - * and owned by IBM, These materials are provided under terms of a License * - * Agreement between IBM and Sun. This technology is protected by multiple * - * US and International patents. This notice and attribution to IBM may not * - * to removed. * - ******************************************************************************* - */ - -package jdk_internal.icu.impl; - -import jdk_internal.icu.text.Replaceable; -import jdk_internal.icu.text.ReplaceableString; -import jdk_internal.icu.text.UCharacterIterator; - -/** - * DLF docs must define behavior when Replaceable is mutated underneath the - * iterator. - * - * This and ICUCharacterIterator share some code, maybe they should share an - * implementation, or the common state and implementation should be moved up - * into UCharacterIterator. - * - * What are first, last, and getBeginIndex doing here?!?!?! - */ -public class ReplaceableUCharacterIterator extends UCharacterIterator { - - // public constructor ------------------------------------------------------ - - /** - * Public constructor - * - * @param str text which the iterator will be based on - */ - public ReplaceableUCharacterIterator(String str) { - if (str == null) { - throw new IllegalArgumentException(); - } - this.replaceable = new ReplaceableString(str); - this.currentIndex = 0; - } - - /** - * Public constructor - * - * @param buf buffer of text on which the iterator will be based - */ - public ReplaceableUCharacterIterator(StringBuffer buf) { - if (buf == null) { - throw new IllegalArgumentException(); - } - this.replaceable = new ReplaceableString(buf); - this.currentIndex = 0; - } - - // public methods ---------------------------------------------------------- - - /** - * Creates a copy of this iterator, does not clone the underlying - * Replaceableobject - * - * @return copy of this iterator - */ - public Object clone() { - try { - return super.clone(); - } catch (CloneNotSupportedException e) { - return null; // never invoked - } - } - - /** - * Returns the current UTF16 character. - * - * @return current UTF16 character - */ - public int current() { - if (currentIndex < replaceable.length()) { - return replaceable.charAt(currentIndex); - } - return DONE; - } - - /** - * Returns the length of the text - * - * @return length of the text - */ - public int getLength() { - return replaceable.length(); - } - - /** - * Gets the current currentIndex in text. - * - * @return current currentIndex in text. - */ - public int getIndex() { - return currentIndex; - } - - /** - * Returns next UTF16 character and increments the iterator's currentIndex by 1. - * If the resulting currentIndex is greater or equal to the text length, the - * currentIndex is reset to the text length and a value of DONECODEPOINT is - * returned. - * - * @return next UTF16 character in text or DONE if the new currentIndex is off - * the end of the text range. - */ - public int next() { - if (currentIndex < replaceable.length()) { - return replaceable.charAt(currentIndex++); - } - return DONE; - } - - /** - * Returns previous UTF16 character and decrements the iterator's currentIndex - * by 1. If the resulting currentIndex is less than 0, the currentIndex is reset - * to 0 and a value of DONECODEPOINT is returned. - * - * @return next UTF16 character in text or DONE if the new currentIndex is off - * the start of the text range. - */ - public int previous() { - if (currentIndex > 0) { - return replaceable.charAt(--currentIndex); - } - return DONE; - } - - /** - * Sets the currentIndex to the specified currentIndex in the text and returns - * that single UTF16 character at currentIndex. This assumes the text is stored - * as 16-bit code units. - * - * @param currentIndex the currentIndex within the text. - * @exception IllegalArgumentException is thrown if an invalid currentIndex is - * supplied. i.e. currentIndex is out of - * bounds. - */ - public void setIndex(int currentIndex) { - if (currentIndex < 0 || currentIndex > replaceable.length()) { - throw new IllegalArgumentException(); - } - this.currentIndex = currentIndex; - } - - public int getText(char[] fillIn, int offset) { - int length = replaceable.length(); - if (offset < 0 || offset + length > fillIn.length) { - throw new IndexOutOfBoundsException(Integer.toString(length)); - } - replaceable.getChars(0, length, fillIn, offset); - return length; - } - - // private data members ---------------------------------------------------- - - /** - * Replaceable object - */ - private Replaceable replaceable; - /** - * Current currentIndex - */ - private int currentIndex; - -} +/* + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ******************************************************************************* + * (C) Copyright IBM Corp. 1996-2005 - All Rights Reserved * + * * + * The original version of this source code and documentation is copyrighted * + * and owned by IBM, These materials are provided under terms of a License * + * Agreement between IBM and Sun. This technology is protected by multiple * + * US and International patents. This notice and attribution to IBM may not * + * to removed. * + ******************************************************************************* + */ + +package jdk_internal.icu.impl; + +import jdk_internal.icu.text.Replaceable; +import jdk_internal.icu.text.ReplaceableString; +import jdk_internal.icu.text.UCharacterIterator; + +/** + * DLF docs must define behavior when Replaceable is mutated underneath the + * iterator. + * + * This and ICUCharacterIterator share some code, maybe they should share an + * implementation, or the common state and implementation should be moved up + * into UCharacterIterator. + * + * What are first, last, and getBeginIndex doing here?!?!?! + */ +public class ReplaceableUCharacterIterator extends UCharacterIterator { + + // public constructor ------------------------------------------------------ + + /** + * Public constructor + * + * @param str text which the iterator will be based on + */ + public ReplaceableUCharacterIterator(String str) { + if (str == null) { + throw new IllegalArgumentException(); + } + this.replaceable = new ReplaceableString(str); + this.currentIndex = 0; + } + + /** + * Public constructor + * + * @param buf buffer of text on which the iterator will be based + */ + public ReplaceableUCharacterIterator(StringBuffer buf) { + if (buf == null) { + throw new IllegalArgumentException(); + } + this.replaceable = new ReplaceableString(buf); + this.currentIndex = 0; + } + + // public methods ---------------------------------------------------------- + + /** + * Creates a copy of this iterator, does not clone the underlying + * Replaceableobject + * + * @return copy of this iterator + */ + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + return null; // never invoked + } + } + + /** + * Returns the current UTF16 character. + * + * @return current UTF16 character + */ + public int current() { + if (currentIndex < replaceable.length()) { + return replaceable.charAt(currentIndex); + } + return DONE; + } + + /** + * Returns the length of the text + * + * @return length of the text + */ + public int getLength() { + return replaceable.length(); + } + + /** + * Gets the current currentIndex in text. + * + * @return current currentIndex in text. + */ + public int getIndex() { + return currentIndex; + } + + /** + * Returns next UTF16 character and increments the iterator's currentIndex by 1. + * If the resulting currentIndex is greater or equal to the text length, the + * currentIndex is reset to the text length and a value of DONECODEPOINT is + * returned. + * + * @return next UTF16 character in text or DONE if the new currentIndex is off + * the end of the text range. + */ + public int next() { + if (currentIndex < replaceable.length()) { + return replaceable.charAt(currentIndex++); + } + return DONE; + } + + /** + * Returns previous UTF16 character and decrements the iterator's currentIndex + * by 1. If the resulting currentIndex is less than 0, the currentIndex is reset + * to 0 and a value of DONECODEPOINT is returned. + * + * @return next UTF16 character in text or DONE if the new currentIndex is off + * the start of the text range. + */ + public int previous() { + if (currentIndex > 0) { + return replaceable.charAt(--currentIndex); + } + return DONE; + } + + /** + * Sets the currentIndex to the specified currentIndex in the text and returns + * that single UTF16 character at currentIndex. This assumes the text is stored + * as 16-bit code units. + * + * @param currentIndex the currentIndex within the text. + * @exception IllegalArgumentException is thrown if an invalid currentIndex is + * supplied. i.e. currentIndex is out of + * bounds. + */ + public void setIndex(int currentIndex) { + if (currentIndex < 0 || currentIndex > replaceable.length()) { + throw new IllegalArgumentException(); + } + this.currentIndex = currentIndex; + } + + public int getText(char[] fillIn, int offset) { + int length = replaceable.length(); + if (offset < 0 || offset + length > fillIn.length) { + throw new IndexOutOfBoundsException(Integer.toString(length)); + } + replaceable.getChars(0, length, fillIn, offset); + return length; + } + + // private data members ---------------------------------------------------- + + /** + * Replaceable object + */ + private Replaceable replaceable; + /** + * Current currentIndex + */ + private int currentIndex; + +} diff --git a/src/main/java/jdk_internal/icu/impl/StringPrepDataReader.java b/src/main/java/jdk_internal/icu/impl/StringPrepDataReader.java index 149155f0..f98084a7 100755 --- a/src/main/java/jdk_internal/icu/impl/StringPrepDataReader.java +++ b/src/main/java/jdk_internal/icu/impl/StringPrepDataReader.java @@ -1,120 +1,120 @@ -/* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -/* -/* - ****************************************************************************** - * Copyright (C) 2003, International Business Machines Corporation and * - * others. All Rights Reserved. * - ****************************************************************************** - * - * Created on May 2, 2003 - * - * To change the template for this generated file go to - * Window>Preferences>Java>Code Generation>Code and Comments - */ -// CHANGELOG -// 2005-05-19 Edward Wang -// - copy this file from icu4jsrc_3_2/src/com/ibm/icu/impl/StringPrepDataReader.java -// - move from package com.ibm.icu.impl to package sun.net.idn -// -package jdk_internal.icu.impl; - -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; - -/** - * @author ram - * - * To change the template for this generated type comment go to - * Window>Preferences>Java>Code Generation>Code and Comments - */ -public final class StringPrepDataReader implements ICUBinary.Authenticate { - - /** - *

- * private constructor. - *

- * - * @param inputStream ICU uprop.dat file input stream - * @exception IOException throw if data file fails authentication - * @draft 2.1 - */ - public StringPrepDataReader(InputStream inputStream) throws IOException { - - unicodeVersion = ICUBinary.readHeader(inputStream, DATA_FORMAT_ID, this); - - dataInputStream = new DataInputStream(inputStream); - - } - - public void read(byte[] idnaBytes, char[] mappingTable) throws IOException { - - // Read the bytes that make up the idnaTrie - dataInputStream.read(idnaBytes); - - // Read the extra data - for (int i = 0; i < mappingTable.length; i++) { - mappingTable[i] = dataInputStream.readChar(); - } - } - - public byte[] getDataFormatVersion() { - return DATA_FORMAT_VERSION; - } - - public boolean isDataVersionAcceptable(byte version[]) { - return version[0] == DATA_FORMAT_VERSION[0] && version[2] == DATA_FORMAT_VERSION[2] - && version[3] == DATA_FORMAT_VERSION[3]; - } - - public int[] readIndexes(int length) throws IOException { - int[] indexes = new int[length]; - // Read the indexes - for (int i = 0; i < length; i++) { - indexes[i] = dataInputStream.readInt(); - } - return indexes; - } - - public byte[] getUnicodeVersion() { - return unicodeVersion; - } - // private data members ------------------------------------------------- - - /** - * ICU data file input stream - */ - private DataInputStream dataInputStream; - private byte[] unicodeVersion; - /** - * File format version that this class understands. No guarantees are made if a - * older version is used see store.c of gennorm for more information and values - */ - /// * dataFormat="SPRP" 0x53, 0x50, 0x52, 0x50 */ - private static final byte DATA_FORMAT_ID[] = { (byte) 0x53, (byte) 0x50, (byte) 0x52, (byte) 0x50 }; - private static final byte DATA_FORMAT_VERSION[] = { (byte) 0x3, (byte) 0x2, (byte) 0x5, (byte) 0x2 }; - -} +/* + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* +/* + ****************************************************************************** + * Copyright (C) 2003, International Business Machines Corporation and * + * others. All Rights Reserved. * + ****************************************************************************** + * + * Created on May 2, 2003 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +// CHANGELOG +// 2005-05-19 Edward Wang +// - copy this file from icu4jsrc_3_2/src/com/ibm/icu/impl/StringPrepDataReader.java +// - move from package com.ibm.icu.impl to package sun.net.idn +// +package jdk_internal.icu.impl; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * @author ram + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public final class StringPrepDataReader implements ICUBinary.Authenticate { + + /** + *

+ * private constructor. + *

+ * + * @param inputStream ICU uprop.dat file input stream + * @exception IOException throw if data file fails authentication + * @draft 2.1 + */ + public StringPrepDataReader(InputStream inputStream) throws IOException { + + unicodeVersion = ICUBinary.readHeader(inputStream, DATA_FORMAT_ID, this); + + dataInputStream = new DataInputStream(inputStream); + + } + + public void read(byte[] idnaBytes, char[] mappingTable) throws IOException { + + // Read the bytes that make up the idnaTrie + dataInputStream.read(idnaBytes); + + // Read the extra data + for (int i = 0; i < mappingTable.length; i++) { + mappingTable[i] = dataInputStream.readChar(); + } + } + + public byte[] getDataFormatVersion() { + return DATA_FORMAT_VERSION; + } + + public boolean isDataVersionAcceptable(byte version[]) { + return version[0] == DATA_FORMAT_VERSION[0] && version[2] == DATA_FORMAT_VERSION[2] + && version[3] == DATA_FORMAT_VERSION[3]; + } + + public int[] readIndexes(int length) throws IOException { + int[] indexes = new int[length]; + // Read the indexes + for (int i = 0; i < length; i++) { + indexes[i] = dataInputStream.readInt(); + } + return indexes; + } + + public byte[] getUnicodeVersion() { + return unicodeVersion; + } + // private data members ------------------------------------------------- + + /** + * ICU data file input stream + */ + private DataInputStream dataInputStream; + private byte[] unicodeVersion; + /** + * File format version that this class understands. No guarantees are made if a + * older version is used see store.c of gennorm for more information and values + */ + /// * dataFormat="SPRP" 0x53, 0x50, 0x52, 0x50 */ + private static final byte DATA_FORMAT_ID[] = { (byte) 0x53, (byte) 0x50, (byte) 0x52, (byte) 0x50 }; + private static final byte DATA_FORMAT_VERSION[] = { (byte) 0x3, (byte) 0x2, (byte) 0x5, (byte) 0x2 }; + +} diff --git a/src/main/java/jdk_internal/icu/impl/Trie.java b/src/main/java/jdk_internal/icu/impl/Trie.java index ea48c547..bf587731 100755 --- a/src/main/java/jdk_internal/icu/impl/Trie.java +++ b/src/main/java/jdk_internal/icu/impl/Trie.java @@ -1,368 +1,368 @@ -/* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ****************************************************************************** - * Copyright (C) 1996-2014, International Business Machines Corporation and - * others. All Rights Reserved. - ****************************************************************************** - */ - -package jdk_internal.icu.impl; - -import java.io.DataInputStream; -import java.io.InputStream; - -import jdk_internal.icu.lang.UCharacter; -import jdk_internal.icu.text.UTF16; - -import java.io.IOException; - -/** - *

- * A trie is a kind of compressed, serializable table of values associated with - * Unicode code points (0..0x10ffff). - *

- *

- * This class defines the basic structure of a trie and provides methods to - * retrieve the offsets to the actual data. - *

- *

- * Data will be the form of an array of basic types, char or int. - *

- *

- * The actual data format will have to be specified by the user in the inner - * static interface com.ibm.icu.impl.Trie.DataManipulate. - *

- *

- * This trie implementation is optimized for getting offset while walking - * forward through a UTF-16 string. Therefore, the simplest and fastest access - * macros are the fromLead() and fromOffsetTrail() methods. The fromBMP() method - * are a little more complicated; they get offsets even for lead surrogate - * codepoints, while the fromLead() method get special "folded" offsets for lead - * surrogate code units if there is relevant data associated with them. From - * such a folded offsets, an offset needs to be extracted to supply to the - * fromOffsetTrail() methods. To handle such supplementary codepoints, some - * offset information are kept in the data. - *

- *

- * Methods in com.ibm.icu.impl.Trie.DataManipulate are called to retrieve that - * offset from the folded value for the lead surrogate unit. - *

- *

- * For examples of use, see com.ibm.icu.impl.CharTrie or - * com.ibm.icu.impl.IntTrie. - *

- * - * @author synwee - * @see com.ibm.icu.impl.CharTrie - * @see com.ibm.icu.impl.IntTrie - * @since release 2.1, Jan 01 2002 - */ -public abstract class Trie { - // public class declaration ---------------------------------------- - - /** - * Character data in com.ibm.impl.Trie have different user-specified format for - * different purposes. This interface specifies methods to be implemented in - * order for com.ibm.impl.Trie, to surrogate offset information encapsulated - * within the data. - */ - public static interface DataManipulate { - /** - * Called by com.ibm.icu.impl.Trie to extract from a lead surrogate's data the - * index array offset of the indexes for that lead surrogate. - * - * @param value data value for a surrogate from the trie, including the folding - * offset - * @return data offset or 0 if there is no data for the lead surrogate - */ - public int getFoldingOffset(int value); - } - - // default implementation - private static class DefaultGetFoldingOffset implements DataManipulate { - public int getFoldingOffset(int value) { - return value; - } - } - - // protected constructor ------------------------------------------- - - /** - * Trie constructor for CharTrie use. - * - * @param inputStream ICU data file input stream which contains the trie - * @param dataManipulate object containing the information to parse the trie - * data - * @throws IOException thrown when input stream does not have the right header. - */ - protected Trie(InputStream inputStream, DataManipulate dataManipulate) throws IOException { - DataInputStream input = new DataInputStream(inputStream); - // Magic number to authenticate the data. - int signature = input.readInt(); - m_options_ = input.readInt(); - - if (!checkHeader(signature)) { - throw new IllegalArgumentException( - "ICU data file error: Trie header authentication failed, please check if you have the most updated ICU data file"); - } - - if (dataManipulate != null) { - m_dataManipulate_ = dataManipulate; - } else { - m_dataManipulate_ = new DefaultGetFoldingOffset(); - } - m_isLatin1Linear_ = (m_options_ & HEADER_OPTIONS_LATIN1_IS_LINEAR_MASK_) != 0; - m_dataOffset_ = input.readInt(); - m_dataLength_ = input.readInt(); - unserialize(inputStream); - } - - // protected data members ------------------------------------------ - - /** - * Lead surrogate code points' index displacement in the index array. - * - *
{@code
-	 * 0x10000-0xd800=0x2800
-	 * 0x2800 >> INDEX_STAGE_1_SHIFT_
-	 * }
- */ - protected static final int LEAD_INDEX_OFFSET_ = 0x2800 >> 5; - /** - * Shift size for shifting right the input index. 1..9 - */ - protected static final int INDEX_STAGE_1_SHIFT_ = 5; - /** - * 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 DATA_GRANULARITY. - * 0..INDEX_STAGE_1_SHIFT - */ - protected static final int INDEX_STAGE_2_SHIFT_ = 2; - /** - * Number of data values in a stage 2 (data array) block. - */ - protected static final int DATA_BLOCK_LENGTH = 1 << INDEX_STAGE_1_SHIFT_; - /** - * Mask for getting the lower bits from the input index. DATA_BLOCK_LENGTH - 1. - */ - protected static final int INDEX_STAGE_3_MASK_ = DATA_BLOCK_LENGTH - 1; - /** - * Surrogate mask to use when shifting offset to retrieve supplementary values - */ - protected static final int SURROGATE_MASK_ = 0x3FF; - /** - * Index or UTF16 characters - */ - protected char m_index_[]; - /** - * Internal TrieValue which handles the parsing of the data value. This class is - * to be implemented by the user - */ - protected DataManipulate m_dataManipulate_; - /** - * Start index of the data portion of the trie. CharTrie combines index and data - * into a char array, so this is used to indicate the initial offset to the data - * portion. Note this index always points to the initial value. - */ - protected int m_dataOffset_; - /** - * Length of the data array - */ - protected int m_dataLength_; - - // protected methods ----------------------------------------------- - - /** - * Gets the offset to the data which the surrogate pair points to. - * - * @param lead lead surrogate - * @param trail trailing surrogate - * @return offset to data - */ - protected abstract int getSurrogateOffset(char lead, char trail); - - /** - * Gets the offset to the data which the index ch after variable offset points - * to. Note for locating a non-supplementary character data offset, calling - *

- * getRawOffset(0, ch); - *

- * will do. Otherwise if it is a supplementary character formed by surrogates - * lead and trail. Then we would have to call getRawOffset() with - * getFoldingIndexOffset(). See getSurrogateOffset(). - * - * @param offset index offset which ch is to start from - * @param ch index to be used after offset - * @return offset to the data - */ - protected final int getRawOffset(int offset, char ch) { - return (m_index_[offset + (ch >> INDEX_STAGE_1_SHIFT_)] << INDEX_STAGE_2_SHIFT_) + (ch & INDEX_STAGE_3_MASK_); - } - - /** - * Gets the offset to data which the BMP character points to Treats a lead - * surrogate as a normal code point. - * - * @param ch BMP character - * @return offset to data - */ - protected final int getBMPOffset(char ch) { - return (ch >= UTF16.LEAD_SURROGATE_MIN_VALUE && ch <= UTF16.LEAD_SURROGATE_MAX_VALUE) - ? getRawOffset(LEAD_INDEX_OFFSET_, ch) - : getRawOffset(0, ch); - // using a getRawOffset(ch) makes no diff - } - - /** - * Gets the offset to the data which this lead surrogate character points to. - * Data at the returned offset may contain folding offset information for the - * next trailing surrogate character. - * - * @param ch lead surrogate character - * @return offset to data - */ - protected final int getLeadOffset(char ch) { - return getRawOffset(0, ch); - } - - /** - * Internal trie getter from a code point. Could be faster(?) but longer with - * {@code if((c32)<=0xd7ff) { (result)=_TRIE_GET_RAW(trie, data, 0, c32); }} - * Gets the offset to data which the codepoint points to - * - * @param ch codepoint - * @return offset to data - */ - protected final int getCodePointOffset(int ch) { - // if ((ch >> 16) == 0) slower - if (ch < 0) { - return -1; - } else if (ch < UTF16.LEAD_SURROGATE_MIN_VALUE) { - // fastpath for the part of the BMP below surrogates (D800) where getRawOffset() - // works - return getRawOffset(0, (char) ch); - } else if (ch < UTF16.SUPPLEMENTARY_MIN_VALUE) { - // BMP codepoint - return getBMPOffset((char) ch); - } else if (ch <= UCharacter.MAX_VALUE) { - // look at the construction of supplementary characters - // trail forms the ends of it. - return getSurrogateOffset(UTF16.getLeadSurrogate(ch), (char) (ch & SURROGATE_MASK_)); - } else { - // return -1 if there is an error, in this case we return - return -1; - } - } - - /** - *

- * Parses the inputstream and creates the trie index with it. - *

- *

- * This is overwritten by the child classes. - * - * @param inputStream input stream containing the trie information - * @exception IOException thrown when data reading fails. - */ - protected void unserialize(InputStream inputStream) throws IOException { - // indexLength is a multiple of 1024 >> INDEX_STAGE_2_SHIFT_ - m_index_ = new char[m_dataOffset_]; - DataInputStream input = new DataInputStream(inputStream); - for (int i = 0; i < m_dataOffset_; i++) { - m_index_[i] = input.readChar(); - } - } - - /** - * Determines if this is a 16 bit trie - * - * @return true if this is a 16 bit trie - */ - protected final boolean isCharTrie() { - return (m_options_ & HEADER_OPTIONS_DATA_IS_32_BIT_) == 0; - } - - // private data members -------------------------------------------- - - /** - * Latin 1 option mask - */ - protected static final int HEADER_OPTIONS_LATIN1_IS_LINEAR_MASK_ = 0x200; - /** - * Constant number to authenticate the byte block - */ - protected static final int HEADER_SIGNATURE_ = 0x54726965; - /** - * Header option formatting - */ - private static final int HEADER_OPTIONS_SHIFT_MASK_ = 0xF; - protected static final int HEADER_OPTIONS_INDEX_SHIFT_ = 4; - protected static final int HEADER_OPTIONS_DATA_IS_32_BIT_ = 0x100; - - /** - * Flag indicator for Latin quick access data block - */ - private boolean m_isLatin1Linear_; - - /** - *

- * Trie options field. - *

- *

- * options bit field:
- * 9 1 = Latin-1 data is stored linearly at data + DATA_BLOCK_LENGTH
- * 8 0 = 16-bit data, 1=32-bit data
- * 7..4 INDEX_STAGE_1_SHIFT // 0..INDEX_STAGE_2_SHIFT
- * 3..0 INDEX_STAGE_2_SHIFT // 1..9
- */ - private int m_options_; - - // private methods --------------------------------------------------- - - /** - * Authenticates raw data header. Checking the header information, signature and - * options. - * - * @param signature This contains the options and type of a Trie - * @return true if the header is authenticated valid - */ - private final boolean checkHeader(int signature) { - // check the signature - // Trie in big-endian US-ASCII (0x54726965). - // Magic number to authenticate the data. - if (signature != HEADER_SIGNATURE_) { - return false; - } - - if ((m_options_ & HEADER_OPTIONS_SHIFT_MASK_) != INDEX_STAGE_1_SHIFT_ - || ((m_options_ >> HEADER_OPTIONS_INDEX_SHIFT_) & HEADER_OPTIONS_SHIFT_MASK_) != INDEX_STAGE_2_SHIFT_) { - return false; - } - return true; - } -} +/* + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ****************************************************************************** + * Copyright (C) 1996-2014, International Business Machines Corporation and + * others. All Rights Reserved. + ****************************************************************************** + */ + +package jdk_internal.icu.impl; + +import java.io.DataInputStream; +import java.io.InputStream; + +import jdk_internal.icu.lang.UCharacter; +import jdk_internal.icu.text.UTF16; + +import java.io.IOException; + +/** + *

+ * A trie is a kind of compressed, serializable table of values associated with + * Unicode code points (0..0x10ffff). + *

+ *

+ * This class defines the basic structure of a trie and provides methods to + * retrieve the offsets to the actual data. + *

+ *

+ * Data will be the form of an array of basic types, char or int. + *

+ *

+ * The actual data format will have to be specified by the user in the inner + * static interface com.ibm.icu.impl.Trie.DataManipulate. + *

+ *

+ * This trie implementation is optimized for getting offset while walking + * forward through a UTF-16 string. Therefore, the simplest and fastest access + * macros are the fromLead() and fromOffsetTrail() methods. The fromBMP() method + * are a little more complicated; they get offsets even for lead surrogate + * codepoints, while the fromLead() method get special "folded" offsets for lead + * surrogate code units if there is relevant data associated with them. From + * such a folded offsets, an offset needs to be extracted to supply to the + * fromOffsetTrail() methods. To handle such supplementary codepoints, some + * offset information are kept in the data. + *

+ *

+ * Methods in com.ibm.icu.impl.Trie.DataManipulate are called to retrieve that + * offset from the folded value for the lead surrogate unit. + *

+ *

+ * For examples of use, see com.ibm.icu.impl.CharTrie or + * com.ibm.icu.impl.IntTrie. + *

+ * + * @author synwee + * @see com.ibm.icu.impl.CharTrie + * @see com.ibm.icu.impl.IntTrie + * @since release 2.1, Jan 01 2002 + */ +public abstract class Trie { + // public class declaration ---------------------------------------- + + /** + * Character data in com.ibm.impl.Trie have different user-specified format for + * different purposes. This interface specifies methods to be implemented in + * order for com.ibm.impl.Trie, to surrogate offset information encapsulated + * within the data. + */ + public static interface DataManipulate { + /** + * Called by com.ibm.icu.impl.Trie to extract from a lead surrogate's data the + * index array offset of the indexes for that lead surrogate. + * + * @param value data value for a surrogate from the trie, including the folding + * offset + * @return data offset or 0 if there is no data for the lead surrogate + */ + public int getFoldingOffset(int value); + } + + // default implementation + private static class DefaultGetFoldingOffset implements DataManipulate { + public int getFoldingOffset(int value) { + return value; + } + } + + // protected constructor ------------------------------------------- + + /** + * Trie constructor for CharTrie use. + * + * @param inputStream ICU data file input stream which contains the trie + * @param dataManipulate object containing the information to parse the trie + * data + * @throws IOException thrown when input stream does not have the right header. + */ + protected Trie(InputStream inputStream, DataManipulate dataManipulate) throws IOException { + DataInputStream input = new DataInputStream(inputStream); + // Magic number to authenticate the data. + int signature = input.readInt(); + m_options_ = input.readInt(); + + if (!checkHeader(signature)) { + throw new IllegalArgumentException( + "ICU data file error: Trie header authentication failed, please check if you have the most updated ICU data file"); + } + + if (dataManipulate != null) { + m_dataManipulate_ = dataManipulate; + } else { + m_dataManipulate_ = new DefaultGetFoldingOffset(); + } + m_isLatin1Linear_ = (m_options_ & HEADER_OPTIONS_LATIN1_IS_LINEAR_MASK_) != 0; + m_dataOffset_ = input.readInt(); + m_dataLength_ = input.readInt(); + unserialize(inputStream); + } + + // protected data members ------------------------------------------ + + /** + * Lead surrogate code points' index displacement in the index array. + * + *
{@code
+	 * 0x10000-0xd800=0x2800
+	 * 0x2800 >> INDEX_STAGE_1_SHIFT_
+	 * }
+ */ + protected static final int LEAD_INDEX_OFFSET_ = 0x2800 >> 5; + /** + * Shift size for shifting right the input index. 1..9 + */ + protected static final int INDEX_STAGE_1_SHIFT_ = 5; + /** + * 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 DATA_GRANULARITY. + * 0..INDEX_STAGE_1_SHIFT + */ + protected static final int INDEX_STAGE_2_SHIFT_ = 2; + /** + * Number of data values in a stage 2 (data array) block. + */ + protected static final int DATA_BLOCK_LENGTH = 1 << INDEX_STAGE_1_SHIFT_; + /** + * Mask for getting the lower bits from the input index. DATA_BLOCK_LENGTH - 1. + */ + protected static final int INDEX_STAGE_3_MASK_ = DATA_BLOCK_LENGTH - 1; + /** + * Surrogate mask to use when shifting offset to retrieve supplementary values + */ + protected static final int SURROGATE_MASK_ = 0x3FF; + /** + * Index or UTF16 characters + */ + protected char m_index_[]; + /** + * Internal TrieValue which handles the parsing of the data value. This class is + * to be implemented by the user + */ + protected DataManipulate m_dataManipulate_; + /** + * Start index of the data portion of the trie. CharTrie combines index and data + * into a char array, so this is used to indicate the initial offset to the data + * portion. Note this index always points to the initial value. + */ + protected int m_dataOffset_; + /** + * Length of the data array + */ + protected int m_dataLength_; + + // protected methods ----------------------------------------------- + + /** + * Gets the offset to the data which the surrogate pair points to. + * + * @param lead lead surrogate + * @param trail trailing surrogate + * @return offset to data + */ + protected abstract int getSurrogateOffset(char lead, char trail); + + /** + * Gets the offset to the data which the index ch after variable offset points + * to. Note for locating a non-supplementary character data offset, calling + *

+ * getRawOffset(0, ch); + *

+ * will do. Otherwise if it is a supplementary character formed by surrogates + * lead and trail. Then we would have to call getRawOffset() with + * getFoldingIndexOffset(). See getSurrogateOffset(). + * + * @param offset index offset which ch is to start from + * @param ch index to be used after offset + * @return offset to the data + */ + protected final int getRawOffset(int offset, char ch) { + return (m_index_[offset + (ch >> INDEX_STAGE_1_SHIFT_)] << INDEX_STAGE_2_SHIFT_) + (ch & INDEX_STAGE_3_MASK_); + } + + /** + * Gets the offset to data which the BMP character points to Treats a lead + * surrogate as a normal code point. + * + * @param ch BMP character + * @return offset to data + */ + protected final int getBMPOffset(char ch) { + return (ch >= UTF16.LEAD_SURROGATE_MIN_VALUE && ch <= UTF16.LEAD_SURROGATE_MAX_VALUE) + ? getRawOffset(LEAD_INDEX_OFFSET_, ch) + : getRawOffset(0, ch); + // using a getRawOffset(ch) makes no diff + } + + /** + * Gets the offset to the data which this lead surrogate character points to. + * Data at the returned offset may contain folding offset information for the + * next trailing surrogate character. + * + * @param ch lead surrogate character + * @return offset to data + */ + protected final int getLeadOffset(char ch) { + return getRawOffset(0, ch); + } + + /** + * Internal trie getter from a code point. Could be faster(?) but longer with + * {@code if((c32)<=0xd7ff) { (result)=_TRIE_GET_RAW(trie, data, 0, c32); }} + * Gets the offset to data which the codepoint points to + * + * @param ch codepoint + * @return offset to data + */ + protected final int getCodePointOffset(int ch) { + // if ((ch >> 16) == 0) slower + if (ch < 0) { + return -1; + } else if (ch < UTF16.LEAD_SURROGATE_MIN_VALUE) { + // fastpath for the part of the BMP below surrogates (D800) where getRawOffset() + // works + return getRawOffset(0, (char) ch); + } else if (ch < UTF16.SUPPLEMENTARY_MIN_VALUE) { + // BMP codepoint + return getBMPOffset((char) ch); + } else if (ch <= UCharacter.MAX_VALUE) { + // look at the construction of supplementary characters + // trail forms the ends of it. + return getSurrogateOffset(UTF16.getLeadSurrogate(ch), (char) (ch & SURROGATE_MASK_)); + } else { + // return -1 if there is an error, in this case we return + return -1; + } + } + + /** + *

+ * Parses the inputstream and creates the trie index with it. + *

+ *

+ * This is overwritten by the child classes. + * + * @param inputStream input stream containing the trie information + * @exception IOException thrown when data reading fails. + */ + protected void unserialize(InputStream inputStream) throws IOException { + // indexLength is a multiple of 1024 >> INDEX_STAGE_2_SHIFT_ + m_index_ = new char[m_dataOffset_]; + DataInputStream input = new DataInputStream(inputStream); + for (int i = 0; i < m_dataOffset_; i++) { + m_index_[i] = input.readChar(); + } + } + + /** + * Determines if this is a 16 bit trie + * + * @return true if this is a 16 bit trie + */ + protected final boolean isCharTrie() { + return (m_options_ & HEADER_OPTIONS_DATA_IS_32_BIT_) == 0; + } + + // private data members -------------------------------------------- + + /** + * Latin 1 option mask + */ + protected static final int HEADER_OPTIONS_LATIN1_IS_LINEAR_MASK_ = 0x200; + /** + * Constant number to authenticate the byte block + */ + protected static final int HEADER_SIGNATURE_ = 0x54726965; + /** + * Header option formatting + */ + private static final int HEADER_OPTIONS_SHIFT_MASK_ = 0xF; + protected static final int HEADER_OPTIONS_INDEX_SHIFT_ = 4; + protected static final int HEADER_OPTIONS_DATA_IS_32_BIT_ = 0x100; + + /** + * Flag indicator for Latin quick access data block + */ + private boolean m_isLatin1Linear_; + + /** + *

+ * Trie options field. + *

+ *

+ * options bit field:
+ * 9 1 = Latin-1 data is stored linearly at data + DATA_BLOCK_LENGTH
+ * 8 0 = 16-bit data, 1=32-bit data
+ * 7..4 INDEX_STAGE_1_SHIFT // 0..INDEX_STAGE_2_SHIFT
+ * 3..0 INDEX_STAGE_2_SHIFT // 1..9
+ */ + private int m_options_; + + // private methods --------------------------------------------------- + + /** + * Authenticates raw data header. Checking the header information, signature and + * options. + * + * @param signature This contains the options and type of a Trie + * @return true if the header is authenticated valid + */ + private final boolean checkHeader(int signature) { + // check the signature + // Trie in big-endian US-ASCII (0x54726965). + // Magic number to authenticate the data. + if (signature != HEADER_SIGNATURE_) { + return false; + } + + if ((m_options_ & HEADER_OPTIONS_SHIFT_MASK_) != INDEX_STAGE_1_SHIFT_ + || ((m_options_ >> HEADER_OPTIONS_INDEX_SHIFT_) & HEADER_OPTIONS_SHIFT_MASK_) != INDEX_STAGE_2_SHIFT_) { + return false; + } + return true; + } +} diff --git a/src/main/java/jdk_internal/icu/impl/Trie2.java b/src/main/java/jdk_internal/icu/impl/Trie2.java index 15347caf..f91b9f15 100755 --- a/src/main/java/jdk_internal/icu/impl/Trie2.java +++ b/src/main/java/jdk_internal/icu/impl/Trie2.java @@ -1,652 +1,652 @@ -/* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ******************************************************************************* - * Copyright (C) 2009-2014, International Business Machines Corporation and - * others. All Rights Reserved. - ******************************************************************************* - */ - -package jdk_internal.icu.impl; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - * This is the interface and common implementation of a Unicode Trie2. It is a - * kind of compressed table that maps from Unicode code points (0..0x10ffff) to - * 16- or 32-bit integer values. It works best when there are ranges of - * characters with the same value, which is generally the case with Unicode - * character properties. - * - * This is the second common version of a Unicode trie (hence the name Trie2). - * - */ -abstract class Trie2 implements Iterable { - - /** - * Create a Trie2 from its serialized form. Inverse of utrie2_serialize(). - * - * Reads from the current position and leaves the buffer after the end of the - * trie. - * - * The serialized format is identical between ICU4C and ICU4J, so this function - * will work with serialized Trie2s from either. - * - * The actual type of the returned Trie2 will be either Trie2_16 or Trie2_32, - * depending on the width of the data. - * - * To obtain the width of the Trie2, check the actual class type of the returned - * Trie2. Or use the createFromSerialized() function of Trie2_16 or Trie2_32, - * which will return only Tries of their specific type/size. - * - * The serialized Trie2 on the stream may be in either little or big endian byte - * order. This allows using serialized Tries from ICU4C without needing to - * consider the byte order of the system that created them. - * - * @param bytes a byte buffer to the serialized form of a UTrie2. - * @return An unserialized Trie2, ready for use. - * @throws IllegalArgumentException if the stream does not contain a serialized - * Trie2. - * @throws IOException if a read error occurs in the buffer. - * - */ - public static Trie2 createFromSerialized(ByteBuffer bytes) throws IOException { - // From ICU4C utrie2_impl.h - // * Trie2 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; - - ByteOrder outerByteOrder = bytes.order(); - try { - UTrie2Header header = new UTrie2Header(); - - /* check the signature */ - header.signature = bytes.getInt(); - switch (header.signature) { - case 0x54726932: - // The buffer is already set to the trie data byte order. - break; - case 0x32697254: - // Temporarily reverse the byte order. - boolean isBigEndian = outerByteOrder == ByteOrder.BIG_ENDIAN; - bytes.order(isBigEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); - header.signature = 0x54726932; - break; - default: - throw new IllegalArgumentException("Buffer does not contain a serialized UTrie2"); - } - - header.options = bytes.getChar(); - header.indexLength = bytes.getChar(); - header.shiftedDataLength = bytes.getChar(); - header.index2NullOffset = bytes.getChar(); - header.dataNullOffset = bytes.getChar(); - header.shiftedHighStart = bytes.getChar(); - - if ((header.options & UTRIE2_OPTIONS_VALUE_BITS_MASK) != 0) { - throw new IllegalArgumentException("UTrie2 serialized format error."); - } - - Trie2 This; - This = new Trie2_16(); - This.header = header; - - /* get the length values and offsets */ - This.indexLength = header.indexLength; - This.dataLength = header.shiftedDataLength << UTRIE2_INDEX_SHIFT; - This.index2NullOffset = header.index2NullOffset; - This.dataNullOffset = header.dataNullOffset; - This.highStart = header.shiftedHighStart << UTRIE2_SHIFT_1; - This.highValueIndex = This.dataLength - UTRIE2_DATA_GRANULARITY; - This.highValueIndex += This.indexLength; - - // Allocate the Trie2 index array. If the data width is 16 bits, the array also - // includes the space for the data. - - int indexArraySize = This.indexLength; - indexArraySize += This.dataLength; - This.index = new char[indexArraySize]; - - /* Read in the index */ - int i; - for (i = 0; i < This.indexLength; i++) { - This.index[i] = bytes.getChar(); - } - - /* - * Read in the data. 16 bit data goes in the same array as the index. 32 bit - * data goes in its own separate data array. - */ - This.data16 = This.indexLength; - for (i = 0; i < This.dataLength; i++) { - This.index[This.data16 + i] = bytes.getChar(); - } - - This.data32 = null; - This.initialValue = This.index[This.dataNullOffset]; - This.errorValue = This.index[This.data16 + UTRIE2_BAD_UTF8_DATA_OFFSET]; - - return This; - } finally { - bytes.order(outerByteOrder); - } - } - - /** - * Get the value for a code point as stored in the Trie2. - * - * @param codePoint the code point - * @return the value - */ - public abstract int get(int codePoint); - - /** - * Get the trie value for a UTF-16 code unit. - * - * A Trie2 stores two distinct values for input in the lead surrogate range, one - * for lead surrogates, which is the value that will be returned by this - * function, and a second value that is returned by Trie2.get(). - * - * For code units outside of the lead surrogate range, this function returns the - * same result as Trie2.get(). - * - * This function, together with the alternate value for lead surrogates, makes - * possible very efficient processing of UTF-16 strings without first converting - * surrogate pairs to their corresponding 32 bit code point values. - * - * At build-time, enumerate the contents of the Trie2 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 - * Trie2Writable.setForLeadSurrogateCodeUnit(). - * - * At runtime, use Trie2.getFromU16SingleLead(). 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 and look up its value - * with Trie2.get(); 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. - * - * @param c the code point or lead surrogate value. - * @return the value - */ - public abstract int getFromU16SingleLead(char c); - - /** - * When iterating over the contents of a Trie2, Elements of this type are - * produced. The iterator will return one item for each contiguous range of - * codepoints having the same value. - * - * When iterating, the same Trie2EnumRange object will be reused and returned - * for each range. If you need to retain complete iteration results, clone each - * returned Trie2EnumRange, or save the range in some other way, before - * advancing to the next iteration step. - */ - public static class Range { - public int startCodePoint; - public int endCodePoint; // Inclusive. - public int value; - public boolean leadSurrogate; - - public boolean equals(Object other) { - if (other == null || !(other.getClass().equals(getClass()))) { - return false; - } - Range tother = (Range) other; - return this.startCodePoint == tother.startCodePoint && this.endCodePoint == tother.endCodePoint - && this.value == tother.value && this.leadSurrogate == tother.leadSurrogate; - } - - public int hashCode() { - int h = initHash(); - h = hashUChar32(h, startCodePoint); - h = hashUChar32(h, endCodePoint); - h = hashInt(h, value); - h = hashByte(h, leadSurrogate ? 1 : 0); - return h; - } - } - - /** - * Create an iterator over the value ranges in this Trie2. Values from the Trie2 - * are not remapped or filtered, but are returned as they are stored in the - * Trie2. - * - * @return an Iterator - */ - public Iterator iterator() { - return iterator(defaultValueMapper); - } - - private static ValueMapper defaultValueMapper = new ValueMapper() { - public int map(int in) { - return in; - } - }; - - /** - * Create an iterator over the value ranges from this Trie2. Values from the - * Trie2 are passed through a caller-supplied remapping function, and it is the - * remapped values that determine the ranges that will be produced by the - * iterator. - * - * - * @param mapper provides a function to remap values obtained from the Trie2. - * @return an Iterator - */ - public Iterator iterator(ValueMapper mapper) { - return new Trie2Iterator(mapper); - } - - /** - * When iterating over the contents of a Trie2, an instance of TrieValueMapper - * may be used to remap the values from the Trie2. The remapped values will be - * used both in determining the ranges of codepoints and as the value to be - * returned for each range. - * - * Example of use, with an anonymous subclass of TrieValueMapper: - * - * - * ValueMapper m = new ValueMapper() { int map(int in) {return in & 0x1f;}; } - * for (Iterator iter = trie.iterator(m); i.hasNext(); ) { - * Trie2EnumRange r = i.next(); ... // Do something with the range r. } - * - */ - public interface ValueMapper { - public int map(int originalVal); - } - - // -------------------------------------------------------------------------------- - // - // Below this point are internal implementation items. No further public API. - // - // -------------------------------------------------------------------------------- - - /** - * Trie2 data structure in serialized form: - * - * UTrie2Header header; uint16_t index[header.index2Length]; uint16_t - * data[header.shiftedDataLength<<2]; -- or uint32_t data[...] - * - * For Java, this is read from the stream into an instance of UTrie2Header. (The - * C version just places a struct over the raw serialized data.) - * - * @internal - */ - static class UTrie2Header { - /** "Tri2" in big-endian US-ASCII (0x54726932) */ - int signature; - - /** - * options bit field (uint16_t): 15.. 4 reserved (0) 3.. 0 UTrie2ValueBits - * valueBits - */ - int options; - - /** UTRIE2_INDEX_1_OFFSET..UTRIE2_MAX_INDEX_LENGTH (uint16_t) */ - int indexLength; - - /** - * (UTRIE2_DATA_START_OFFSET..UTRIE2_MAX_DATA_LENGTH)>>UTRIE2_INDEX_SHIFT - * (uint16_t) - */ - int shiftedDataLength; - - /** Null index and data blocks, not shifted. (uint16_t) */ - int 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) - */ - int shiftedHighStart; - } - - // - // Data members of UTrie2. - // - UTrie2Header header; - char index[]; // Index array. Includes data for 16 bit Tries. - int data16; // Offset to data portion of the index array, if 16 bit data. - // zero if 32 bit data. - int data32[]; // NULL if 16b data is used via index - - int indexLength; - int dataLength; - int index2NullOffset; // 0xffff if there is no dedicated index-2 null block - int initialValue; - - /** Value returned for out-of-range code points and illegal UTF-8. */ - int errorValue; - - /* Start of the last range which ends at U+10ffff, and its value. */ - int highStart; - int highValueIndex; - - int dataNullOffset; - - /** - * Trie2 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. - */ - - static final int UTRIE2_OPTIONS_VALUE_BITS_MASK = 0x000f; - - /** Shift size for getting the index-1 table offset. */ - static final int UTRIE2_SHIFT_1 = 6 + 5; - - /** Shift size for getting the index-2 table offset. */ - static final int UTRIE2_SHIFT_2 = 5; - - /** - * Difference between the two shift sizes, for getting an index-1 offset from an - * index-2 offset. 6=11-5 - */ - static final int 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. - */ - static final int UTRIE2_OMITTED_BMP_INDEX_1_LENGTH = 0x10000 >> UTRIE2_SHIFT_1; - - /** Number of entries in an index-2 block. 64=0x40 */ - static final int UTRIE2_INDEX_2_BLOCK_LENGTH = 1 << UTRIE2_SHIFT_1_2; - - /** Mask for getting the lower bits for the in-index-2-block offset. */ - static final int UTRIE2_INDEX_2_MASK = UTRIE2_INDEX_2_BLOCK_LENGTH - 1; - - /** Number of entries in a data block. 32=0x20 */ - static final int UTRIE2_DATA_BLOCK_LENGTH = 1 << UTRIE2_SHIFT_2; - - /** Mask for getting the lower bits for the in-data-block offset. */ - static final int UTRIE2_DATA_MASK = UTRIE2_DATA_BLOCK_LENGTH - 1; - - /** - * 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 - * data blocks to be aligned by UTRIE2_DATA_GRANULARITY. - */ - static final int UTRIE2_INDEX_SHIFT = 2; - - /** The alignment size of a data block. Also the granularity for compaction. */ - static final int UTRIE2_DATA_GRANULARITY = 1 << UTRIE2_INDEX_SHIFT; - - /** - * 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.) - */ - static final int UTRIE2_LSCP_INDEX_2_OFFSET = 0x10000 >> UTRIE2_SHIFT_2; - static final int UTRIE2_LSCP_INDEX_2_LENGTH = 0x400 >> UTRIE2_SHIFT_2; - - /** Count the lengths of both BMP pieces. 2080=0x820 */ - static final int 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. - */ - static final int UTRIE2_UTF8_2B_INDEX_2_OFFSET = UTRIE2_INDEX_2_BMP_LENGTH; - static final int 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. - */ - static final int UTRIE2_INDEX_1_OFFSET = UTRIE2_UTF8_2B_INDEX_2_OFFSET + UTRIE2_UTF8_2B_INDEX_2_LENGTH; - - /** - * 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. - */ - static final int UTRIE2_BAD_UTF8_DATA_OFFSET = 0x80; - - /** - * Implementation class for an iterator over a Trie2. - * - * Iteration over a Trie2 first returns all of the ranges that are indexed by - * code points, then returns the special alternate values for the lead - * surrogates - * - * @internal - */ - class Trie2Iterator implements Iterator { - - // The normal constructor that configures the iterator to cover the complete - // contents of the Trie2 - Trie2Iterator(ValueMapper vm) { - mapper = vm; - nextStart = 0; - limitCP = 0x110000; - doLeadSurrogates = true; - } - - /** - * The main next() function for Trie2 iterators - * - */ - public Range next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - if (nextStart >= limitCP) { - // Switch over from iterating normal code point values to - // doing the alternate lead-surrogate values. - doingCodePoints = false; - nextStart = 0xd800; - } - int endOfRange = 0; - int val = 0; - int mappedVal = 0; - - if (doingCodePoints) { - // Iteration over code point values. - val = get(nextStart); - mappedVal = mapper.map(val); - endOfRange = rangeEnd(nextStart, limitCP, val); - // Loop once for each range in the Trie2 with the same raw (unmapped) value. - // Loop continues so long as the mapped values are the same. - for (;;) { - if (endOfRange >= limitCP - 1) { - break; - } - val = get(endOfRange + 1); - if (mapper.map(val) != mappedVal) { - break; - } - endOfRange = rangeEnd(endOfRange + 1, limitCP, val); - } - } else { - // Iteration over the alternate lead surrogate values. - val = getFromU16SingleLead((char) nextStart); - mappedVal = mapper.map(val); - endOfRange = rangeEndLS((char) nextStart); - // Loop once for each range in the Trie2 with the same raw (unmapped) value. - // Loop continues so long as the mapped values are the same. - for (;;) { - if (endOfRange >= 0xdbff) { - break; - } - val = getFromU16SingleLead((char) (endOfRange + 1)); - if (mapper.map(val) != mappedVal) { - break; - } - endOfRange = rangeEndLS((char) (endOfRange + 1)); - } - } - returnValue.startCodePoint = nextStart; - returnValue.endCodePoint = endOfRange; - returnValue.value = mappedVal; - returnValue.leadSurrogate = !doingCodePoints; - nextStart = endOfRange + 1; - return returnValue; - } - - /** - * - */ - public boolean hasNext() { - return doingCodePoints && (doLeadSurrogates || nextStart < limitCP) || nextStart < 0xdc00; - } - - private int rangeEndLS(char startingLS) { - if (startingLS >= 0xdbff) { - return 0xdbff; - } - - int c; - int val = getFromU16SingleLead(startingLS); - for (c = startingLS + 1; c <= 0x0dbff; c++) { - if (getFromU16SingleLead((char) c) != val) { - break; - } - } - return c - 1; - } - - // - // Iteration State Variables - // - private ValueMapper mapper; - private Range returnValue = new Range(); - // The starting code point for the next range to be returned. - private int nextStart; - // The upper limit for the last normal range to be returned. Normally 0x110000, - // but - // may be lower when iterating over the code points for a single lead surrogate. - private int limitCP; - - // True while iterating over the Trie2 values for code points. - // False while iterating over the alternate values for lead surrogates. - private boolean doingCodePoints = true; - - // True if the iterator should iterate the special values for lead surrogates in - // addition to the normal values for code points. - private boolean doLeadSurrogates = true; - } - - /** - * Find the last character in a contiguous range of characters with the same - * Trie2 value as the input character. - * - * @param c The character to begin with. - * @return The last contiguous character with the same value. - */ - int rangeEnd(int start, int limitp, int val) { - int c; - int limit = Math.min(highStart, limitp); - - for (c = start + 1; c < limit; c++) { - if (get(c) != val) { - break; - } - } - if (c >= highStart) { - c = limitp; - } - return c - 1; - } - - // - // Hashing implementation functions. FNV hash. Respected public domain - // algorithm. - // - private static int initHash() { - return 0x811c9DC5; // unsigned 2166136261 - } - - private static int hashByte(int h, int b) { - h = h * 16777619; - h = h ^ b; - return h; - } - - private static int hashUChar32(int h, int c) { - h = Trie2.hashByte(h, c & 255); - h = Trie2.hashByte(h, (c >> 8) & 255); - h = Trie2.hashByte(h, c >> 16); - return h; - } - - private static int hashInt(int h, int i) { - h = Trie2.hashByte(h, i & 255); - h = Trie2.hashByte(h, (i >> 8) & 255); - h = Trie2.hashByte(h, (i >> 16) & 255); - h = Trie2.hashByte(h, (i >> 24) & 255); - return h; - } - -} +/* + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ******************************************************************************* + * Copyright (C) 2009-2014, International Business Machines Corporation and + * others. All Rights Reserved. + ******************************************************************************* + */ + +package jdk_internal.icu.impl; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * This is the interface and common implementation of a Unicode Trie2. It is a + * kind of compressed table that maps from Unicode code points (0..0x10ffff) to + * 16- or 32-bit integer values. It works best when there are ranges of + * characters with the same value, which is generally the case with Unicode + * character properties. + * + * This is the second common version of a Unicode trie (hence the name Trie2). + * + */ +abstract class Trie2 implements Iterable { + + /** + * Create a Trie2 from its serialized form. Inverse of utrie2_serialize(). + * + * Reads from the current position and leaves the buffer after the end of the + * trie. + * + * The serialized format is identical between ICU4C and ICU4J, so this function + * will work with serialized Trie2s from either. + * + * The actual type of the returned Trie2 will be either Trie2_16 or Trie2_32, + * depending on the width of the data. + * + * To obtain the width of the Trie2, check the actual class type of the returned + * Trie2. Or use the createFromSerialized() function of Trie2_16 or Trie2_32, + * which will return only Tries of their specific type/size. + * + * The serialized Trie2 on the stream may be in either little or big endian byte + * order. This allows using serialized Tries from ICU4C without needing to + * consider the byte order of the system that created them. + * + * @param bytes a byte buffer to the serialized form of a UTrie2. + * @return An unserialized Trie2, ready for use. + * @throws IllegalArgumentException if the stream does not contain a serialized + * Trie2. + * @throws IOException if a read error occurs in the buffer. + * + */ + public static Trie2 createFromSerialized(ByteBuffer bytes) throws IOException { + // From ICU4C utrie2_impl.h + // * Trie2 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; + + ByteOrder outerByteOrder = bytes.order(); + try { + UTrie2Header header = new UTrie2Header(); + + /* check the signature */ + header.signature = bytes.getInt(); + switch (header.signature) { + case 0x54726932: + // The buffer is already set to the trie data byte order. + break; + case 0x32697254: + // Temporarily reverse the byte order. + boolean isBigEndian = outerByteOrder == ByteOrder.BIG_ENDIAN; + bytes.order(isBigEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); + header.signature = 0x54726932; + break; + default: + throw new IllegalArgumentException("Buffer does not contain a serialized UTrie2"); + } + + header.options = bytes.getChar(); + header.indexLength = bytes.getChar(); + header.shiftedDataLength = bytes.getChar(); + header.index2NullOffset = bytes.getChar(); + header.dataNullOffset = bytes.getChar(); + header.shiftedHighStart = bytes.getChar(); + + if ((header.options & UTRIE2_OPTIONS_VALUE_BITS_MASK) != 0) { + throw new IllegalArgumentException("UTrie2 serialized format error."); + } + + Trie2 This; + This = new Trie2_16(); + This.header = header; + + /* get the length values and offsets */ + This.indexLength = header.indexLength; + This.dataLength = header.shiftedDataLength << UTRIE2_INDEX_SHIFT; + This.index2NullOffset = header.index2NullOffset; + This.dataNullOffset = header.dataNullOffset; + This.highStart = header.shiftedHighStart << UTRIE2_SHIFT_1; + This.highValueIndex = This.dataLength - UTRIE2_DATA_GRANULARITY; + This.highValueIndex += This.indexLength; + + // Allocate the Trie2 index array. If the data width is 16 bits, the array also + // includes the space for the data. + + int indexArraySize = This.indexLength; + indexArraySize += This.dataLength; + This.index = new char[indexArraySize]; + + /* Read in the index */ + int i; + for (i = 0; i < This.indexLength; i++) { + This.index[i] = bytes.getChar(); + } + + /* + * Read in the data. 16 bit data goes in the same array as the index. 32 bit + * data goes in its own separate data array. + */ + This.data16 = This.indexLength; + for (i = 0; i < This.dataLength; i++) { + This.index[This.data16 + i] = bytes.getChar(); + } + + This.data32 = null; + This.initialValue = This.index[This.dataNullOffset]; + This.errorValue = This.index[This.data16 + UTRIE2_BAD_UTF8_DATA_OFFSET]; + + return This; + } finally { + bytes.order(outerByteOrder); + } + } + + /** + * Get the value for a code point as stored in the Trie2. + * + * @param codePoint the code point + * @return the value + */ + public abstract int get(int codePoint); + + /** + * Get the trie value for a UTF-16 code unit. + * + * A Trie2 stores two distinct values for input in the lead surrogate range, one + * for lead surrogates, which is the value that will be returned by this + * function, and a second value that is returned by Trie2.get(). + * + * For code units outside of the lead surrogate range, this function returns the + * same result as Trie2.get(). + * + * This function, together with the alternate value for lead surrogates, makes + * possible very efficient processing of UTF-16 strings without first converting + * surrogate pairs to their corresponding 32 bit code point values. + * + * At build-time, enumerate the contents of the Trie2 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 + * Trie2Writable.setForLeadSurrogateCodeUnit(). + * + * At runtime, use Trie2.getFromU16SingleLead(). 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 and look up its value + * with Trie2.get(); 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. + * + * @param c the code point or lead surrogate value. + * @return the value + */ + public abstract int getFromU16SingleLead(char c); + + /** + * When iterating over the contents of a Trie2, Elements of this type are + * produced. The iterator will return one item for each contiguous range of + * codepoints having the same value. + * + * When iterating, the same Trie2EnumRange object will be reused and returned + * for each range. If you need to retain complete iteration results, clone each + * returned Trie2EnumRange, or save the range in some other way, before + * advancing to the next iteration step. + */ + public static class Range { + public int startCodePoint; + public int endCodePoint; // Inclusive. + public int value; + public boolean leadSurrogate; + + public boolean equals(Object other) { + if (other == null || !(other.getClass().equals(getClass()))) { + return false; + } + Range tother = (Range) other; + return this.startCodePoint == tother.startCodePoint && this.endCodePoint == tother.endCodePoint + && this.value == tother.value && this.leadSurrogate == tother.leadSurrogate; + } + + public int hashCode() { + int h = initHash(); + h = hashUChar32(h, startCodePoint); + h = hashUChar32(h, endCodePoint); + h = hashInt(h, value); + h = hashByte(h, leadSurrogate ? 1 : 0); + return h; + } + } + + /** + * Create an iterator over the value ranges in this Trie2. Values from the Trie2 + * are not remapped or filtered, but are returned as they are stored in the + * Trie2. + * + * @return an Iterator + */ + public Iterator iterator() { + return iterator(defaultValueMapper); + } + + private static ValueMapper defaultValueMapper = new ValueMapper() { + public int map(int in) { + return in; + } + }; + + /** + * Create an iterator over the value ranges from this Trie2. Values from the + * Trie2 are passed through a caller-supplied remapping function, and it is the + * remapped values that determine the ranges that will be produced by the + * iterator. + * + * + * @param mapper provides a function to remap values obtained from the Trie2. + * @return an Iterator + */ + public Iterator iterator(ValueMapper mapper) { + return new Trie2Iterator(mapper); + } + + /** + * When iterating over the contents of a Trie2, an instance of TrieValueMapper + * may be used to remap the values from the Trie2. The remapped values will be + * used both in determining the ranges of codepoints and as the value to be + * returned for each range. + * + * Example of use, with an anonymous subclass of TrieValueMapper: + * + * + * ValueMapper m = new ValueMapper() { int map(int in) {return in & 0x1f;}; } + * for (Iterator iter = trie.iterator(m); i.hasNext(); ) { + * Trie2EnumRange r = i.next(); ... // Do something with the range r. } + * + */ + public interface ValueMapper { + public int map(int originalVal); + } + + // -------------------------------------------------------------------------------- + // + // Below this point are internal implementation items. No further public API. + // + // -------------------------------------------------------------------------------- + + /** + * Trie2 data structure in serialized form: + * + * UTrie2Header header; uint16_t index[header.index2Length]; uint16_t + * data[header.shiftedDataLength<<2]; -- or uint32_t data[...] + * + * For Java, this is read from the stream into an instance of UTrie2Header. (The + * C version just places a struct over the raw serialized data.) + * + * @internal + */ + static class UTrie2Header { + /** "Tri2" in big-endian US-ASCII (0x54726932) */ + int signature; + + /** + * options bit field (uint16_t): 15.. 4 reserved (0) 3.. 0 UTrie2ValueBits + * valueBits + */ + int options; + + /** UTRIE2_INDEX_1_OFFSET..UTRIE2_MAX_INDEX_LENGTH (uint16_t) */ + int indexLength; + + /** + * (UTRIE2_DATA_START_OFFSET..UTRIE2_MAX_DATA_LENGTH)>>UTRIE2_INDEX_SHIFT + * (uint16_t) + */ + int shiftedDataLength; + + /** Null index and data blocks, not shifted. (uint16_t) */ + int 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) + */ + int shiftedHighStart; + } + + // + // Data members of UTrie2. + // + UTrie2Header header; + char index[]; // Index array. Includes data for 16 bit Tries. + int data16; // Offset to data portion of the index array, if 16 bit data. + // zero if 32 bit data. + int data32[]; // NULL if 16b data is used via index + + int indexLength; + int dataLength; + int index2NullOffset; // 0xffff if there is no dedicated index-2 null block + int initialValue; + + /** Value returned for out-of-range code points and illegal UTF-8. */ + int errorValue; + + /* Start of the last range which ends at U+10ffff, and its value. */ + int highStart; + int highValueIndex; + + int dataNullOffset; + + /** + * Trie2 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. + */ + + static final int UTRIE2_OPTIONS_VALUE_BITS_MASK = 0x000f; + + /** Shift size for getting the index-1 table offset. */ + static final int UTRIE2_SHIFT_1 = 6 + 5; + + /** Shift size for getting the index-2 table offset. */ + static final int UTRIE2_SHIFT_2 = 5; + + /** + * Difference between the two shift sizes, for getting an index-1 offset from an + * index-2 offset. 6=11-5 + */ + static final int 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. + */ + static final int UTRIE2_OMITTED_BMP_INDEX_1_LENGTH = 0x10000 >> UTRIE2_SHIFT_1; + + /** Number of entries in an index-2 block. 64=0x40 */ + static final int UTRIE2_INDEX_2_BLOCK_LENGTH = 1 << UTRIE2_SHIFT_1_2; + + /** Mask for getting the lower bits for the in-index-2-block offset. */ + static final int UTRIE2_INDEX_2_MASK = UTRIE2_INDEX_2_BLOCK_LENGTH - 1; + + /** Number of entries in a data block. 32=0x20 */ + static final int UTRIE2_DATA_BLOCK_LENGTH = 1 << UTRIE2_SHIFT_2; + + /** Mask for getting the lower bits for the in-data-block offset. */ + static final int UTRIE2_DATA_MASK = UTRIE2_DATA_BLOCK_LENGTH - 1; + + /** + * 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 + * data blocks to be aligned by UTRIE2_DATA_GRANULARITY. + */ + static final int UTRIE2_INDEX_SHIFT = 2; + + /** The alignment size of a data block. Also the granularity for compaction. */ + static final int UTRIE2_DATA_GRANULARITY = 1 << UTRIE2_INDEX_SHIFT; + + /** + * 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.) + */ + static final int UTRIE2_LSCP_INDEX_2_OFFSET = 0x10000 >> UTRIE2_SHIFT_2; + static final int UTRIE2_LSCP_INDEX_2_LENGTH = 0x400 >> UTRIE2_SHIFT_2; + + /** Count the lengths of both BMP pieces. 2080=0x820 */ + static final int 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. + */ + static final int UTRIE2_UTF8_2B_INDEX_2_OFFSET = UTRIE2_INDEX_2_BMP_LENGTH; + static final int 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. + */ + static final int UTRIE2_INDEX_1_OFFSET = UTRIE2_UTF8_2B_INDEX_2_OFFSET + UTRIE2_UTF8_2B_INDEX_2_LENGTH; + + /** + * 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. + */ + static final int UTRIE2_BAD_UTF8_DATA_OFFSET = 0x80; + + /** + * Implementation class for an iterator over a Trie2. + * + * Iteration over a Trie2 first returns all of the ranges that are indexed by + * code points, then returns the special alternate values for the lead + * surrogates + * + * @internal + */ + class Trie2Iterator implements Iterator { + + // The normal constructor that configures the iterator to cover the complete + // contents of the Trie2 + Trie2Iterator(ValueMapper vm) { + mapper = vm; + nextStart = 0; + limitCP = 0x110000; + doLeadSurrogates = true; + } + + /** + * The main next() function for Trie2 iterators + * + */ + public Range next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + if (nextStart >= limitCP) { + // Switch over from iterating normal code point values to + // doing the alternate lead-surrogate values. + doingCodePoints = false; + nextStart = 0xd800; + } + int endOfRange = 0; + int val = 0; + int mappedVal = 0; + + if (doingCodePoints) { + // Iteration over code point values. + val = get(nextStart); + mappedVal = mapper.map(val); + endOfRange = rangeEnd(nextStart, limitCP, val); + // Loop once for each range in the Trie2 with the same raw (unmapped) value. + // Loop continues so long as the mapped values are the same. + for (;;) { + if (endOfRange >= limitCP - 1) { + break; + } + val = get(endOfRange + 1); + if (mapper.map(val) != mappedVal) { + break; + } + endOfRange = rangeEnd(endOfRange + 1, limitCP, val); + } + } else { + // Iteration over the alternate lead surrogate values. + val = getFromU16SingleLead((char) nextStart); + mappedVal = mapper.map(val); + endOfRange = rangeEndLS((char) nextStart); + // Loop once for each range in the Trie2 with the same raw (unmapped) value. + // Loop continues so long as the mapped values are the same. + for (;;) { + if (endOfRange >= 0xdbff) { + break; + } + val = getFromU16SingleLead((char) (endOfRange + 1)); + if (mapper.map(val) != mappedVal) { + break; + } + endOfRange = rangeEndLS((char) (endOfRange + 1)); + } + } + returnValue.startCodePoint = nextStart; + returnValue.endCodePoint = endOfRange; + returnValue.value = mappedVal; + returnValue.leadSurrogate = !doingCodePoints; + nextStart = endOfRange + 1; + return returnValue; + } + + /** + * + */ + public boolean hasNext() { + return doingCodePoints && (doLeadSurrogates || nextStart < limitCP) || nextStart < 0xdc00; + } + + private int rangeEndLS(char startingLS) { + if (startingLS >= 0xdbff) { + return 0xdbff; + } + + int c; + int val = getFromU16SingleLead(startingLS); + for (c = startingLS + 1; c <= 0x0dbff; c++) { + if (getFromU16SingleLead((char) c) != val) { + break; + } + } + return c - 1; + } + + // + // Iteration State Variables + // + private ValueMapper mapper; + private Range returnValue = new Range(); + // The starting code point for the next range to be returned. + private int nextStart; + // The upper limit for the last normal range to be returned. Normally 0x110000, + // but + // may be lower when iterating over the code points for a single lead surrogate. + private int limitCP; + + // True while iterating over the Trie2 values for code points. + // False while iterating over the alternate values for lead surrogates. + private boolean doingCodePoints = true; + + // True if the iterator should iterate the special values for lead surrogates in + // addition to the normal values for code points. + private boolean doLeadSurrogates = true; + } + + /** + * Find the last character in a contiguous range of characters with the same + * Trie2 value as the input character. + * + * @param c The character to begin with. + * @return The last contiguous character with the same value. + */ + int rangeEnd(int start, int limitp, int val) { + int c; + int limit = Math.min(highStart, limitp); + + for (c = start + 1; c < limit; c++) { + if (get(c) != val) { + break; + } + } + if (c >= highStart) { + c = limitp; + } + return c - 1; + } + + // + // Hashing implementation functions. FNV hash. Respected public domain + // algorithm. + // + private static int initHash() { + return 0x811c9DC5; // unsigned 2166136261 + } + + private static int hashByte(int h, int b) { + h = h * 16777619; + h = h ^ b; + return h; + } + + private static int hashUChar32(int h, int c) { + h = Trie2.hashByte(h, c & 255); + h = Trie2.hashByte(h, (c >> 8) & 255); + h = Trie2.hashByte(h, c >> 16); + return h; + } + + private static int hashInt(int h, int i) { + h = Trie2.hashByte(h, i & 255); + h = Trie2.hashByte(h, (i >> 8) & 255); + h = Trie2.hashByte(h, (i >> 16) & 255); + h = Trie2.hashByte(h, (i >> 24) & 255); + return h; + } + +} diff --git a/src/main/java/jdk_internal/icu/impl/Trie2_16.java b/src/main/java/jdk_internal/icu/impl/Trie2_16.java index 632ac070..57a3b6e3 100755 --- a/src/main/java/jdk_internal/icu/impl/Trie2_16.java +++ b/src/main/java/jdk_internal/icu/impl/Trie2_16.java @@ -1,170 +1,170 @@ -/* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ******************************************************************************* - * Copyright (C) 2009-2014, International Business Machines Corporation and - * others. All Rights Reserved. - ******************************************************************************* - */ - -package jdk_internal.icu.impl; - -import java.io.IOException; -import java.nio.ByteBuffer; - -/** - * @author aheninger - * - * A read-only Trie2, holding 16 bit data values. - * - * A Trie2 is a highly optimized data structure for mapping from Unicode - * code points (values ranging from 0 to 0x10ffff) to a 16 or 32 bit - * value. - * - * See class Trie2 for descriptions of the API for accessing the - * contents of a trie. - * - * The fundamental data access methods are declared final in this class, - * with the intent that applications might gain a little extra - * performance, when compared with calling the same methods via the - * abstract UTrie2 base class. - */ -public final class Trie2_16 extends Trie2 { - - /** - * Internal constructor, not for general use. - */ - Trie2_16() { - } - - /** - * Create a Trie2 from its serialized form. Inverse of utrie2_serialize(). The - * serialized format is identical between ICU4C and ICU4J, so this function will - * work with serialized Trie2s from either. - * - * The serialized Trie2 in the bytes may be in either little or big endian byte - * order. This allows using serialized Tries from ICU4C without needing to - * consider the byte order of the system that created them. - * - * @param bytes a byte buffer to the serialized form of a UTrie2. - * @return An unserialized Trie2_16, ready for use. - * @throws IllegalArgumentException if the buffer does not contain a serialized - * Trie2. - * @throws IOException if a read error occurs in the buffer. - * @throws ClassCastException if the bytes contain a serialized Trie2_32 - */ - public static Trie2_16 createFromSerialized(ByteBuffer bytes) throws IOException { - return (Trie2_16) Trie2.createFromSerialized(bytes); - } - - /** - * Get the value for a code point as stored in the Trie2. - * - * @param codePoint the code point - * @return the value - */ - @Override - public final int get(int codePoint) { - int value; - int ix; - - if (codePoint >= 0) { - if (codePoint < 0x0d800 || (codePoint > 0x0dbff && codePoint <= 0x0ffff)) { - // Ordinary BMP code point, excluding leading surrogates. - // BMP uses a single level lookup. BMP index starts at offset 0 in the Trie2 - // index. - // 16 bit data is stored in the index array itself. - ix = index[codePoint >> UTRIE2_SHIFT_2]; - ix = (ix << UTRIE2_INDEX_SHIFT) + (codePoint & UTRIE2_DATA_MASK); - value = index[ix]; - return value; - } - if (codePoint <= 0xffff) { - // Lead Surrogate Code Point. A Separate index section is stored for - // lead surrogate code units and code points. - // The main index has the code unit data. - // For this function, we need the code point data. - // Note: this expression could be refactored for slightly improved efficiency, - // but - // surrogate code points will be so rare in practice that it's not worth it. - ix = index[UTRIE2_LSCP_INDEX_2_OFFSET + ((codePoint - 0xd800) >> UTRIE2_SHIFT_2)]; - ix = (ix << UTRIE2_INDEX_SHIFT) + (codePoint & UTRIE2_DATA_MASK); - value = index[ix]; - return value; - } - if (codePoint < highStart) { - // Supplemental code point, use two-level lookup. - ix = (UTRIE2_INDEX_1_OFFSET - UTRIE2_OMITTED_BMP_INDEX_1_LENGTH) + (codePoint >> UTRIE2_SHIFT_1); - ix = index[ix]; - ix += (codePoint >> UTRIE2_SHIFT_2) & UTRIE2_INDEX_2_MASK; - ix = index[ix]; - ix = (ix << UTRIE2_INDEX_SHIFT) + (codePoint & UTRIE2_DATA_MASK); - value = index[ix]; - return value; - } - if (codePoint <= 0x10ffff) { - value = index[highValueIndex]; - return value; - } - } - - // Fall through. The code point is outside of the legal range of 0..0x10ffff. - return errorValue; - } - - /** - * Get a Trie2 value for a UTF-16 code unit. - * - * This function returns the same value as get() if the input character is - * outside of the lead surrogate range - * - * There are two values stored in a Trie2 for inputs in the lead surrogate - * range. This function returns the alternate value, while Trie2.get() returns - * the main value. - * - * @param codeUnit a 16 bit code unit or lead surrogate value. - * @return the value - */ - @Override - public int getFromU16SingleLead(char codeUnit) { - int value; - int ix; - - // Because the input is a 16 bit char, we can skip the tests for it being in - // the BMP range. It is. - ix = index[codeUnit >> UTRIE2_SHIFT_2]; - ix = (ix << UTRIE2_INDEX_SHIFT) + (codeUnit & UTRIE2_DATA_MASK); - value = index[ix]; - return value; - } - - /** - * @return the number of bytes of the serialized trie - */ - public int getSerializedLength() { - return 16 + (header.indexLength + dataLength) * 2; - } -} +/* + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ******************************************************************************* + * Copyright (C) 2009-2014, International Business Machines Corporation and + * others. All Rights Reserved. + ******************************************************************************* + */ + +package jdk_internal.icu.impl; + +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + * @author aheninger + * + * A read-only Trie2, holding 16 bit data values. + * + * A Trie2 is a highly optimized data structure for mapping from Unicode + * code points (values ranging from 0 to 0x10ffff) to a 16 or 32 bit + * value. + * + * See class Trie2 for descriptions of the API for accessing the + * contents of a trie. + * + * The fundamental data access methods are declared final in this class, + * with the intent that applications might gain a little extra + * performance, when compared with calling the same methods via the + * abstract UTrie2 base class. + */ +public final class Trie2_16 extends Trie2 { + + /** + * Internal constructor, not for general use. + */ + Trie2_16() { + } + + /** + * Create a Trie2 from its serialized form. Inverse of utrie2_serialize(). The + * serialized format is identical between ICU4C and ICU4J, so this function will + * work with serialized Trie2s from either. + * + * The serialized Trie2 in the bytes may be in either little or big endian byte + * order. This allows using serialized Tries from ICU4C without needing to + * consider the byte order of the system that created them. + * + * @param bytes a byte buffer to the serialized form of a UTrie2. + * @return An unserialized Trie2_16, ready for use. + * @throws IllegalArgumentException if the buffer does not contain a serialized + * Trie2. + * @throws IOException if a read error occurs in the buffer. + * @throws ClassCastException if the bytes contain a serialized Trie2_32 + */ + public static Trie2_16 createFromSerialized(ByteBuffer bytes) throws IOException { + return (Trie2_16) Trie2.createFromSerialized(bytes); + } + + /** + * Get the value for a code point as stored in the Trie2. + * + * @param codePoint the code point + * @return the value + */ + @Override + public final int get(int codePoint) { + int value; + int ix; + + if (codePoint >= 0) { + if (codePoint < 0x0d800 || (codePoint > 0x0dbff && codePoint <= 0x0ffff)) { + // Ordinary BMP code point, excluding leading surrogates. + // BMP uses a single level lookup. BMP index starts at offset 0 in the Trie2 + // index. + // 16 bit data is stored in the index array itself. + ix = index[codePoint >> UTRIE2_SHIFT_2]; + ix = (ix << UTRIE2_INDEX_SHIFT) + (codePoint & UTRIE2_DATA_MASK); + value = index[ix]; + return value; + } + if (codePoint <= 0xffff) { + // Lead Surrogate Code Point. A Separate index section is stored for + // lead surrogate code units and code points. + // The main index has the code unit data. + // For this function, we need the code point data. + // Note: this expression could be refactored for slightly improved efficiency, + // but + // surrogate code points will be so rare in practice that it's not worth it. + ix = index[UTRIE2_LSCP_INDEX_2_OFFSET + ((codePoint - 0xd800) >> UTRIE2_SHIFT_2)]; + ix = (ix << UTRIE2_INDEX_SHIFT) + (codePoint & UTRIE2_DATA_MASK); + value = index[ix]; + return value; + } + if (codePoint < highStart) { + // Supplemental code point, use two-level lookup. + ix = (UTRIE2_INDEX_1_OFFSET - UTRIE2_OMITTED_BMP_INDEX_1_LENGTH) + (codePoint >> UTRIE2_SHIFT_1); + ix = index[ix]; + ix += (codePoint >> UTRIE2_SHIFT_2) & UTRIE2_INDEX_2_MASK; + ix = index[ix]; + ix = (ix << UTRIE2_INDEX_SHIFT) + (codePoint & UTRIE2_DATA_MASK); + value = index[ix]; + return value; + } + if (codePoint <= 0x10ffff) { + value = index[highValueIndex]; + return value; + } + } + + // Fall through. The code point is outside of the legal range of 0..0x10ffff. + return errorValue; + } + + /** + * Get a Trie2 value for a UTF-16 code unit. + * + * This function returns the same value as get() if the input character is + * outside of the lead surrogate range + * + * There are two values stored in a Trie2 for inputs in the lead surrogate + * range. This function returns the alternate value, while Trie2.get() returns + * the main value. + * + * @param codeUnit a 16 bit code unit or lead surrogate value. + * @return the value + */ + @Override + public int getFromU16SingleLead(char codeUnit) { + int value; + int ix; + + // Because the input is a 16 bit char, we can skip the tests for it being in + // the BMP range. It is. + ix = index[codeUnit >> UTRIE2_SHIFT_2]; + ix = (ix << UTRIE2_INDEX_SHIFT) + (codeUnit & UTRIE2_DATA_MASK); + value = index[ix]; + return value; + } + + /** + * @return the number of bytes of the serialized trie + */ + public int getSerializedLength() { + return 16 + (header.indexLength + dataLength) * 2; + } +} diff --git a/src/main/java/jdk_internal/icu/impl/UBiDiProps.java b/src/main/java/jdk_internal/icu/impl/UBiDiProps.java index 70331b71..3a78cac7 100755 --- a/src/main/java/jdk_internal/icu/impl/UBiDiProps.java +++ b/src/main/java/jdk_internal/icu/impl/UBiDiProps.java @@ -1,271 +1,271 @@ -/* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -/* - ******************************************************************************* - * - * Copyright (C) 2004-2014, International Business Machines - * Corporation and others. All Rights Reserved. - * - ******************************************************************************* - * file name: UBiDiProps.java - * encoding: US-ASCII - * tab size: 8 (not used) - * indentation:4 - * - * created on: 2005jan16 - * created by: Markus W. Scherer - * - * Low-level Unicode bidi/shaping properties access. - * Java port of ubidi_props.h/.c. - */ - -package jdk_internal.icu.impl; - -import java.io.IOException; -import java.nio.ByteBuffer; - -import jdk_internal.icu.lang.UCharacter; - -public final class UBiDiProps { - // constructors etc. --------------------------------------------------- *** - - // port of ubidi_openProps() - private UBiDiProps() throws IOException { - ByteBuffer bytes = ICUBinary.getRequiredData(DATA_FILE_NAME); - readData(bytes); - } - - private void readData(ByteBuffer bytes) throws IOException { - // read the header - ICUBinary.readHeader(bytes, FMT, new IsAcceptable()); - - // read indexes[] - int i, count; - count = bytes.getInt(); - if (count < IX_TOP) { - throw new IOException("indexes[0] too small in " + DATA_FILE_NAME); - } - indexes = new int[count]; - - indexes[0] = count; - for (i = 1; i < count; ++i) { - indexes[i] = bytes.getInt(); - } - - // read the trie - trie = Trie2_16.createFromSerialized(bytes); - int expectedTrieLength = indexes[IX_TRIE_SIZE]; - int trieLength = trie.getSerializedLength(); - if (trieLength > expectedTrieLength) { - throw new IOException(DATA_FILE_NAME + ": not enough bytes for the trie"); - } - // skip padding after trie bytes - ICUBinary.skipBytes(bytes, expectedTrieLength - trieLength); - - // read mirrors[] - count = indexes[IX_MIRROR_LENGTH]; - if (count > 0) { - mirrors = new int[count]; - for (i = 0; i < count; ++i) { - mirrors[i] = bytes.getInt(); - } - } - - // read jgArray[] - count = indexes[IX_JG_LIMIT] - indexes[IX_JG_START]; - jgArray = new byte[count]; - for (i = 0; i < count; ++i) { - jgArray[i] = bytes.get(); - } - - // read jgArray2[] - count = indexes[IX_JG_LIMIT2] - indexes[IX_JG_START2]; - jgArray2 = new byte[count]; - for (i = 0; i < count; ++i) { - jgArray2[i] = bytes.get(); - } - } - - // implement ICUBinary.Authenticate - private static final class IsAcceptable implements ICUBinary.Authenticate { - public boolean isDataVersionAcceptable(byte version[]) { - return version[0] == 2; - } - } - - // property access functions ------------------------------------------- *** - - public final int getClass(int c) { - return getClassFromProps(trie.get(c)); - } - - private final int getMirror(int c, int props) { - int delta = getMirrorDeltaFromProps(props); - if (delta != ESC_MIRROR_DELTA) { - return c + delta; - } else { - /* look for mirror code point in the mirrors[] table */ - int m; - int i, length; - int c2; - - length = indexes[IX_MIRROR_LENGTH]; - - /* linear search */ - for (i = 0; i < length; ++i) { - m = mirrors[i]; - c2 = getMirrorCodePoint(m); - if (c == c2) { - /* found c, return its mirror code point using the index in m */ - return getMirrorCodePoint(mirrors[getMirrorIndex(m)]); - } else if (c < c2) { - break; - } - } - - /* c not found, return it itself */ - return c; - } - } - - public final int getMirror(int c) { - int props = trie.get(c); - return getMirror(c, props); - } - - public final int getJoiningType(int c) { - return (trie.get(c) & JT_MASK) >> JT_SHIFT; - } - - public final int getJoiningGroup(int c) { - int start, limit; - - start = indexes[IX_JG_START]; - limit = indexes[IX_JG_LIMIT]; - if (start <= c && c < limit) { - return (int) jgArray[c - start] & 0xff; - } - start = indexes[IX_JG_START2]; - limit = indexes[IX_JG_LIMIT2]; - if (start <= c && c < limit) { - return (int) jgArray2[c - start] & 0xff; - } - return UCharacter.JoiningGroup.NO_JOINING_GROUP; - } - - public final int getPairedBracketType(int c) { - return (trie.get(c) & BPT_MASK) >> BPT_SHIFT; - } - - public final int getPairedBracket(int c) { - int props = trie.get(c); - if ((props & BPT_MASK) == 0) { - return c; - } else { - return getMirror(c, props); - } - } - - // data members -------------------------------------------------------- *** - private int indexes[]; - private int mirrors[]; - private byte jgArray[]; - private byte jgArray2[]; - - private Trie2_16 trie; - - // data format constants ----------------------------------------------- *** - @SuppressWarnings("deprecation") - private static final String DATA_FILE_NAME = "/assets/eagler/icudt/ubidi.icu"; - - /* format "BiDi" */ - private static final int FMT = 0x42694469; - - /* indexes into indexes[] */ - private static final int IX_TRIE_SIZE = 2; - private static final int IX_MIRROR_LENGTH = 3; - - private static final int IX_JG_START = 4; - private static final int IX_JG_LIMIT = 5; - private static final int IX_JG_START2 = 6; /* new in format version 2.2, ICU 54 */ - private static final int IX_JG_LIMIT2 = 7; - - private static final int IX_TOP = 16; - - // definitions for 16-bit bidi/shaping properties word ----------------- *** - - /* CLASS_SHIFT=0, */ /* bidi class: 5 bits (4..0) */ - private static final int JT_SHIFT = 5; /* joining type: 3 bits (7..5) */ - - private static final int BPT_SHIFT = 8; /* Bidi_Paired_Bracket_Type(bpt): 2 bits (9..8) */ - - private static final int MIRROR_DELTA_SHIFT = 13; /* bidi mirroring delta: 3 bits (15..13) */ - - private static final int CLASS_MASK = 0x0000001f; - private static final int JT_MASK = 0x000000e0; - private static final int BPT_MASK = 0x00000300; - - private static final int getClassFromProps(int props) { - return props & CLASS_MASK; - } - - private static final boolean getFlagFromProps(int props, int shift) { - return ((props >> shift) & 1) != 0; - } - - private static final int getMirrorDeltaFromProps(int props) { - return (short) props >> MIRROR_DELTA_SHIFT; - } - - private static final int ESC_MIRROR_DELTA = -4; - - // definitions for 32-bit mirror table entry --------------------------- *** - - /* the source Unicode code point takes 21 bits (20..0) */ - private static final int MIRROR_INDEX_SHIFT = 21; - - private static final int getMirrorCodePoint(int m) { - return m & 0x1fffff; - } - - private static final int getMirrorIndex(int m) { - return m >>> MIRROR_INDEX_SHIFT; - } - - /* - * public singleton instance - */ - public static final UBiDiProps INSTANCE; - - // This static initializer block must be placed after - // other static member initialization - static { - try { - INSTANCE = new UBiDiProps(); - } catch (IOException e) { - throw new RuntimeException("Missing resource: \"" + DATA_FILE_NAME + "\"; Reason: " + e.getMessage()); - } - } -} +/* + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + ******************************************************************************* + * + * Copyright (C) 2004-2014, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: UBiDiProps.java + * encoding: US-ASCII + * tab size: 8 (not used) + * indentation:4 + * + * created on: 2005jan16 + * created by: Markus W. Scherer + * + * Low-level Unicode bidi/shaping properties access. + * Java port of ubidi_props.h/.c. + */ + +package jdk_internal.icu.impl; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import jdk_internal.icu.lang.UCharacter; + +public final class UBiDiProps { + // constructors etc. --------------------------------------------------- *** + + // port of ubidi_openProps() + private UBiDiProps() throws IOException { + ByteBuffer bytes = ICUBinary.getRequiredData(DATA_FILE_NAME); + readData(bytes); + } + + private void readData(ByteBuffer bytes) throws IOException { + // read the header + ICUBinary.readHeader(bytes, FMT, new IsAcceptable()); + + // read indexes[] + int i, count; + count = bytes.getInt(); + if (count < IX_TOP) { + throw new IOException("indexes[0] too small in " + DATA_FILE_NAME); + } + indexes = new int[count]; + + indexes[0] = count; + for (i = 1; i < count; ++i) { + indexes[i] = bytes.getInt(); + } + + // read the trie + trie = Trie2_16.createFromSerialized(bytes); + int expectedTrieLength = indexes[IX_TRIE_SIZE]; + int trieLength = trie.getSerializedLength(); + if (trieLength > expectedTrieLength) { + throw new IOException(DATA_FILE_NAME + ": not enough bytes for the trie"); + } + // skip padding after trie bytes + ICUBinary.skipBytes(bytes, expectedTrieLength - trieLength); + + // read mirrors[] + count = indexes[IX_MIRROR_LENGTH]; + if (count > 0) { + mirrors = new int[count]; + for (i = 0; i < count; ++i) { + mirrors[i] = bytes.getInt(); + } + } + + // read jgArray[] + count = indexes[IX_JG_LIMIT] - indexes[IX_JG_START]; + jgArray = new byte[count]; + for (i = 0; i < count; ++i) { + jgArray[i] = bytes.get(); + } + + // read jgArray2[] + count = indexes[IX_JG_LIMIT2] - indexes[IX_JG_START2]; + jgArray2 = new byte[count]; + for (i = 0; i < count; ++i) { + jgArray2[i] = bytes.get(); + } + } + + // implement ICUBinary.Authenticate + private static final class IsAcceptable implements ICUBinary.Authenticate { + public boolean isDataVersionAcceptable(byte version[]) { + return version[0] == 2; + } + } + + // property access functions ------------------------------------------- *** + + public final int getClass(int c) { + return getClassFromProps(trie.get(c)); + } + + private final int getMirror(int c, int props) { + int delta = getMirrorDeltaFromProps(props); + if (delta != ESC_MIRROR_DELTA) { + return c + delta; + } else { + /* look for mirror code point in the mirrors[] table */ + int m; + int i, length; + int c2; + + length = indexes[IX_MIRROR_LENGTH]; + + /* linear search */ + for (i = 0; i < length; ++i) { + m = mirrors[i]; + c2 = getMirrorCodePoint(m); + if (c == c2) { + /* found c, return its mirror code point using the index in m */ + return getMirrorCodePoint(mirrors[getMirrorIndex(m)]); + } else if (c < c2) { + break; + } + } + + /* c not found, return it itself */ + return c; + } + } + + public final int getMirror(int c) { + int props = trie.get(c); + return getMirror(c, props); + } + + public final int getJoiningType(int c) { + return (trie.get(c) & JT_MASK) >> JT_SHIFT; + } + + public final int getJoiningGroup(int c) { + int start, limit; + + start = indexes[IX_JG_START]; + limit = indexes[IX_JG_LIMIT]; + if (start <= c && c < limit) { + return (int) jgArray[c - start] & 0xff; + } + start = indexes[IX_JG_START2]; + limit = indexes[IX_JG_LIMIT2]; + if (start <= c && c < limit) { + return (int) jgArray2[c - start] & 0xff; + } + return UCharacter.JoiningGroup.NO_JOINING_GROUP; + } + + public final int getPairedBracketType(int c) { + return (trie.get(c) & BPT_MASK) >> BPT_SHIFT; + } + + public final int getPairedBracket(int c) { + int props = trie.get(c); + if ((props & BPT_MASK) == 0) { + return c; + } else { + return getMirror(c, props); + } + } + + // data members -------------------------------------------------------- *** + private int indexes[]; + private int mirrors[]; + private byte jgArray[]; + private byte jgArray2[]; + + private Trie2_16 trie; + + // data format constants ----------------------------------------------- *** + @SuppressWarnings("deprecation") + private static final String DATA_FILE_NAME = "/assets/eagler/icudt/ubidi.icu"; + + /* format "BiDi" */ + private static final int FMT = 0x42694469; + + /* indexes into indexes[] */ + private static final int IX_TRIE_SIZE = 2; + private static final int IX_MIRROR_LENGTH = 3; + + private static final int IX_JG_START = 4; + private static final int IX_JG_LIMIT = 5; + private static final int IX_JG_START2 = 6; /* new in format version 2.2, ICU 54 */ + private static final int IX_JG_LIMIT2 = 7; + + private static final int IX_TOP = 16; + + // definitions for 16-bit bidi/shaping properties word ----------------- *** + + /* CLASS_SHIFT=0, */ /* bidi class: 5 bits (4..0) */ + private static final int JT_SHIFT = 5; /* joining type: 3 bits (7..5) */ + + private static final int BPT_SHIFT = 8; /* Bidi_Paired_Bracket_Type(bpt): 2 bits (9..8) */ + + private static final int MIRROR_DELTA_SHIFT = 13; /* bidi mirroring delta: 3 bits (15..13) */ + + private static final int CLASS_MASK = 0x0000001f; + private static final int JT_MASK = 0x000000e0; + private static final int BPT_MASK = 0x00000300; + + private static final int getClassFromProps(int props) { + return props & CLASS_MASK; + } + + private static final boolean getFlagFromProps(int props, int shift) { + return ((props >> shift) & 1) != 0; + } + + private static final int getMirrorDeltaFromProps(int props) { + return (short) props >> MIRROR_DELTA_SHIFT; + } + + private static final int ESC_MIRROR_DELTA = -4; + + // definitions for 32-bit mirror table entry --------------------------- *** + + /* the source Unicode code point takes 21 bits (20..0) */ + private static final int MIRROR_INDEX_SHIFT = 21; + + private static final int getMirrorCodePoint(int m) { + return m & 0x1fffff; + } + + private static final int getMirrorIndex(int m) { + return m >>> MIRROR_INDEX_SHIFT; + } + + /* + * public singleton instance + */ + public static final UBiDiProps INSTANCE; + + // This static initializer block must be placed after + // other static member initialization + static { + try { + INSTANCE = new UBiDiProps(); + } catch (IOException e) { + throw new RuntimeException("Missing resource: \"" + DATA_FILE_NAME + "\"; Reason: " + e.getMessage()); + } + } +} diff --git a/src/main/java/jdk_internal/icu/impl/UCharacterProperty.java b/src/main/java/jdk_internal/icu/impl/UCharacterProperty.java index 1d838380..63a9154f 100755 --- a/src/main/java/jdk_internal/icu/impl/UCharacterProperty.java +++ b/src/main/java/jdk_internal/icu/impl/UCharacterProperty.java @@ -1,627 +1,627 @@ -/* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -/* - ******************************************************************************* - * Copyright (C) 1996-2014, International Business Machines Corporation and - * others. All Rights Reserved. - ******************************************************************************* - */ - -package jdk_internal.icu.impl; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.Iterator; - -import jdk_internal.icu.lang.UCharacter.HangulSyllableType; -import jdk_internal.icu.lang.UCharacter.NumericType; -import jdk_internal.icu.text.UTF16; -import jdk_internal.icu.text.UnicodeSet; -import jdk_internal.icu.util.VersionInfo; - -/** - *

- * Internal class used for Unicode character property database. - *

- *

- * This classes store binary data read from uprops.icu. It does not have the - * capability to parse the data into more high-level information. It only - * returns bytes of information when required. - *

- *

- * Due to the form most commonly used for retrieval, array of char is used to - * store the binary data. - *

- *

- * UCharacterPropertyDB also contains information on accessing indexes to - * significant points in the binary data. - *

- *

- * Responsibility for molding the binary data into more meaning form lies on - * UCharacter. - *

- * - * @author Syn Wee Quek - * @since release 2.1, february 1st 2002 - */ - -public final class UCharacterProperty { - // public data members ----------------------------------------------- - - /* - * public singleton instance - */ - public static final UCharacterProperty INSTANCE; - - /** - * Trie data - */ - public Trie2_16 m_trie_; - - /** - * Unicode version - */ - public VersionInfo m_unicodeVersion_; - - /** - * Character type mask - */ - public static final int TYPE_MASK = 0x1F; - - // uprops.h enum UPropertySource --------------------------------------- *** - - /** From uchar.c/uprops.icu main trie */ - public static final int SRC_CHAR = 1; - /** From uchar.c/uprops.icu properties vectors trie */ - public static final int SRC_PROPSVEC = 2; - /** From ubidi_props.c/ubidi.icu */ - public static final int SRC_BIDI = 5; - /** From normalizer2impl.cpp/nfc.nrm */ - public static final int SRC_NFC = 8; - /** From normalizer2impl.cpp/nfkc.nrm */ - public static final int SRC_NFKC = 9; - - // public methods ---------------------------------------------------- - - /** - * Gets the main property value for code point ch. - * - * @param ch code point whose property value is to be retrieved - * @return property value of code point - */ - public final int getProperty(int ch) { - return m_trie_.get(ch); - } - - /** - * Gets the unicode additional properties. Java version of C - * u_getUnicodeProperties(). - * - * @param codepoint codepoint whose additional properties is to be retrieved - * @param column The column index. - * @return unicode properties - */ - public int getAdditional(int codepoint, int column) { - assert column >= 0; - if (column >= m_additionalColumnsCount_) { - return 0; - } - return m_additionalVectors_[m_additionalTrie_.get(codepoint) + column]; - } - - /** - *

- * 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. - *

- *

- * This API does not check the validity of the codepoint. - *

- * - * @param codepoint The code point. - * @return the Unicode version number - */ - public VersionInfo getAge(int codepoint) { - int version = getAdditional(codepoint, 0) >> AGE_SHIFT_; - return VersionInfo.getInstance((version >> FIRST_NIBBLE_SHIFT_) & LAST_NIBBLE_MASK_, - version & LAST_NIBBLE_MASK_, 0, 0); - } - - // int-value and enumerated properties --------------------------------- *** - - public int getType(int c) { - return getProperty(c) & TYPE_MASK; - } - - /* - * 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. - */ - private static final int /* UHangulSyllableType */ gcbToHst[] = { HangulSyllableType.NOT_APPLICABLE, /* - * U_GCB_OTHER - */ - HangulSyllableType.NOT_APPLICABLE, /* U_GCB_CONTROL */ - HangulSyllableType.NOT_APPLICABLE, /* U_GCB_CR */ - HangulSyllableType.NOT_APPLICABLE, /* U_GCB_EXTEND */ - HangulSyllableType.LEADING_JAMO, /* U_GCB_L */ - HangulSyllableType.NOT_APPLICABLE, /* U_GCB_LF */ - HangulSyllableType.LV_SYLLABLE, /* U_GCB_LV */ - HangulSyllableType.LVT_SYLLABLE, /* U_GCB_LVT */ - HangulSyllableType.TRAILING_JAMO, /* U_GCB_T */ - HangulSyllableType.VOWEL_JAMO /* U_GCB_V */ - /* - * Omit GCB values beyond what we need for hst. The code below checks for the - * array length. - */ - }; - - private class IntProperty { - int column; // SRC_PROPSVEC column, or "source" if mask==0 - int mask; - int shift; - - IntProperty(int column, int mask, int shift) { - this.column = column; - this.mask = mask; - this.shift = shift; - } - - IntProperty(int source) { - this.column = source; - this.mask = 0; - } - - int getValue(int c) { - // systematic, directly stored properties - return (getAdditional(c, column) & mask) >>> shift; - } - } - - private class BiDiIntProperty extends IntProperty { - BiDiIntProperty() { - super(SRC_BIDI); - } - } - - private class CombiningClassIntProperty extends IntProperty { - CombiningClassIntProperty(int source) { - super(source); - } - } - - private class NormQuickCheckIntProperty extends IntProperty { // UCHAR_NF*_QUICK_CHECK properties - int which; - int max; - - NormQuickCheckIntProperty(int source, int which, int max) { - super(source); - this.which = which; - this.max = max; - } - } - - private IntProperty intProp = new BiDiIntProperty() { // BIDI_PAIRED_BRACKET_TYPE - int getValue(int c) { - return UBiDiProps.INSTANCE.getPairedBracketType(c); - } - }; - - public int getIntPropertyValue(int c, int which) { - if (which == BIDI_PAIRED_BRACKET_TYPE) { - return intProp.getValue(c); - } - return 0; // undefined - } - - /** - * Forms a supplementary code point from the argument character
- * Note this is for internal use hence no checks for the validity of the - * surrogate characters are done - * - * @param lead lead surrogate character - * @param trail trailing surrogate character - * @return code point of the supplementary character - */ - public static int getRawSupplementary(char lead, char trail) { - return (lead << LEAD_SURROGATE_SHIFT_) + trail + SURROGATE_OFFSET_; - } - - /** - * Gets the type mask - * - * @param type character type - * @return mask - */ - public static final int getMask(int type) { - return 1 << type; - } - - /** - * Returns the digit values of characters like 'A' - 'Z', normal, half-width and - * full-width. This method assumes that the other digit characters are checked - * by the calling method. - * - * @param ch character to test - * @return -1 if ch is not a character of the form 'A' - 'Z', otherwise its - * corresponding digit will be returned. - */ - public static int getEuropeanDigit(int ch) { - if ((ch > 0x7a && ch < 0xff21) || ch < 0x41 || (ch > 0x5a && ch < 0x61) || ch > 0xff5a - || (ch > 0xff3a && ch < 0xff41)) { - return -1; - } - if (ch <= 0x7a) { - // ch >= 0x41 or ch < 0x61 - return ch + 10 - ((ch <= 0x5a) ? 0x41 : 0x61); - } - // ch >= 0xff21 - if (ch <= 0xff3a) { - return ch + 10 - 0xff21; - } - // ch >= 0xff41 && ch <= 0xff5a - return ch + 10 - 0xff41; - } - - public int digit(int c) { - int value = getNumericTypeValue(getProperty(c)) - NTV_DECIMAL_START_; - if (value <= 9) { - return value; - } else { - return -1; - } - } - - // protected variables ----------------------------------------------- - - /** - * Extra property trie - */ - Trie2_16 m_additionalTrie_; - /** - * Extra property vectors, 1st column for age and second for binary properties. - */ - int m_additionalVectors_[]; - /** - * Number of additional columns - */ - int m_additionalColumnsCount_; - /** - * Maximum values for block, bits used as in vector word 0 - */ - int m_maxBlockScriptValue_; - /** - * Maximum values for script, bits used as in vector word 0 - */ - int m_maxJTGValue_; - /** - * Script_Extensions data - */ - public char[] m_scriptExtensions_; - - // private variables ------------------------------------------------- - - /** - * Default name of the datafile - */ - @SuppressWarnings("deprecation") - private static final String DATA_FILE_NAME_ = "/assets/eagler/icudt/uprops.icu"; - - /** - * Shift value for lead surrogate to form a supplementary character. - */ - private static final int LEAD_SURROGATE_SHIFT_ = 10; - /** - * Offset to add to combined surrogate pair to avoid masking. - */ - private static final int SURROGATE_OFFSET_ = UTF16.SUPPLEMENTARY_MIN_VALUE - - (UTF16.SURROGATE_MIN_VALUE << LEAD_SURROGATE_SHIFT_) - UTF16.TRAIL_SURROGATE_MIN_VALUE; - - // property data constants ------------------------------------------------- - - /** - * Numeric types and values in the main properties words. - */ - private static final int NUMERIC_TYPE_VALUE_SHIFT_ = 6; - - private static final int getNumericTypeValue(int props) { - return props >> NUMERIC_TYPE_VALUE_SHIFT_; - } - - /* constants for the storage form of numeric types and values */ - /** No numeric value. */ - private static final int NTV_NONE_ = 0; - /** Decimal digits: nv=0..9 */ - private static final int NTV_DECIMAL_START_ = 1; - /** Other digits: nv=0..9 */ - private static final int NTV_DIGIT_START_ = 11; - /** Small integers: nv=0..154 */ - private static final int NTV_NUMERIC_START_ = 21; - - private static final int ntvGetType(int ntv) { - return (ntv == NTV_NONE_) ? NumericType.NONE - : (ntv < NTV_DIGIT_START_) ? NumericType.DECIMAL - : (ntv < NTV_NUMERIC_START_) ? NumericType.DIGIT : NumericType.NUMERIC; - } - - /* - * Properties in vector word 0 Bits 31..24 DerivedAge version major/minor one - * nibble each 23..22 3..1: Bits 21..20 & 7..0 = Script_Extensions index 3: - * Script value from Script_Extensions 2: Script=Inherited 1: Script=Common 0: - * Script=bits 21..20 & 7..0 21..20 Bits 9..8 of the UScriptCode, or index to - * Script_Extensions 19..17 East Asian Width 16.. 8 UBlockCode 7.. 0 - * UScriptCode, or index to Script_Extensions - */ - - /** - * Script_Extensions: mask includes Script - */ - public static final int SCRIPT_X_MASK = 0x00f000ff; - // private static final int SCRIPT_X_SHIFT = 22; - - // The UScriptCode or Script_Extensions index is split across two bit fields. - // (Starting with Unicode 13/ICU 66/2019 due to more varied Script_Extensions.) - // Shift the high bits right by 12 to assemble the full value. - public static final int SCRIPT_HIGH_MASK = 0x00300000; - public static final int SCRIPT_HIGH_SHIFT = 12; - public static final int MAX_SCRIPT = 0x3ff; - - /** - * Integer properties mask and shift values for East Asian cell width. - * Equivalent to icu4c UPROPS_EA_MASK - */ - private static final int EAST_ASIAN_MASK_ = 0x000e0000; - /** - * Integer properties mask and shift values for East Asian cell width. - * Equivalent to icu4c UPROPS_EA_SHIFT - */ - private static final int EAST_ASIAN_SHIFT_ = 17; - /** - * Integer properties mask and shift values for blocks. Equivalent to icu4c - * UPROPS_BLOCK_MASK - */ - private static final int BLOCK_MASK_ = 0x0001ff00; - /** - * Integer properties mask and shift values for blocks. Equivalent to icu4c - * UPROPS_BLOCK_SHIFT - */ - private static final int BLOCK_SHIFT_ = 8; - /** - * Integer properties mask and shift values for scripts. Equivalent to icu4c - * UPROPS_SHIFT_LOW_MASK. - */ - public static final int SCRIPT_LOW_MASK = 0x000000ff; - - public static final int mergeScriptCodeOrIndex(int scriptX) { - return ((scriptX & SCRIPT_HIGH_MASK) >> SCRIPT_HIGH_SHIFT) | (scriptX & SCRIPT_LOW_MASK); - } - - /** - * Additional properties used in internal trie data - */ - /* - * Properties in vector word 1 Each bit encodes one binary property. The - * following constants represent the bit number, use 1< expectedTrieLength) { - throw new IOException("uprops.icu: not enough bytes for main trie"); - } - // skip padding after trie bytes - ICUBinary.skipBytes(bytes, expectedTrieLength - trieLength); - - // skip unused intervening data structures - ICUBinary.skipBytes(bytes, (additionalOffset - propertyOffset) * 4); - - if (m_additionalColumnsCount_ > 0) { - // reads the additional property block - m_additionalTrie_ = Trie2_16.createFromSerialized(bytes); - expectedTrieLength = (additionalVectorsOffset - additionalOffset) * 4; - trieLength = m_additionalTrie_.getSerializedLength(); - if (trieLength > expectedTrieLength) { - throw new IOException("uprops.icu: not enough bytes for additional-properties trie"); - } - // skip padding after trie bytes - ICUBinary.skipBytes(bytes, expectedTrieLength - trieLength); - - // additional properties - int size = scriptExtensionsOffset - additionalVectorsOffset; - m_additionalVectors_ = new int[size]; - for (int i = 0; i < size; i++) { - m_additionalVectors_[i] = bytes.getInt(); - } - } - - // Script_Extensions - int numChars = (reservedOffset7 - scriptExtensionsOffset) * 2; - if (numChars > 0) { - m_scriptExtensions_ = new char[numChars]; - for (int i = 0; i < numChars; ++i) { - m_scriptExtensions_[i] = bytes.getChar(); - } - } - } - - private static final class IsAcceptable implements ICUBinary.Authenticate { - // @Override when we switch to Java 6 - public boolean isDataVersionAcceptable(byte version[]) { - return version[0] == 7; - } - } - - private static final int DATA_FORMAT = 0x5550726F; // "UPro" - - public void upropsvec_addPropertyStarts(UnicodeSet set) { - /* - * add the start code point of each same-value range of the properties vectors - * trie - */ - if (m_additionalColumnsCount_ > 0) { - /* - * if m_additionalColumnsCount_==0 then the properties vectors trie may not be - * there at all - */ - Iterator trieIterator = m_additionalTrie_.iterator(); - Trie2.Range range; - while (trieIterator.hasNext() && !(range = trieIterator.next()).leadSurrogate) { - set.add(range.startCodePoint); - } - } - } - - // This static initializer block must be placed after - // other static member initialization - static { - try { - INSTANCE = new UCharacterProperty(); - } catch (IOException e) { - throw new RuntimeException("Missing resource: \"" + DATA_FILE_NAME_ + "\"; Reason: " + e.getMessage()); - } - } - - // Moved from UProperty.java - /** - * 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 UCharacter.BidiPairedBracketType values. - * - * @stable ICU 52 - */ - public static final int BIDI_PAIRED_BRACKET_TYPE = 0x1015; - -} +/* + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + ******************************************************************************* + * Copyright (C) 1996-2014, International Business Machines Corporation and + * others. All Rights Reserved. + ******************************************************************************* + */ + +package jdk_internal.icu.impl; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Iterator; + +import jdk_internal.icu.lang.UCharacter.HangulSyllableType; +import jdk_internal.icu.lang.UCharacter.NumericType; +import jdk_internal.icu.text.UTF16; +import jdk_internal.icu.text.UnicodeSet; +import jdk_internal.icu.util.VersionInfo; + +/** + *

+ * Internal class used for Unicode character property database. + *

+ *

+ * This classes store binary data read from uprops.icu. It does not have the + * capability to parse the data into more high-level information. It only + * returns bytes of information when required. + *

+ *

+ * Due to the form most commonly used for retrieval, array of char is used to + * store the binary data. + *

+ *

+ * UCharacterPropertyDB also contains information on accessing indexes to + * significant points in the binary data. + *

+ *

+ * Responsibility for molding the binary data into more meaning form lies on + * UCharacter. + *

+ * + * @author Syn Wee Quek + * @since release 2.1, february 1st 2002 + */ + +public final class UCharacterProperty { + // public data members ----------------------------------------------- + + /* + * public singleton instance + */ + public static final UCharacterProperty INSTANCE; + + /** + * Trie data + */ + public Trie2_16 m_trie_; + + /** + * Unicode version + */ + public VersionInfo m_unicodeVersion_; + + /** + * Character type mask + */ + public static final int TYPE_MASK = 0x1F; + + // uprops.h enum UPropertySource --------------------------------------- *** + + /** From uchar.c/uprops.icu main trie */ + public static final int SRC_CHAR = 1; + /** From uchar.c/uprops.icu properties vectors trie */ + public static final int SRC_PROPSVEC = 2; + /** From ubidi_props.c/ubidi.icu */ + public static final int SRC_BIDI = 5; + /** From normalizer2impl.cpp/nfc.nrm */ + public static final int SRC_NFC = 8; + /** From normalizer2impl.cpp/nfkc.nrm */ + public static final int SRC_NFKC = 9; + + // public methods ---------------------------------------------------- + + /** + * Gets the main property value for code point ch. + * + * @param ch code point whose property value is to be retrieved + * @return property value of code point + */ + public final int getProperty(int ch) { + return m_trie_.get(ch); + } + + /** + * Gets the unicode additional properties. Java version of C + * u_getUnicodeProperties(). + * + * @param codepoint codepoint whose additional properties is to be retrieved + * @param column The column index. + * @return unicode properties + */ + public int getAdditional(int codepoint, int column) { + assert column >= 0; + if (column >= m_additionalColumnsCount_) { + return 0; + } + return m_additionalVectors_[m_additionalTrie_.get(codepoint) + column]; + } + + /** + *

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

+ *

+ * This API does not check the validity of the codepoint. + *

+ * + * @param codepoint The code point. + * @return the Unicode version number + */ + public VersionInfo getAge(int codepoint) { + int version = getAdditional(codepoint, 0) >> AGE_SHIFT_; + return VersionInfo.getInstance((version >> FIRST_NIBBLE_SHIFT_) & LAST_NIBBLE_MASK_, + version & LAST_NIBBLE_MASK_, 0, 0); + } + + // int-value and enumerated properties --------------------------------- *** + + public int getType(int c) { + return getProperty(c) & TYPE_MASK; + } + + /* + * 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. + */ + private static final int /* UHangulSyllableType */ gcbToHst[] = { HangulSyllableType.NOT_APPLICABLE, /* + * U_GCB_OTHER + */ + HangulSyllableType.NOT_APPLICABLE, /* U_GCB_CONTROL */ + HangulSyllableType.NOT_APPLICABLE, /* U_GCB_CR */ + HangulSyllableType.NOT_APPLICABLE, /* U_GCB_EXTEND */ + HangulSyllableType.LEADING_JAMO, /* U_GCB_L */ + HangulSyllableType.NOT_APPLICABLE, /* U_GCB_LF */ + HangulSyllableType.LV_SYLLABLE, /* U_GCB_LV */ + HangulSyllableType.LVT_SYLLABLE, /* U_GCB_LVT */ + HangulSyllableType.TRAILING_JAMO, /* U_GCB_T */ + HangulSyllableType.VOWEL_JAMO /* U_GCB_V */ + /* + * Omit GCB values beyond what we need for hst. The code below checks for the + * array length. + */ + }; + + private class IntProperty { + int column; // SRC_PROPSVEC column, or "source" if mask==0 + int mask; + int shift; + + IntProperty(int column, int mask, int shift) { + this.column = column; + this.mask = mask; + this.shift = shift; + } + + IntProperty(int source) { + this.column = source; + this.mask = 0; + } + + int getValue(int c) { + // systematic, directly stored properties + return (getAdditional(c, column) & mask) >>> shift; + } + } + + private class BiDiIntProperty extends IntProperty { + BiDiIntProperty() { + super(SRC_BIDI); + } + } + + private class CombiningClassIntProperty extends IntProperty { + CombiningClassIntProperty(int source) { + super(source); + } + } + + private class NormQuickCheckIntProperty extends IntProperty { // UCHAR_NF*_QUICK_CHECK properties + int which; + int max; + + NormQuickCheckIntProperty(int source, int which, int max) { + super(source); + this.which = which; + this.max = max; + } + } + + private IntProperty intProp = new BiDiIntProperty() { // BIDI_PAIRED_BRACKET_TYPE + int getValue(int c) { + return UBiDiProps.INSTANCE.getPairedBracketType(c); + } + }; + + public int getIntPropertyValue(int c, int which) { + if (which == BIDI_PAIRED_BRACKET_TYPE) { + return intProp.getValue(c); + } + return 0; // undefined + } + + /** + * Forms a supplementary code point from the argument character
+ * Note this is for internal use hence no checks for the validity of the + * surrogate characters are done + * + * @param lead lead surrogate character + * @param trail trailing surrogate character + * @return code point of the supplementary character + */ + public static int getRawSupplementary(char lead, char trail) { + return (lead << LEAD_SURROGATE_SHIFT_) + trail + SURROGATE_OFFSET_; + } + + /** + * Gets the type mask + * + * @param type character type + * @return mask + */ + public static final int getMask(int type) { + return 1 << type; + } + + /** + * Returns the digit values of characters like 'A' - 'Z', normal, half-width and + * full-width. This method assumes that the other digit characters are checked + * by the calling method. + * + * @param ch character to test + * @return -1 if ch is not a character of the form 'A' - 'Z', otherwise its + * corresponding digit will be returned. + */ + public static int getEuropeanDigit(int ch) { + if ((ch > 0x7a && ch < 0xff21) || ch < 0x41 || (ch > 0x5a && ch < 0x61) || ch > 0xff5a + || (ch > 0xff3a && ch < 0xff41)) { + return -1; + } + if (ch <= 0x7a) { + // ch >= 0x41 or ch < 0x61 + return ch + 10 - ((ch <= 0x5a) ? 0x41 : 0x61); + } + // ch >= 0xff21 + if (ch <= 0xff3a) { + return ch + 10 - 0xff21; + } + // ch >= 0xff41 && ch <= 0xff5a + return ch + 10 - 0xff41; + } + + public int digit(int c) { + int value = getNumericTypeValue(getProperty(c)) - NTV_DECIMAL_START_; + if (value <= 9) { + return value; + } else { + return -1; + } + } + + // protected variables ----------------------------------------------- + + /** + * Extra property trie + */ + Trie2_16 m_additionalTrie_; + /** + * Extra property vectors, 1st column for age and second for binary properties. + */ + int m_additionalVectors_[]; + /** + * Number of additional columns + */ + int m_additionalColumnsCount_; + /** + * Maximum values for block, bits used as in vector word 0 + */ + int m_maxBlockScriptValue_; + /** + * Maximum values for script, bits used as in vector word 0 + */ + int m_maxJTGValue_; + /** + * Script_Extensions data + */ + public char[] m_scriptExtensions_; + + // private variables ------------------------------------------------- + + /** + * Default name of the datafile + */ + @SuppressWarnings("deprecation") + private static final String DATA_FILE_NAME_ = "/assets/eagler/icudt/uprops.icu"; + + /** + * Shift value for lead surrogate to form a supplementary character. + */ + private static final int LEAD_SURROGATE_SHIFT_ = 10; + /** + * Offset to add to combined surrogate pair to avoid masking. + */ + private static final int SURROGATE_OFFSET_ = UTF16.SUPPLEMENTARY_MIN_VALUE + - (UTF16.SURROGATE_MIN_VALUE << LEAD_SURROGATE_SHIFT_) - UTF16.TRAIL_SURROGATE_MIN_VALUE; + + // property data constants ------------------------------------------------- + + /** + * Numeric types and values in the main properties words. + */ + private static final int NUMERIC_TYPE_VALUE_SHIFT_ = 6; + + private static final int getNumericTypeValue(int props) { + return props >> NUMERIC_TYPE_VALUE_SHIFT_; + } + + /* constants for the storage form of numeric types and values */ + /** No numeric value. */ + private static final int NTV_NONE_ = 0; + /** Decimal digits: nv=0..9 */ + private static final int NTV_DECIMAL_START_ = 1; + /** Other digits: nv=0..9 */ + private static final int NTV_DIGIT_START_ = 11; + /** Small integers: nv=0..154 */ + private static final int NTV_NUMERIC_START_ = 21; + + private static final int ntvGetType(int ntv) { + return (ntv == NTV_NONE_) ? NumericType.NONE + : (ntv < NTV_DIGIT_START_) ? NumericType.DECIMAL + : (ntv < NTV_NUMERIC_START_) ? NumericType.DIGIT : NumericType.NUMERIC; + } + + /* + * Properties in vector word 0 Bits 31..24 DerivedAge version major/minor one + * nibble each 23..22 3..1: Bits 21..20 & 7..0 = Script_Extensions index 3: + * Script value from Script_Extensions 2: Script=Inherited 1: Script=Common 0: + * Script=bits 21..20 & 7..0 21..20 Bits 9..8 of the UScriptCode, or index to + * Script_Extensions 19..17 East Asian Width 16.. 8 UBlockCode 7.. 0 + * UScriptCode, or index to Script_Extensions + */ + + /** + * Script_Extensions: mask includes Script + */ + public static final int SCRIPT_X_MASK = 0x00f000ff; + // private static final int SCRIPT_X_SHIFT = 22; + + // The UScriptCode or Script_Extensions index is split across two bit fields. + // (Starting with Unicode 13/ICU 66/2019 due to more varied Script_Extensions.) + // Shift the high bits right by 12 to assemble the full value. + public static final int SCRIPT_HIGH_MASK = 0x00300000; + public static final int SCRIPT_HIGH_SHIFT = 12; + public static final int MAX_SCRIPT = 0x3ff; + + /** + * Integer properties mask and shift values for East Asian cell width. + * Equivalent to icu4c UPROPS_EA_MASK + */ + private static final int EAST_ASIAN_MASK_ = 0x000e0000; + /** + * Integer properties mask and shift values for East Asian cell width. + * Equivalent to icu4c UPROPS_EA_SHIFT + */ + private static final int EAST_ASIAN_SHIFT_ = 17; + /** + * Integer properties mask and shift values for blocks. Equivalent to icu4c + * UPROPS_BLOCK_MASK + */ + private static final int BLOCK_MASK_ = 0x0001ff00; + /** + * Integer properties mask and shift values for blocks. Equivalent to icu4c + * UPROPS_BLOCK_SHIFT + */ + private static final int BLOCK_SHIFT_ = 8; + /** + * Integer properties mask and shift values for scripts. Equivalent to icu4c + * UPROPS_SHIFT_LOW_MASK. + */ + public static final int SCRIPT_LOW_MASK = 0x000000ff; + + public static final int mergeScriptCodeOrIndex(int scriptX) { + return ((scriptX & SCRIPT_HIGH_MASK) >> SCRIPT_HIGH_SHIFT) | (scriptX & SCRIPT_LOW_MASK); + } + + /** + * Additional properties used in internal trie data + */ + /* + * Properties in vector word 1 Each bit encodes one binary property. The + * following constants represent the bit number, use 1< expectedTrieLength) { + throw new IOException("uprops.icu: not enough bytes for main trie"); + } + // skip padding after trie bytes + ICUBinary.skipBytes(bytes, expectedTrieLength - trieLength); + + // skip unused intervening data structures + ICUBinary.skipBytes(bytes, (additionalOffset - propertyOffset) * 4); + + if (m_additionalColumnsCount_ > 0) { + // reads the additional property block + m_additionalTrie_ = Trie2_16.createFromSerialized(bytes); + expectedTrieLength = (additionalVectorsOffset - additionalOffset) * 4; + trieLength = m_additionalTrie_.getSerializedLength(); + if (trieLength > expectedTrieLength) { + throw new IOException("uprops.icu: not enough bytes for additional-properties trie"); + } + // skip padding after trie bytes + ICUBinary.skipBytes(bytes, expectedTrieLength - trieLength); + + // additional properties + int size = scriptExtensionsOffset - additionalVectorsOffset; + m_additionalVectors_ = new int[size]; + for (int i = 0; i < size; i++) { + m_additionalVectors_[i] = bytes.getInt(); + } + } + + // Script_Extensions + int numChars = (reservedOffset7 - scriptExtensionsOffset) * 2; + if (numChars > 0) { + m_scriptExtensions_ = new char[numChars]; + for (int i = 0; i < numChars; ++i) { + m_scriptExtensions_[i] = bytes.getChar(); + } + } + } + + private static final class IsAcceptable implements ICUBinary.Authenticate { + // @Override when we switch to Java 6 + public boolean isDataVersionAcceptable(byte version[]) { + return version[0] == 7; + } + } + + private static final int DATA_FORMAT = 0x5550726F; // "UPro" + + public void upropsvec_addPropertyStarts(UnicodeSet set) { + /* + * add the start code point of each same-value range of the properties vectors + * trie + */ + if (m_additionalColumnsCount_ > 0) { + /* + * if m_additionalColumnsCount_==0 then the properties vectors trie may not be + * there at all + */ + Iterator trieIterator = m_additionalTrie_.iterator(); + Trie2.Range range; + while (trieIterator.hasNext() && !(range = trieIterator.next()).leadSurrogate) { + set.add(range.startCodePoint); + } + } + } + + // This static initializer block must be placed after + // other static member initialization + static { + try { + INSTANCE = new UCharacterProperty(); + } catch (IOException e) { + throw new RuntimeException("Missing resource: \"" + DATA_FILE_NAME_ + "\"; Reason: " + e.getMessage()); + } + } + + // Moved from UProperty.java + /** + * 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 UCharacter.BidiPairedBracketType values. + * + * @stable ICU 52 + */ + public static final int BIDI_PAIRED_BRACKET_TYPE = 0x1015; + +} diff --git a/src/main/java/jdk_internal/icu/impl/UnicodeSetStringSpan.java b/src/main/java/jdk_internal/icu/impl/UnicodeSetStringSpan.java index ddde4688..2363cc50 100755 --- a/src/main/java/jdk_internal/icu/impl/UnicodeSetStringSpan.java +++ b/src/main/java/jdk_internal/icu/impl/UnicodeSetStringSpan.java @@ -1,1179 +1,1179 @@ -/* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ****************************************************************************** - * - * Copyright (C) 2009-2014, International Business Machines - * Corporation and others. All Rights Reserved. - * - ****************************************************************************** - */ - -package jdk_internal.icu.impl; - -import java.util.ArrayList; - -import jdk_internal.icu.text.UTF16; -import jdk_internal.icu.text.UnicodeSet; -import jdk_internal.icu.text.UnicodeSet.SpanCondition; -import jdk_internal.icu.util.OutputInt; - -/* - * 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. - */ -public class UnicodeSetStringSpan { - - /* - * 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. - */ - public static final int WITH_COUNT = 0x40; // spanAndCount() may be called - public static final int FWD = 0x20; - public static final int BACK = 0x10; - // public static final int UTF16 = 8; - public static final int CONTAINED = 2; - public static final int NOT_CONTAINED = 1; - - public static final int ALL = 0x7f; - - public static final int FWD_UTF16_CONTAINED = FWD | /* UTF16 | */ CONTAINED; - public static final int FWD_UTF16_NOT_CONTAINED = FWD | /* UTF16 | */NOT_CONTAINED; - public static final int BACK_UTF16_CONTAINED = BACK | /* UTF16 | */ CONTAINED; - public static final int BACK_UTF16_NOT_CONTAINED = BACK | /* UTF16 | */NOT_CONTAINED; - - /** - * Special spanLength short values. (since Java has not unsigned byte type) All - * code points in the string are contained in the parent set. - */ - static final short ALL_CP_CONTAINED = 0xff; - - /** The spanLength is >=0xfe. */ - static final short LONG_SPAN = ALL_CP_CONTAINED - 1; - - /** Set for span(). Same as parent but without strings. */ - private UnicodeSet spanSet; - - /** - * Set for span(not contained). Same as spanSet, plus characters that start or - * end strings. - */ - private UnicodeSet spanNotSet; - - /** The strings of the parent set. */ - private ArrayList strings; - - /** The lengths of span(), spanBack() etc. for each string. */ - private short[] spanLengths; - - /** Maximum lengths of relevant strings. */ - private int maxLength16; - - /** Are there strings that are not fully contained in the code point set? */ - private boolean someRelevant; - - /** Set up for all variants of span()? */ - private boolean all; - - /** Span helper */ - private OffsetList offsets; - - /** - * Constructs for all variants of span(), or only for any one variant. - * Initializes as little as possible, for single use. - */ - public UnicodeSetStringSpan(final UnicodeSet set, final ArrayList setStrings, int which) { - spanSet = new UnicodeSet(0, 0x10ffff); - // TODO: With Java 6, just take the parent set's strings as is, - // as a NavigableSet, rather than as an ArrayList copy of the set of - // strings. - // Then iterate via the first() and higher() methods. - // (We do not want to create multiple Iterator objects in each span().) - // See ICU ticket #7454. - strings = setStrings; - all = (which == ALL); - spanSet.retainAll(set); - if (0 != (which & NOT_CONTAINED)) { - // Default to the same sets. - // addToSpanNotSet() will create a separate set if necessary. - spanNotSet = spanSet; - } - offsets = new OffsetList(); - - // Determine if the strings even need to be taken into account at all for span() - // etc. - // If any string is relevant, then all strings need to be used for - // span(longest match) but only the relevant ones for span(while contained). - // TODO: Possible optimization: Distinguish CONTAINED vs. LONGEST_MATCH - // and do not store UTF-8 strings if !thisRelevant and CONTAINED. - // (Only store irrelevant UTF-8 strings for LONGEST_MATCH where they are - // relevant after all.) - // Also count the lengths of the UTF-8 versions of the strings for memory - // allocation. - int stringsLength = strings.size(); - - int i, spanLength; - someRelevant = false; - for (i = 0; i < stringsLength; ++i) { - String string = strings.get(i); - int length16 = string.length(); - spanLength = spanSet.span(string, SpanCondition.CONTAINED); - if (spanLength < length16) { // Relevant string. - someRelevant = true; - } - if (/* (0 != (which & UTF16)) && */ length16 > maxLength16) { - maxLength16 = length16; - } - } - if (!someRelevant && (which & WITH_COUNT) == 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(); - } - - int spanBackLengthsOffset; - - // Allocate a block of meta data. - int allocSize; - if (all) { - // 2 sets of span lengths - allocSize = stringsLength * (2); - } else { - allocSize = stringsLength; // One set of span lengths. - } - spanLengths = new short[allocSize]; - - if (all) { - // Store span lengths for all span() variants. - spanBackLengthsOffset = stringsLength; - } else { - // Store span lengths for only one span() variant. - spanBackLengthsOffset = 0; - } - - // Set the meta data and spanNotSet and write the UTF-8 strings. - - for (i = 0; i < stringsLength; ++i) { - String string = strings.get(i); - int length16 = string.length(); - spanLength = spanSet.span(string, SpanCondition.CONTAINED); - if (spanLength < length16) { // Relevant string. - if (true /* 0 != (which & UTF16) */) { - if (0 != (which & CONTAINED)) { - if (0 != (which & FWD)) { - spanLengths[i] = makeSpanLengthByte(spanLength); - } - if (0 != (which & BACK)) { - spanLength = length16 - spanSet.spanBack(string, length16, SpanCondition.CONTAINED); - spanLengths[spanBackLengthsOffset + i] = makeSpanLengthByte(spanLength); - } - } else /* not CONTAINED, not all, but NOT_CONTAINED */ { - spanLengths[i] = spanLengths[spanBackLengthsOffset + i] = 0; // Only store a relevant/irrelevant - // flag. - } - } - if (0 != (which & NOT_CONTAINED)) { - // Add string start and end code points to the spanNotSet so that - // a span(while not contained) stops before any string. - int c; - if (0 != (which & FWD)) { - c = string.codePointAt(0); - addToSpanNotSet(c); - } - if (0 != (which & BACK)) { - c = string.codePointBefore(length16); - addToSpanNotSet(c); - } - } - } else { // Irrelevant string. - if (all) { - spanLengths[i] = spanLengths[spanBackLengthsOffset + i] = ALL_CP_CONTAINED; - } else { - // All spanXYZLengths pointers contain the same address. - spanLengths[i] = ALL_CP_CONTAINED; - } - } - } - - // Finish. - if (all) { - spanNotSet.freeze(); - } - } - - /** - * 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). - */ - public boolean needsStringSpanUTF16() { - return someRelevant; - } - - /** For fast UnicodeSet::contains(c). */ - public boolean contains(int c) { - return spanSet.contains(c); - } - - /** - * Adds a starting or ending string character to the spanNotSet so that a - * character span ends before any string. - */ - private void addToSpanNotSet(int c) { - if (spanNotSet == null || spanNotSet == spanSet) { - if (spanSet.contains(c)) { - return; // Nothing to do. - } - spanNotSet = spanSet.cloneAsThawed(); - } - spanNotSet.add(c); - } - - /* - * 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(SpanCondition.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(SpanCondition.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(SpanCondition.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(SpanCondition.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(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(SpanCondition.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(SpanCondition.CONTAINED). Stop if spanLength==0, - * otherwise continue the loop. - */ - /** - * Spans a string. - * - * @param s The string to be spanned - * @param start The start index that the span begins - * @param spanCondition The span condition - * @return the limit (exclusive end) of the span - */ - public int span(CharSequence s, int start, SpanCondition spanCondition) { - if (spanCondition == SpanCondition.NOT_CONTAINED) { - return spanNot(s, start, null); - } - int spanLimit = spanSet.span(s, start, SpanCondition.CONTAINED); - if (spanLimit == s.length()) { - return spanLimit; - } - return spanWithStrings(s, start, spanLimit, spanCondition); - } - - /** - * Synchronized method for complicated spans using the offsets. Avoids - * synchronization for simple cases. - * - * @param spanLimit = spanSet.span(s, start, CONTAINED) - */ - private synchronized int spanWithStrings(CharSequence s, int start, int spanLimit, SpanCondition spanCondition) { - // Consider strings; they may overlap with the span. - int initSize = 0; - if (spanCondition == SpanCondition.CONTAINED) { - // Use offset list to try all possibilities. - initSize = maxLength16; - } - offsets.setMaxLength(initSize); - int length = s.length(); - int pos = spanLimit, rest = length - spanLimit; - int spanLength = spanLimit - start; - int i, stringsLength = strings.size(); - for (;;) { - if (spanCondition == SpanCondition.CONTAINED) { - for (i = 0; i < stringsLength; ++i) { - int overlap = spanLengths[i]; - if (overlap == ALL_CP_CONTAINED) { - continue; // Irrelevant string. - } - String string = strings.get(i); - - int length16 = string.length(); - - // 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. - overlap = string.offsetByCodePoints(overlap, -1); // Length of the string minus the last code - // point. - } - if (overlap > spanLength) { - overlap = spanLength; - } - int 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, string, length16)) { - if (inc == rest) { - return length; // Reached the end of the string. - } - offsets.addOffset(inc); - } - if (overlap == 0) { - break; - } - --overlap; - ++inc; - } - } - } else /* SIMPLE */ { - int maxInc = 0, maxOverlap = 0; - for (i = 0; i < stringsLength; ++i) { - int overlap = spanLengths[i]; - // For longest match, we do need to try to match even an all-contained string - // to find the match from the earliest start. - - String string = strings.get(i); - - int length16 = string.length(); - - // Try to match this string at pos-overlap..pos. - if (overlap >= 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; - } - int inc = length16 - overlap; // Keep overlap+inc==length16. - for (;;) { - if (inc > rest || overlap < maxOverlap) { - break; - } - // Try to match if the string is longer or starts earlier. - if ((overlap > maxOverlap || /* redundant overlap==maxOverlap && */inc > maxInc) - && matches16CPB(s, pos - overlap, length, string, 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. - spanLimit = spanSet.span(s, pos, SpanCondition.CONTAINED); - spanLength = spanLimit - pos; - if (spanLength == rest || // Reached the end of the string, or - spanLength == 0 // neither strings nor span progressed. - ) { - return spanLimit; - } - 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. - } - } - int minOffset = offsets.popMinimum(null); - pos += minOffset; - rest -= minOffset; - spanLength = 0; // Match strings from after a string match. - } - } - - /** - * Spans a string and counts the smallest number of set elements on any path - * across the span. - * - *

- * For proper counting, we cannot ignore strings that are fully contained in - * code point spans. - * - *

- * If the set does not have any fully-contained strings, then we could optimize - * this like span(), but such sets are likely rare, and this is at least still - * linear. - * - * @param s The string to be spanned - * @param start The start index that the span begins - * @param spanCondition The span condition - * @param outCount The count - * @return the limit (exclusive end) of the span - */ - public int spanAndCount(CharSequence s, int start, SpanCondition spanCondition, OutputInt outCount) { - if (spanCondition == SpanCondition.NOT_CONTAINED) { - return spanNot(s, start, outCount); - } - // Consider strings; they may overlap with the span, - // and they may result in a smaller count that with just code points. - if (spanCondition == SpanCondition.CONTAINED) { - return spanContainedAndCount(s, start, outCount); - } - // SIMPLE (not synchronized, does not use offsets) - int stringsLength = strings.size(); - int length = s.length(); - int pos = start; - int rest = length - start; - int count = 0; - while (rest != 0) { - // Try to match the next code point. - int cpLength = spanOne(spanSet, s, pos, rest); - int maxInc = (cpLength > 0) ? cpLength : 0; - // Try to match all of the strings. - for (int i = 0; i < stringsLength; ++i) { - String string = strings.get(i); - int length16 = string.length(); - if (maxInc < length16 && length16 <= rest && matches16CPB(s, pos, length, string, length16)) { - maxInc = length16; - } - } - // We are done if there is no match beyond pos. - if (maxInc == 0) { - outCount.value = count; - return pos; - } - // Continue from the longest match. - ++count; - pos += maxInc; - rest -= maxInc; - } - outCount.value = count; - return pos; - } - - private synchronized int spanContainedAndCount(CharSequence s, int start, OutputInt outCount) { - // Use offset list to try all possibilities. - offsets.setMaxLength(maxLength16); - int stringsLength = strings.size(); - int length = s.length(); - int pos = start; - int rest = length - start; - int count = 0; - while (rest != 0) { - // Try to match the next code point. - int cpLength = spanOne(spanSet, s, pos, rest); - if (cpLength > 0) { - offsets.addOffsetAndCount(cpLength, count + 1); - } - // Try to match all of the strings. - for (int i = 0; i < stringsLength; ++i) { - String string = strings.get(i); - int length16 = string.length(); - // Note: If the strings were sorted by length, then we could also - // avoid trying to match if there is already a match of the same length. - if (length16 <= rest && !offsets.hasCountAtOffset(length16, count + 1) - && matches16CPB(s, pos, length, string, length16)) { - offsets.addOffsetAndCount(length16, count + 1); - } - } - // We are done if there is no match beyond pos. - if (offsets.isEmpty()) { - outCount.value = count; - return pos; - } - // Continue from the nearest match. - int minOffset = offsets.popMinimum(outCount); - count = outCount.value; - pos += minOffset; - rest -= minOffset; - } - outCount.value = count; - return pos; - } - - /** - * Span a string backwards. - * - * @param s The string to be spanned - * @param spanCondition The span condition - * @return The string index which starts the span (i.e. inclusive). - */ - public synchronized int spanBack(CharSequence s, int length, SpanCondition spanCondition) { - if (spanCondition == SpanCondition.NOT_CONTAINED) { - return spanNotBack(s, length); - } - int pos = spanSet.spanBack(s, length, SpanCondition.CONTAINED); - if (pos == 0) { - return 0; - } - int spanLength = length - pos; - - // Consider strings; they may overlap with the span. - int initSize = 0; - if (spanCondition == SpanCondition.CONTAINED) { - // Use offset list to try all possibilities. - initSize = maxLength16; - } - offsets.setMaxLength(initSize); - int i, stringsLength = strings.size(); - int spanBackLengthsOffset = 0; - if (all) { - spanBackLengthsOffset = stringsLength; - } - for (;;) { - if (spanCondition == SpanCondition.CONTAINED) { - for (i = 0; i < stringsLength; ++i) { - int overlap = spanLengths[spanBackLengthsOffset + i]; - if (overlap == ALL_CP_CONTAINED) { - continue; // Irrelevant string. - } - String string = strings.get(i); - - int length16 = string.length(); - - // 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. - int len1 = 0; - len1 = string.offsetByCodePoints(0, 1); - overlap -= len1; // Length of the string minus the first code point. - } - if (overlap > spanLength) { - overlap = spanLength; - } - int 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, string, length16)) { - if (dec == pos) { - return 0; // Reached the start of the string. - } - offsets.addOffset(dec); - } - if (overlap == 0) { - break; - } - --overlap; - ++dec; - } - } - } else /* SIMPLE */ { - int maxDec = 0, maxOverlap = 0; - for (i = 0; i < stringsLength; ++i) { - int overlap = spanLengths[spanBackLengthsOffset + i]; - // For longest match, we do need to try to match even an all-contained string - // to find the match from the latest end. - - String string = strings.get(i); - - int length16 = string.length(); - - // Try to match this string at pos-(length16-overlap)..pos-length16. - if (overlap >= 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; - } - int dec = length16 - overlap; // Keep dec+overlap==length16. - for (;;) { - if (dec > pos || overlap < maxOverlap) { - break; - } - // Try to match if the string is longer or ends later. - if ((overlap > maxOverlap || /* redundant overlap==maxOverlap && */dec > maxDec) - && matches16CPB(s, pos - dec, length, string, 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. - int oldPos = pos; - pos = spanSet.spanBack(s, oldPos, SpanCondition.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(null); - spanLength = 0; // Match strings from before a string match. - } - } - - /** - * Algorithm for spanNot()==span(SpanCondition.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(SpanCondition.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. - * - * @param s The string to be spanned - * @param start The start index that the span begins - * @param outCount If not null: Receives the number of code points across the - * span. - * @return the limit (exclusive end) of the span - */ - private int spanNot(CharSequence s, int start, OutputInt outCount) { - int length = s.length(); - int pos = start, rest = length - start; - int stringsLength = strings.size(); - int count = 0; - do { - // Span until we find a code point from the set, - // or a code point that starts or ends some string. - int spanLimit; - if (outCount == null) { - spanLimit = spanNotSet.span(s, pos, SpanCondition.NOT_CONTAINED); - } else { - spanLimit = spanNotSet.spanAndCount(s, pos, SpanCondition.NOT_CONTAINED, outCount); - outCount.value = count = count + outCount.value; - } - if (spanLimit == length) { - return length; // Reached the end of the string. - } - pos = spanLimit; - rest = length - spanLimit; - - // Check whether the current code point is in the original set, - // without the string starts and ends. - int 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 (int i = 0; i < stringsLength; ++i) { - if (spanLengths[i] == ALL_CP_CONTAINED) { - continue; // Irrelevant string. - } - String string = strings.get(i); - - int length16 = string.length(); - if (length16 <= rest && matches16CPB(s, pos, length, string, 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; - ++count; - } while (rest != 0); - if (outCount != null) { - outCount.value = count; - } - return length; // Reached the end of the string. - } - - private int spanNotBack(CharSequence s, int length) { - int pos = length; - int 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 = spanNotSet.spanBack(s, pos, SpanCondition.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. - int 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; i < stringsLength; ++i) { - // Use spanLengths rather than a spanLengths pointer because - // it is easier and we only need to know whether the string is irrelevant - // which is the same in either array. - if (spanLengths[i] == ALL_CP_CONTAINED) { - continue; // Irrelevant string. - } - String string = strings.get(i); - - int length16 = string.length(); - if (length16 <= pos && matches16CPB(s, pos - length16, length, string, 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. - } - - static short makeSpanLengthByte(int spanLength) { - // 0xfe==UnicodeSetStringSpan::LONG_SPAN - return spanLength < LONG_SPAN ? (short) spanLength : LONG_SPAN; - } - - // Compare strings without any argument checks. Requires length>0. - private static boolean matches16(CharSequence s, int start, final String t, int length) { - int end = start + length; - while (length-- > 0) { - if (s.charAt(--end) != t.charAt(length)) { - return false; - } - } - 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. - * - * @param s The string to match in. - * @param start The start index of s. - * @param limit The limit of the subsequence of s being spanned. - * @param t The substring to be matched in s. - * @param tlength The length of t. - */ - static boolean matches16CPB(CharSequence s, int start, int limit, final String t, int tlength) { - return matches16(s, start, t, tlength) - && !(0 < start && Character.isHighSurrogate(s.charAt(start - 1)) - && Character.isLowSurrogate(s.charAt(start))) - && !((start + tlength) < limit && Character.isHighSurrogate(s.charAt(start + tlength - 1)) - && Character.isLowSurrogate(s.charAt(start + tlength))); - } - - /** - * Does the set contain the next code point? If so, return its length; otherwise - * return its negative length. - */ - static int spanOne(final UnicodeSet set, CharSequence s, int start, int length) { - char c = s.charAt(start); - if (c >= 0xd800 && c <= 0xdbff && length >= 2) { - char c2 = s.charAt(start + 1); - if (UTF16.isTrailSurrogate(c2)) { - int supplementary = UCharacterProperty.getRawSupplementary(c, c2); - return set.contains(supplementary) ? 2 : -2; - } - } - return set.contains(c) ? 1 : -1; - } - - static int spanOneBack(final UnicodeSet set, CharSequence s, int length) { - char c = s.charAt(length - 1); - if (c >= 0xdc00 && c <= 0xdfff && length >= 2) { - char c2 = s.charAt(length - 2); - if (UTF16.isLeadSurrogate(c2)) { - int supplementary = UCharacterProperty.getRawSupplementary(c2, c); - return set.contains(supplementary) ? 2 : -2; - } - } - return set.contains(c) ? 1 : -1; - } - - /** - * Helper class for UnicodeSetStringSpan. - * - *

- * List of offsets from the current position from where to try matching a code - * point or a string. Stores 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. - * - *

- * This class optionally also tracks the minimum non-negative count for each - * position, intended to count the smallest number of elements of any path - * leading to that position. - * - *

- * The implementation uses a circular buffer of count integers, each indicating - * whether the corresponding offset is in the list, and its path element count. - * 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. - * - *

- * Note: An earlier version did not track counts and stored only byte flags. - * With boolean flags, 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. - */ - private static final class OffsetList { - private int[] list; - private int length; - private int start; - - public OffsetList() { - list = new int[16]; // default size - } - - public void setMaxLength(int maxLength) { - if (maxLength > list.length) { - list = new int[maxLength]; - } - clear(); - } - - public void clear() { - for (int i = list.length; i-- > 0;) { - list[i] = 0; - } - start = length = 0; - } - - public boolean isEmpty() { - return (length == 0); - } - - /** - * Reduces 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. - * - * @param delta [1..maxLength] - */ - public void shift(int delta) { - int i = start + delta; - if (i >= list.length) { - i -= list.length; - } - if (list[i] != 0) { - list[i] = 0; - --length; - } - start = i; - } - - /** - * Adds an offset. The list must not contain it yet. - * - * @param offset [1..maxLength] - */ - public void addOffset(int offset) { - int i = start + offset; - if (i >= list.length) { - i -= list.length; - } - assert list[i] == 0; - list[i] = 1; - ++length; - } - - /** - * Adds an offset and updates its count. The list may already contain the - * offset. - * - * @param offset [1..maxLength] - */ - public void addOffsetAndCount(int offset, int count) { - assert count > 0; - int i = start + offset; - if (i >= list.length) { - i -= list.length; - } - if (list[i] == 0) { - list[i] = count; - ++length; - } else if (count < list[i]) { - list[i] = count; - } - } - - /** - * @param offset [1..maxLength] - */ - public boolean containsOffset(int offset) { - int i = start + offset; - if (i >= list.length) { - i -= list.length; - } - return list[i] != 0; - } - - /** - * @param offset [1..maxLength] - */ - public boolean hasCountAtOffset(int offset, int count) { - int i = start + offset; - if (i >= list.length) { - i -= list.length; - } - int oldCount = list[i]; - return oldCount != 0 && oldCount <= count; - } - - /** - * Finds the lowest stored offset from a non-empty list, removes it, and reduces - * all other offsets by this minimum. - * - * @return min=[1..maxLength] - */ - public int popMinimum(OutputInt outCount) { - // Look for the next offset in list[start+1..list.length-1]. - int i = start, result; - while (++i < list.length) { - int count = list[i]; - if (count != 0) { - list[i] = 0; - --length; - result = i - start; - start = i; - if (outCount != null) { - outCount.value = count; - } - return result; - } - } - // i==list.length - - // Wrap around and look for the next offset in list[0..start]. - // Since the list is not empty, there will be one. - result = list.length - start; - i = 0; - int count; - while ((count = list[i]) == 0) { - ++i; - } - list[i] = 0; - --length; - start = i; - if (outCount != null) { - outCount.value = count; - } - return result + i; - } - } -} +/* + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ****************************************************************************** + * + * Copyright (C) 2009-2014, International Business Machines + * Corporation and others. All Rights Reserved. + * + ****************************************************************************** + */ + +package jdk_internal.icu.impl; + +import java.util.ArrayList; + +import jdk_internal.icu.text.UTF16; +import jdk_internal.icu.text.UnicodeSet; +import jdk_internal.icu.text.UnicodeSet.SpanCondition; +import jdk_internal.icu.util.OutputInt; + +/* + * 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. + */ +public class UnicodeSetStringSpan { + + /* + * 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. + */ + public static final int WITH_COUNT = 0x40; // spanAndCount() may be called + public static final int FWD = 0x20; + public static final int BACK = 0x10; + // public static final int UTF16 = 8; + public static final int CONTAINED = 2; + public static final int NOT_CONTAINED = 1; + + public static final int ALL = 0x7f; + + public static final int FWD_UTF16_CONTAINED = FWD | /* UTF16 | */ CONTAINED; + public static final int FWD_UTF16_NOT_CONTAINED = FWD | /* UTF16 | */NOT_CONTAINED; + public static final int BACK_UTF16_CONTAINED = BACK | /* UTF16 | */ CONTAINED; + public static final int BACK_UTF16_NOT_CONTAINED = BACK | /* UTF16 | */NOT_CONTAINED; + + /** + * Special spanLength short values. (since Java has not unsigned byte type) All + * code points in the string are contained in the parent set. + */ + static final short ALL_CP_CONTAINED = 0xff; + + /** The spanLength is >=0xfe. */ + static final short LONG_SPAN = ALL_CP_CONTAINED - 1; + + /** Set for span(). Same as parent but without strings. */ + private UnicodeSet spanSet; + + /** + * Set for span(not contained). Same as spanSet, plus characters that start or + * end strings. + */ + private UnicodeSet spanNotSet; + + /** The strings of the parent set. */ + private ArrayList strings; + + /** The lengths of span(), spanBack() etc. for each string. */ + private short[] spanLengths; + + /** Maximum lengths of relevant strings. */ + private int maxLength16; + + /** Are there strings that are not fully contained in the code point set? */ + private boolean someRelevant; + + /** Set up for all variants of span()? */ + private boolean all; + + /** Span helper */ + private OffsetList offsets; + + /** + * Constructs for all variants of span(), or only for any one variant. + * Initializes as little as possible, for single use. + */ + public UnicodeSetStringSpan(final UnicodeSet set, final ArrayList setStrings, int which) { + spanSet = new UnicodeSet(0, 0x10ffff); + // TODO: With Java 6, just take the parent set's strings as is, + // as a NavigableSet, rather than as an ArrayList copy of the set of + // strings. + // Then iterate via the first() and higher() methods. + // (We do not want to create multiple Iterator objects in each span().) + // See ICU ticket #7454. + strings = setStrings; + all = (which == ALL); + spanSet.retainAll(set); + if (0 != (which & NOT_CONTAINED)) { + // Default to the same sets. + // addToSpanNotSet() will create a separate set if necessary. + spanNotSet = spanSet; + } + offsets = new OffsetList(); + + // Determine if the strings even need to be taken into account at all for span() + // etc. + // If any string is relevant, then all strings need to be used for + // span(longest match) but only the relevant ones for span(while contained). + // TODO: Possible optimization: Distinguish CONTAINED vs. LONGEST_MATCH + // and do not store UTF-8 strings if !thisRelevant and CONTAINED. + // (Only store irrelevant UTF-8 strings for LONGEST_MATCH where they are + // relevant after all.) + // Also count the lengths of the UTF-8 versions of the strings for memory + // allocation. + int stringsLength = strings.size(); + + int i, spanLength; + someRelevant = false; + for (i = 0; i < stringsLength; ++i) { + String string = strings.get(i); + int length16 = string.length(); + spanLength = spanSet.span(string, SpanCondition.CONTAINED); + if (spanLength < length16) { // Relevant string. + someRelevant = true; + } + if (/* (0 != (which & UTF16)) && */ length16 > maxLength16) { + maxLength16 = length16; + } + } + if (!someRelevant && (which & WITH_COUNT) == 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(); + } + + int spanBackLengthsOffset; + + // Allocate a block of meta data. + int allocSize; + if (all) { + // 2 sets of span lengths + allocSize = stringsLength * (2); + } else { + allocSize = stringsLength; // One set of span lengths. + } + spanLengths = new short[allocSize]; + + if (all) { + // Store span lengths for all span() variants. + spanBackLengthsOffset = stringsLength; + } else { + // Store span lengths for only one span() variant. + spanBackLengthsOffset = 0; + } + + // Set the meta data and spanNotSet and write the UTF-8 strings. + + for (i = 0; i < stringsLength; ++i) { + String string = strings.get(i); + int length16 = string.length(); + spanLength = spanSet.span(string, SpanCondition.CONTAINED); + if (spanLength < length16) { // Relevant string. + if (true /* 0 != (which & UTF16) */) { + if (0 != (which & CONTAINED)) { + if (0 != (which & FWD)) { + spanLengths[i] = makeSpanLengthByte(spanLength); + } + if (0 != (which & BACK)) { + spanLength = length16 - spanSet.spanBack(string, length16, SpanCondition.CONTAINED); + spanLengths[spanBackLengthsOffset + i] = makeSpanLengthByte(spanLength); + } + } else /* not CONTAINED, not all, but NOT_CONTAINED */ { + spanLengths[i] = spanLengths[spanBackLengthsOffset + i] = 0; // Only store a relevant/irrelevant + // flag. + } + } + if (0 != (which & NOT_CONTAINED)) { + // Add string start and end code points to the spanNotSet so that + // a span(while not contained) stops before any string. + int c; + if (0 != (which & FWD)) { + c = string.codePointAt(0); + addToSpanNotSet(c); + } + if (0 != (which & BACK)) { + c = string.codePointBefore(length16); + addToSpanNotSet(c); + } + } + } else { // Irrelevant string. + if (all) { + spanLengths[i] = spanLengths[spanBackLengthsOffset + i] = ALL_CP_CONTAINED; + } else { + // All spanXYZLengths pointers contain the same address. + spanLengths[i] = ALL_CP_CONTAINED; + } + } + } + + // Finish. + if (all) { + spanNotSet.freeze(); + } + } + + /** + * 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). + */ + public boolean needsStringSpanUTF16() { + return someRelevant; + } + + /** For fast UnicodeSet::contains(c). */ + public boolean contains(int c) { + return spanSet.contains(c); + } + + /** + * Adds a starting or ending string character to the spanNotSet so that a + * character span ends before any string. + */ + private void addToSpanNotSet(int c) { + if (spanNotSet == null || spanNotSet == spanSet) { + if (spanSet.contains(c)) { + return; // Nothing to do. + } + spanNotSet = spanSet.cloneAsThawed(); + } + spanNotSet.add(c); + } + + /* + * 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(SpanCondition.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(SpanCondition.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(SpanCondition.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(SpanCondition.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(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(SpanCondition.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(SpanCondition.CONTAINED). Stop if spanLength==0, + * otherwise continue the loop. + */ + /** + * Spans a string. + * + * @param s The string to be spanned + * @param start The start index that the span begins + * @param spanCondition The span condition + * @return the limit (exclusive end) of the span + */ + public int span(CharSequence s, int start, SpanCondition spanCondition) { + if (spanCondition == SpanCondition.NOT_CONTAINED) { + return spanNot(s, start, null); + } + int spanLimit = spanSet.span(s, start, SpanCondition.CONTAINED); + if (spanLimit == s.length()) { + return spanLimit; + } + return spanWithStrings(s, start, spanLimit, spanCondition); + } + + /** + * Synchronized method for complicated spans using the offsets. Avoids + * synchronization for simple cases. + * + * @param spanLimit = spanSet.span(s, start, CONTAINED) + */ + private synchronized int spanWithStrings(CharSequence s, int start, int spanLimit, SpanCondition spanCondition) { + // Consider strings; they may overlap with the span. + int initSize = 0; + if (spanCondition == SpanCondition.CONTAINED) { + // Use offset list to try all possibilities. + initSize = maxLength16; + } + offsets.setMaxLength(initSize); + int length = s.length(); + int pos = spanLimit, rest = length - spanLimit; + int spanLength = spanLimit - start; + int i, stringsLength = strings.size(); + for (;;) { + if (spanCondition == SpanCondition.CONTAINED) { + for (i = 0; i < stringsLength; ++i) { + int overlap = spanLengths[i]; + if (overlap == ALL_CP_CONTAINED) { + continue; // Irrelevant string. + } + String string = strings.get(i); + + int length16 = string.length(); + + // 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. + overlap = string.offsetByCodePoints(overlap, -1); // Length of the string minus the last code + // point. + } + if (overlap > spanLength) { + overlap = spanLength; + } + int 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, string, length16)) { + if (inc == rest) { + return length; // Reached the end of the string. + } + offsets.addOffset(inc); + } + if (overlap == 0) { + break; + } + --overlap; + ++inc; + } + } + } else /* SIMPLE */ { + int maxInc = 0, maxOverlap = 0; + for (i = 0; i < stringsLength; ++i) { + int overlap = spanLengths[i]; + // For longest match, we do need to try to match even an all-contained string + // to find the match from the earliest start. + + String string = strings.get(i); + + int length16 = string.length(); + + // Try to match this string at pos-overlap..pos. + if (overlap >= 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; + } + int inc = length16 - overlap; // Keep overlap+inc==length16. + for (;;) { + if (inc > rest || overlap < maxOverlap) { + break; + } + // Try to match if the string is longer or starts earlier. + if ((overlap > maxOverlap || /* redundant overlap==maxOverlap && */inc > maxInc) + && matches16CPB(s, pos - overlap, length, string, 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. + spanLimit = spanSet.span(s, pos, SpanCondition.CONTAINED); + spanLength = spanLimit - pos; + if (spanLength == rest || // Reached the end of the string, or + spanLength == 0 // neither strings nor span progressed. + ) { + return spanLimit; + } + 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. + } + } + int minOffset = offsets.popMinimum(null); + pos += minOffset; + rest -= minOffset; + spanLength = 0; // Match strings from after a string match. + } + } + + /** + * Spans a string and counts the smallest number of set elements on any path + * across the span. + * + *

+ * For proper counting, we cannot ignore strings that are fully contained in + * code point spans. + * + *

+ * If the set does not have any fully-contained strings, then we could optimize + * this like span(), but such sets are likely rare, and this is at least still + * linear. + * + * @param s The string to be spanned + * @param start The start index that the span begins + * @param spanCondition The span condition + * @param outCount The count + * @return the limit (exclusive end) of the span + */ + public int spanAndCount(CharSequence s, int start, SpanCondition spanCondition, OutputInt outCount) { + if (spanCondition == SpanCondition.NOT_CONTAINED) { + return spanNot(s, start, outCount); + } + // Consider strings; they may overlap with the span, + // and they may result in a smaller count that with just code points. + if (spanCondition == SpanCondition.CONTAINED) { + return spanContainedAndCount(s, start, outCount); + } + // SIMPLE (not synchronized, does not use offsets) + int stringsLength = strings.size(); + int length = s.length(); + int pos = start; + int rest = length - start; + int count = 0; + while (rest != 0) { + // Try to match the next code point. + int cpLength = spanOne(spanSet, s, pos, rest); + int maxInc = (cpLength > 0) ? cpLength : 0; + // Try to match all of the strings. + for (int i = 0; i < stringsLength; ++i) { + String string = strings.get(i); + int length16 = string.length(); + if (maxInc < length16 && length16 <= rest && matches16CPB(s, pos, length, string, length16)) { + maxInc = length16; + } + } + // We are done if there is no match beyond pos. + if (maxInc == 0) { + outCount.value = count; + return pos; + } + // Continue from the longest match. + ++count; + pos += maxInc; + rest -= maxInc; + } + outCount.value = count; + return pos; + } + + private synchronized int spanContainedAndCount(CharSequence s, int start, OutputInt outCount) { + // Use offset list to try all possibilities. + offsets.setMaxLength(maxLength16); + int stringsLength = strings.size(); + int length = s.length(); + int pos = start; + int rest = length - start; + int count = 0; + while (rest != 0) { + // Try to match the next code point. + int cpLength = spanOne(spanSet, s, pos, rest); + if (cpLength > 0) { + offsets.addOffsetAndCount(cpLength, count + 1); + } + // Try to match all of the strings. + for (int i = 0; i < stringsLength; ++i) { + String string = strings.get(i); + int length16 = string.length(); + // Note: If the strings were sorted by length, then we could also + // avoid trying to match if there is already a match of the same length. + if (length16 <= rest && !offsets.hasCountAtOffset(length16, count + 1) + && matches16CPB(s, pos, length, string, length16)) { + offsets.addOffsetAndCount(length16, count + 1); + } + } + // We are done if there is no match beyond pos. + if (offsets.isEmpty()) { + outCount.value = count; + return pos; + } + // Continue from the nearest match. + int minOffset = offsets.popMinimum(outCount); + count = outCount.value; + pos += minOffset; + rest -= minOffset; + } + outCount.value = count; + return pos; + } + + /** + * Span a string backwards. + * + * @param s The string to be spanned + * @param spanCondition The span condition + * @return The string index which starts the span (i.e. inclusive). + */ + public synchronized int spanBack(CharSequence s, int length, SpanCondition spanCondition) { + if (spanCondition == SpanCondition.NOT_CONTAINED) { + return spanNotBack(s, length); + } + int pos = spanSet.spanBack(s, length, SpanCondition.CONTAINED); + if (pos == 0) { + return 0; + } + int spanLength = length - pos; + + // Consider strings; they may overlap with the span. + int initSize = 0; + if (spanCondition == SpanCondition.CONTAINED) { + // Use offset list to try all possibilities. + initSize = maxLength16; + } + offsets.setMaxLength(initSize); + int i, stringsLength = strings.size(); + int spanBackLengthsOffset = 0; + if (all) { + spanBackLengthsOffset = stringsLength; + } + for (;;) { + if (spanCondition == SpanCondition.CONTAINED) { + for (i = 0; i < stringsLength; ++i) { + int overlap = spanLengths[spanBackLengthsOffset + i]; + if (overlap == ALL_CP_CONTAINED) { + continue; // Irrelevant string. + } + String string = strings.get(i); + + int length16 = string.length(); + + // 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. + int len1 = 0; + len1 = string.offsetByCodePoints(0, 1); + overlap -= len1; // Length of the string minus the first code point. + } + if (overlap > spanLength) { + overlap = spanLength; + } + int 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, string, length16)) { + if (dec == pos) { + return 0; // Reached the start of the string. + } + offsets.addOffset(dec); + } + if (overlap == 0) { + break; + } + --overlap; + ++dec; + } + } + } else /* SIMPLE */ { + int maxDec = 0, maxOverlap = 0; + for (i = 0; i < stringsLength; ++i) { + int overlap = spanLengths[spanBackLengthsOffset + i]; + // For longest match, we do need to try to match even an all-contained string + // to find the match from the latest end. + + String string = strings.get(i); + + int length16 = string.length(); + + // Try to match this string at pos-(length16-overlap)..pos-length16. + if (overlap >= 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; + } + int dec = length16 - overlap; // Keep dec+overlap==length16. + for (;;) { + if (dec > pos || overlap < maxOverlap) { + break; + } + // Try to match if the string is longer or ends later. + if ((overlap > maxOverlap || /* redundant overlap==maxOverlap && */dec > maxDec) + && matches16CPB(s, pos - dec, length, string, 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. + int oldPos = pos; + pos = spanSet.spanBack(s, oldPos, SpanCondition.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(null); + spanLength = 0; // Match strings from before a string match. + } + } + + /** + * Algorithm for spanNot()==span(SpanCondition.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(SpanCondition.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. + * + * @param s The string to be spanned + * @param start The start index that the span begins + * @param outCount If not null: Receives the number of code points across the + * span. + * @return the limit (exclusive end) of the span + */ + private int spanNot(CharSequence s, int start, OutputInt outCount) { + int length = s.length(); + int pos = start, rest = length - start; + int stringsLength = strings.size(); + int count = 0; + do { + // Span until we find a code point from the set, + // or a code point that starts or ends some string. + int spanLimit; + if (outCount == null) { + spanLimit = spanNotSet.span(s, pos, SpanCondition.NOT_CONTAINED); + } else { + spanLimit = spanNotSet.spanAndCount(s, pos, SpanCondition.NOT_CONTAINED, outCount); + outCount.value = count = count + outCount.value; + } + if (spanLimit == length) { + return length; // Reached the end of the string. + } + pos = spanLimit; + rest = length - spanLimit; + + // Check whether the current code point is in the original set, + // without the string starts and ends. + int 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 (int i = 0; i < stringsLength; ++i) { + if (spanLengths[i] == ALL_CP_CONTAINED) { + continue; // Irrelevant string. + } + String string = strings.get(i); + + int length16 = string.length(); + if (length16 <= rest && matches16CPB(s, pos, length, string, 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; + ++count; + } while (rest != 0); + if (outCount != null) { + outCount.value = count; + } + return length; // Reached the end of the string. + } + + private int spanNotBack(CharSequence s, int length) { + int pos = length; + int 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 = spanNotSet.spanBack(s, pos, SpanCondition.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. + int 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; i < stringsLength; ++i) { + // Use spanLengths rather than a spanLengths pointer because + // it is easier and we only need to know whether the string is irrelevant + // which is the same in either array. + if (spanLengths[i] == ALL_CP_CONTAINED) { + continue; // Irrelevant string. + } + String string = strings.get(i); + + int length16 = string.length(); + if (length16 <= pos && matches16CPB(s, pos - length16, length, string, 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. + } + + static short makeSpanLengthByte(int spanLength) { + // 0xfe==UnicodeSetStringSpan::LONG_SPAN + return spanLength < LONG_SPAN ? (short) spanLength : LONG_SPAN; + } + + // Compare strings without any argument checks. Requires length>0. + private static boolean matches16(CharSequence s, int start, final String t, int length) { + int end = start + length; + while (length-- > 0) { + if (s.charAt(--end) != t.charAt(length)) { + return false; + } + } + 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. + * + * @param s The string to match in. + * @param start The start index of s. + * @param limit The limit of the subsequence of s being spanned. + * @param t The substring to be matched in s. + * @param tlength The length of t. + */ + static boolean matches16CPB(CharSequence s, int start, int limit, final String t, int tlength) { + return matches16(s, start, t, tlength) + && !(0 < start && Character.isHighSurrogate(s.charAt(start - 1)) + && Character.isLowSurrogate(s.charAt(start))) + && !((start + tlength) < limit && Character.isHighSurrogate(s.charAt(start + tlength - 1)) + && Character.isLowSurrogate(s.charAt(start + tlength))); + } + + /** + * Does the set contain the next code point? If so, return its length; otherwise + * return its negative length. + */ + static int spanOne(final UnicodeSet set, CharSequence s, int start, int length) { + char c = s.charAt(start); + if (c >= 0xd800 && c <= 0xdbff && length >= 2) { + char c2 = s.charAt(start + 1); + if (UTF16.isTrailSurrogate(c2)) { + int supplementary = UCharacterProperty.getRawSupplementary(c, c2); + return set.contains(supplementary) ? 2 : -2; + } + } + return set.contains(c) ? 1 : -1; + } + + static int spanOneBack(final UnicodeSet set, CharSequence s, int length) { + char c = s.charAt(length - 1); + if (c >= 0xdc00 && c <= 0xdfff && length >= 2) { + char c2 = s.charAt(length - 2); + if (UTF16.isLeadSurrogate(c2)) { + int supplementary = UCharacterProperty.getRawSupplementary(c2, c); + return set.contains(supplementary) ? 2 : -2; + } + } + return set.contains(c) ? 1 : -1; + } + + /** + * Helper class for UnicodeSetStringSpan. + * + *

+ * List of offsets from the current position from where to try matching a code + * point or a string. Stores 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. + * + *

+ * This class optionally also tracks the minimum non-negative count for each + * position, intended to count the smallest number of elements of any path + * leading to that position. + * + *

+ * The implementation uses a circular buffer of count integers, each indicating + * whether the corresponding offset is in the list, and its path element count. + * 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. + * + *

+ * Note: An earlier version did not track counts and stored only byte flags. + * With boolean flags, 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. + */ + private static final class OffsetList { + private int[] list; + private int length; + private int start; + + public OffsetList() { + list = new int[16]; // default size + } + + public void setMaxLength(int maxLength) { + if (maxLength > list.length) { + list = new int[maxLength]; + } + clear(); + } + + public void clear() { + for (int i = list.length; i-- > 0;) { + list[i] = 0; + } + start = length = 0; + } + + public boolean isEmpty() { + return (length == 0); + } + + /** + * Reduces 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. + * + * @param delta [1..maxLength] + */ + public void shift(int delta) { + int i = start + delta; + if (i >= list.length) { + i -= list.length; + } + if (list[i] != 0) { + list[i] = 0; + --length; + } + start = i; + } + + /** + * Adds an offset. The list must not contain it yet. + * + * @param offset [1..maxLength] + */ + public void addOffset(int offset) { + int i = start + offset; + if (i >= list.length) { + i -= list.length; + } + assert list[i] == 0; + list[i] = 1; + ++length; + } + + /** + * Adds an offset and updates its count. The list may already contain the + * offset. + * + * @param offset [1..maxLength] + */ + public void addOffsetAndCount(int offset, int count) { + assert count > 0; + int i = start + offset; + if (i >= list.length) { + i -= list.length; + } + if (list[i] == 0) { + list[i] = count; + ++length; + } else if (count < list[i]) { + list[i] = count; + } + } + + /** + * @param offset [1..maxLength] + */ + public boolean containsOffset(int offset) { + int i = start + offset; + if (i >= list.length) { + i -= list.length; + } + return list[i] != 0; + } + + /** + * @param offset [1..maxLength] + */ + public boolean hasCountAtOffset(int offset, int count) { + int i = start + offset; + if (i >= list.length) { + i -= list.length; + } + int oldCount = list[i]; + return oldCount != 0 && oldCount <= count; + } + + /** + * Finds the lowest stored offset from a non-empty list, removes it, and reduces + * all other offsets by this minimum. + * + * @return min=[1..maxLength] + */ + public int popMinimum(OutputInt outCount) { + // Look for the next offset in list[start+1..list.length-1]. + int i = start, result; + while (++i < list.length) { + int count = list[i]; + if (count != 0) { + list[i] = 0; + --length; + result = i - start; + start = i; + if (outCount != null) { + outCount.value = count; + } + return result; + } + } + // i==list.length + + // Wrap around and look for the next offset in list[0..start]. + // Since the list is not empty, there will be one. + result = list.length - start; + i = 0; + int count; + while ((count = list[i]) == 0) { + ++i; + } + list[i] = 0; + --length; + start = i; + if (outCount != null) { + outCount.value = count; + } + return result + i; + } + } +} diff --git a/src/main/java/jdk_internal/icu/impl/Utility.java b/src/main/java/jdk_internal/icu/impl/Utility.java index c1915b15..60b58284 100755 --- a/src/main/java/jdk_internal/icu/impl/Utility.java +++ b/src/main/java/jdk_internal/icu/impl/Utility.java @@ -1,266 +1,266 @@ -/* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -/* - ******************************************************************************* - * Copyright (C) 1996-2011, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - */ - -package jdk_internal.icu.impl; - -import java.io.IOException; -import java.util.Locale; - -import jdk_internal.icu.lang.UCharacter; -import jdk_internal.icu.text.UTF16; - -public final class Utility { - - /** - * Convert characters outside the range U+0020 to U+007F to Unicode escapes, and - * convert backslash to a double backslash. - */ - public static final String escape(String s) { - StringBuilder buf = new StringBuilder(); - for (int i = 0; i < s.length();) { - int c = Character.codePointAt(s, i); - i += UTF16.getCharCount(c); - if (c >= ' ' && c <= 0x007F) { - if (c == '\\') { - buf.append("\\\\"); // That is, "\\" - } else { - buf.append((char) c); - } - } else { - boolean four = c <= 0xFFFF; - buf.append(four ? "\\u" : "\\U"); - buf.append(hex(c, four ? 4 : 8)); - } - } - return buf.toString(); - } - - /* This map must be in ASCENDING ORDER OF THE ESCAPE CODE */ - private static final char[] 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 }; - - /** - * Convert an escape to a 32-bit code point value. We attempt to parallel the - * icu4c unescapeAt() function. - * - * @param offset16 an array containing offset to the character after - * the backslash. Upon return offset16[0] will be updated to - * point after the escape sequence. - * @return character value from 0 to 10FFFF, or -1 on error. - */ - public static int unescapeAt(String s, int[] offset16) { - int c; - int result = 0; - int n = 0; - int minDig = 0; - int maxDig = 0; - int bitsPerDigit = 4; - int dig; - int i; - boolean braces = false; - - /* Check that offset is in range */ - int offset = offset16[0]; - int length = s.length(); - if (offset < 0 || offset >= length) { - return -1; - } - - /* Fetch first UChar after '\\' */ - c = Character.codePointAt(s, offset); - offset += UTF16.getCharCount(c); - - /* Convert hexadecimal and octal escapes */ - switch (c) { - case 'u': - minDig = maxDig = 4; - break; - case 'U': - minDig = maxDig = 8; - break; - case 'x': - minDig = 1; - if (offset < length && UTF16.charAt(s, offset) == 0x7B /* { */) { - ++offset; - braces = true; - maxDig = 8; - } else { - maxDig = 2; - } - break; - default: - dig = UCharacter.digit(c, 8); - 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 = UTF16.charAt(s, offset); - dig = UCharacter.digit(c, (bitsPerDigit == 3) ? 8 : 16); - if (dig < 0) { - break; - } - result = (result << bitsPerDigit) | dig; - offset += UTF16.getCharCount(c); - ++n; - } - if (n < minDig) { - return -1; - } - if (braces) { - if (c != 0x7D /* } */) { - return -1; - } - ++offset; - } - if (result < 0 || result >= 0x110000) { - return -1; - } - // 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 && UTF16.isLeadSurrogate((char) result)) { - int ahead = offset + 1; - c = s.charAt(offset); // [sic] get 16-bit code unit - if (c == '\\' && ahead < length) { - int o[] = new int[] { ahead }; - c = unescapeAt(s, o); - ahead = o[0]; - } - if (UTF16.isTrailSurrogate((char) c)) { - offset = ahead; - result = UCharacterProperty.getRawSupplementary((char) result, (char) c); - } - } - offset16[0] = offset; - return result; - } - - /* Convert C-style escapes in table */ - for (i = 0; i < UNESCAPE_MAP.length; i += 2) { - if (c == UNESCAPE_MAP[i]) { - offset16[0] = offset; - return UNESCAPE_MAP[i + 1]; - } else if (c < UNESCAPE_MAP[i]) { - break; - } - } - - /* Map \cX to control-X: X & 0x1F */ - if (c == 'c' && offset < length) { - c = UTF16.charAt(s, offset); - offset16[0] = offset + UTF16.getCharCount(c); - return 0x1F & c; - } - - /* - * If no special forms are recognized, then consider the backslash to - * generically escape the next character. - */ - offset16[0] = offset; - return c; - } - - /** - * Supplies a zero-padded hex representation of an integer (without 0x) - */ - public static String hex(long i, int places) { - if (i == Long.MIN_VALUE) - return "-8000000000000000"; - boolean negative = i < 0; - if (negative) { - i = -i; - } - String result = Long.toString(i, 16).toUpperCase(Locale.ENGLISH); - if (result.length() < places) { - result = "0000000000000000".substring(result.length(), places) + result; - } - if (negative) { - return '-' + result; - } - return result; - } - - static final char DIGITS[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', - 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; - - /** - * Return true if the character is NOT printable ASCII. The tab, newline and - * linefeed characters are considered unprintable. - */ - public static boolean isUnprintable(int c) { - // 0x20 = 32 and 0x7E = 126 - return !(c >= 0x20 && c <= 0x7E); - } - - /** - * Escape unprintable characters 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. - */ - public static boolean escapeUnprintable(T result, int c) { - try { - if (isUnprintable(c)) { - result.append('\\'); - if ((c & ~0xFFFF) != 0) { - result.append('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('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 true; - } - return false; - } catch (IOException e) { - throw new IllegalArgumentException(e); - } - } -} +/* + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + ******************************************************************************* + * Copyright (C) 1996-2011, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + */ + +package jdk_internal.icu.impl; + +import java.io.IOException; +import java.util.Locale; + +import jdk_internal.icu.lang.UCharacter; +import jdk_internal.icu.text.UTF16; + +public final class Utility { + + /** + * Convert characters outside the range U+0020 to U+007F to Unicode escapes, and + * convert backslash to a double backslash. + */ + public static final String escape(String s) { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < s.length();) { + int c = Character.codePointAt(s, i); + i += UTF16.getCharCount(c); + if (c >= ' ' && c <= 0x007F) { + if (c == '\\') { + buf.append("\\\\"); // That is, "\\" + } else { + buf.append((char) c); + } + } else { + boolean four = c <= 0xFFFF; + buf.append(four ? "\\u" : "\\U"); + buf.append(hex(c, four ? 4 : 8)); + } + } + return buf.toString(); + } + + /* This map must be in ASCENDING ORDER OF THE ESCAPE CODE */ + private static final char[] 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 }; + + /** + * Convert an escape to a 32-bit code point value. We attempt to parallel the + * icu4c unescapeAt() function. + * + * @param offset16 an array containing offset to the character after + * the backslash. Upon return offset16[0] will be updated to + * point after the escape sequence. + * @return character value from 0 to 10FFFF, or -1 on error. + */ + public static int unescapeAt(String s, int[] offset16) { + int c; + int result = 0; + int n = 0; + int minDig = 0; + int maxDig = 0; + int bitsPerDigit = 4; + int dig; + int i; + boolean braces = false; + + /* Check that offset is in range */ + int offset = offset16[0]; + int length = s.length(); + if (offset < 0 || offset >= length) { + return -1; + } + + /* Fetch first UChar after '\\' */ + c = Character.codePointAt(s, offset); + offset += UTF16.getCharCount(c); + + /* Convert hexadecimal and octal escapes */ + switch (c) { + case 'u': + minDig = maxDig = 4; + break; + case 'U': + minDig = maxDig = 8; + break; + case 'x': + minDig = 1; + if (offset < length && UTF16.charAt(s, offset) == 0x7B /* { */) { + ++offset; + braces = true; + maxDig = 8; + } else { + maxDig = 2; + } + break; + default: + dig = UCharacter.digit(c, 8); + 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 = UTF16.charAt(s, offset); + dig = UCharacter.digit(c, (bitsPerDigit == 3) ? 8 : 16); + if (dig < 0) { + break; + } + result = (result << bitsPerDigit) | dig; + offset += UTF16.getCharCount(c); + ++n; + } + if (n < minDig) { + return -1; + } + if (braces) { + if (c != 0x7D /* } */) { + return -1; + } + ++offset; + } + if (result < 0 || result >= 0x110000) { + return -1; + } + // 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 && UTF16.isLeadSurrogate((char) result)) { + int ahead = offset + 1; + c = s.charAt(offset); // [sic] get 16-bit code unit + if (c == '\\' && ahead < length) { + int o[] = new int[] { ahead }; + c = unescapeAt(s, o); + ahead = o[0]; + } + if (UTF16.isTrailSurrogate((char) c)) { + offset = ahead; + result = UCharacterProperty.getRawSupplementary((char) result, (char) c); + } + } + offset16[0] = offset; + return result; + } + + /* Convert C-style escapes in table */ + for (i = 0; i < UNESCAPE_MAP.length; i += 2) { + if (c == UNESCAPE_MAP[i]) { + offset16[0] = offset; + return UNESCAPE_MAP[i + 1]; + } else if (c < UNESCAPE_MAP[i]) { + break; + } + } + + /* Map \cX to control-X: X & 0x1F */ + if (c == 'c' && offset < length) { + c = UTF16.charAt(s, offset); + offset16[0] = offset + UTF16.getCharCount(c); + return 0x1F & c; + } + + /* + * If no special forms are recognized, then consider the backslash to + * generically escape the next character. + */ + offset16[0] = offset; + return c; + } + + /** + * Supplies a zero-padded hex representation of an integer (without 0x) + */ + public static String hex(long i, int places) { + if (i == Long.MIN_VALUE) + return "-8000000000000000"; + boolean negative = i < 0; + if (negative) { + i = -i; + } + String result = Long.toString(i, 16).toUpperCase(Locale.ENGLISH); + if (result.length() < places) { + result = "0000000000000000".substring(result.length(), places) + result; + } + if (negative) { + return '-' + result; + } + return result; + } + + static final char DIGITS[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; + + /** + * Return true if the character is NOT printable ASCII. The tab, newline and + * linefeed characters are considered unprintable. + */ + public static boolean isUnprintable(int c) { + // 0x20 = 32 and 0x7E = 126 + return !(c >= 0x20 && c <= 0x7E); + } + + /** + * Escape unprintable characters 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. + */ + public static boolean escapeUnprintable(T result, int c) { + try { + if (isUnprintable(c)) { + result.append('\\'); + if ((c & ~0xFFFF) != 0) { + result.append('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('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 true; + } + return false; + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + } +} diff --git a/src/main/java/jdk_internal/icu/lang/UCharacter.java b/src/main/java/jdk_internal/icu/lang/UCharacter.java index e47952e0..f9e1f9b7 100755 --- a/src/main/java/jdk_internal/icu/lang/UCharacter.java +++ b/src/main/java/jdk_internal/icu/lang/UCharacter.java @@ -1,562 +1,562 @@ -/* - * Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** -******************************************************************************* -* Copyright (C) 1996-2014, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -*/ - -package jdk_internal.icu.lang; - -import jdk_internal.icu.impl.UBiDiProps; -import jdk_internal.icu.impl.UCharacterProperty; -import jdk_internal.icu.text.Normalizer2; -import jdk_internal.icu.text.UTF16; -import jdk_internal.icu.util.VersionInfo; - -/** - *

- * The UCharacter class provides extensions to the - * - * java.lang.Character class. These extensions provide support for more - * Unicode properties and together with the UTF16 - * class, provide support for supplementary characters (those with code points - * above U+FFFF). Each ICU release supports the latest version of Unicode - * available at that time. - * - *

- * Code points are represented in these API using ints. While it would be more - * convenient in Java to have a separate primitive datatype for them, ints - * suffice in the meantime. - * - *

- * To use this class please add the jar file name icu4j.jar to the class path, - * since it contains data files which supply the information used by this - * file.
- * E.g. In Windows
- * set CLASSPATH=%CLASSPATH%;$JAR_FILE_PATH/ucharacter.jar.
- * Otherwise, another method would be to copy the files uprops.dat and - * unames.icu from the icu4j source subdirectory - * $ICU4J_SRC/src/com.ibm.icu.impl.data to your class directory - * $ICU4J_CLASS/com.ibm.icu.impl.data. - * - *

- * Aside from the additions for UTF-16 support, and the updated Unicode - * properties, the main differences between UCharacter and Character are: - *

    - *
  • UCharacter is not designed to be a char wrapper and does not have APIs to - * which involves management of that single char.
    - * These include: - *
      - *
    • char charValue(), - *
    • int compareTo(java.lang.Character, java.lang.Character), etc. - *
    - *
  • UCharacter does not include Character APIs that are deprecated, nor does - * it include the Java-specific character information, such as boolean - * isJavaIdentifierPart(char ch). - *
  • Character maps characters 'A' - 'Z' and 'a' - 'z' to the numeric values - * '10' - '35'. UCharacter also does this in digit and getNumericValue, to - * adhere to the java semantics of these methods. New methods unicodeDigit, and - * getUnicodeNumericValue do not treat the above code points as having numeric - * values. This is a semantic change from ICU4J 1.3.1. - *
- *

- * Further detail on differences can be determined using the program - * com.ibm.icu.dev.test.lang.UCharacterCompare - *

- *

- * In addition to Java compatibility functions, which calculate derived - * properties, this API provides low-level access to the Unicode Character - * Database. - *

- *

- * 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 - * (http://www.icu-project.org/userguide/properties.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: - * - *

{@code
- * - alpha:     isUAlphabetic(c) or hasBinaryProperty(c, UProperty.ALPHABETIC)
- * - lower:     isULowercase(c) or hasBinaryProperty(c, UProperty.LOWERCASE)
- * - upper:     isUUppercase(c) or hasBinaryProperty(c, UProperty.UPPERCASE)
- * - punct:     ((1<
- * 

- *

- * The C/POSIX character classes are also available in UnicodeSet patterns, - * using patterns like [:graph:] or \p{graph}. - *

- * - * There are several ICU (and Java) whitespace functions. Comparison: - *
    - *
  • 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 and ZWSP) - *
  • isWhitespace: Java isWhitespace; Z + whitespace ISO controls but - * excluding no-break spaces - *
  • isSpaceChar: just Z (including no-break spaces) - *
- *

- *

- * This class is not subclassable. - *

- * - * @author Syn Wee Quek - * @stable ICU 2.1 - * @see com.ibm.icu.lang.UCharacterEnums - */ - -public final class UCharacter { - - /** - * Joining Group constants. - * - * @see UProperty#JOINING_GROUP - * @stable ICU 2.4 - */ - public static interface JoiningGroup { - /** - * @stable ICU 2.4 - */ - public static final int NO_JOINING_GROUP = 0; - } - - /** - * Numeric Type constants. - * - * @see UProperty#NUMERIC_TYPE - * @stable ICU 2.4 - */ - public static interface NumericType { - /** - * @stable ICU 2.4 - */ - public static final int NONE = 0; - /** - * @stable ICU 2.4 - */ - public static final int DECIMAL = 1; - /** - * @stable ICU 2.4 - */ - public static final int DIGIT = 2; - /** - * @stable ICU 2.4 - */ - public static final int NUMERIC = 3; - /** - * @stable ICU 2.4 - */ - public static final int COUNT = 4; - } - - /** - * Hangul Syllable Type constants. - * - * @see UProperty#HANGUL_SYLLABLE_TYPE - * @stable ICU 2.6 - */ - public static interface HangulSyllableType { - /** - * @stable ICU 2.6 - */ - public static final int NOT_APPLICABLE = 0; /* [NA] */ /* See note !! */ - /** - * @stable ICU 2.6 - */ - public static final int LEADING_JAMO = 1; /* [L] */ - /** - * @stable ICU 2.6 - */ - public static final int VOWEL_JAMO = 2; /* [V] */ - /** - * @stable ICU 2.6 - */ - public static final int TRAILING_JAMO = 3; /* [T] */ - /** - * @stable ICU 2.6 - */ - public static final int LV_SYLLABLE = 4; /* [LV] */ - /** - * @stable ICU 2.6 - */ - public static final int LVT_SYLLABLE = 5; /* [LVT] */ - /** - * @stable ICU 2.6 - */ - public static final int COUNT = 6; - } - - // public data members ----------------------------------------------- - - /** - * The lowest Unicode code point value. - * - * @stable ICU 2.1 - */ - public static final int MIN_VALUE = UTF16.CODEPOINT_MIN_VALUE; - - /** - * The highest Unicode code point value (scalar value) according to the Unicode - * Standard. This is a 21-bit value (21 bits, rounded up).
- * Up-to-date Unicode implementation of java.lang.Character.MAX_VALUE - * - * @stable ICU 2.1 - */ - public static final int MAX_VALUE = UTF16.CODEPOINT_MAX_VALUE; - - // public methods ---------------------------------------------------- - - /** - * Returns the numeric value of a decimal digit code point.
- * This method observes the semantics of - * java.lang.Character.digit(). Note that this will return positive - * values for code points for which isDigit returns false, just like - * java.lang.Character.
- * Semantic Change: In release 1.3.1 and prior, this did not treat the - * European letters as having a digit value, and also treated numeric letters - * and other numbers as digits. This has been changed to conform to the java - * semantics.
- * A code point is a valid digit if and only if: - *
    - *
  • ch is a decimal digit or one of the european letters, and - *
  • the value of ch is less than the specified radix. - *
- * - * @param ch the code point to query - * @param radix the radix - * @return the numeric value represented by the code point in the specified - * radix, or -1 if the code point is not a decimal digit or if its value - * is too large for the radix - * @stable ICU 2.1 - */ - public static int digit(int ch, int radix) { - if (2 <= radix && radix <= 36) { - int value = digit(ch); - if (value < 0) { - // ch is not a decimal digit, try latin letters - value = UCharacterProperty.getEuropeanDigit(ch); - } - return (value < radix) ? value : -1; - } else { - return -1; // invalid radix - } - } - - /** - * Returns the numeric value of a decimal digit code point.
- * This is a convenience overload of digit(int, int) that provides - * a decimal radix.
- * Semantic Change: In release 1.3.1 and prior, this treated numeric - * letters and other numbers as digits. This has been changed to conform to the - * java semantics. - * - * @param ch the code point to query - * @return the numeric value represented by the code point, or -1 if the code - * point is not a decimal digit or if its value is too large for a - * decimal radix - * @stable ICU 2.1 - */ - public static int digit(int ch) { - return UCharacterProperty.INSTANCE.digit(ch); - } - - /** - * Returns a value indicating a code point's Unicode category. Up-to-date - * Unicode implementation of java.lang.Character.getType() except for the above - * mentioned code points that had their category changed.
- * Return results are constants from the interface - * UCharacterCategory
- * NOTE: the UCharacterCategory values are not compatible with - * those returned by java.lang.Character.getType. UCharacterCategory values - * match the ones used in ICU4C, while java.lang.Character type values, though - * similar, skip the value 17. - *

- * - * @param ch code point whose type is to be determined - * @return category which is a value of UCharacterCategory - * @stable ICU 2.1 - */ - public static int getType(int ch) { - return UCharacterProperty.INSTANCE.getType(ch); - } - - /** - * Returns the Bidirection property of a code point. For example, 0x0041 (letter - * A) has the LEFT_TO_RIGHT directional property.
- * Result returned belongs to the interface - * UCharacterDirection - * - * @param ch the code point to be determined its direction - * @return direction constant from UCharacterDirection. - * @stable ICU 2.1 - */ - public static int getDirection(int ch) { - return UBiDiProps.INSTANCE.getClass(ch); - } - - /** - * Maps the specified code point to a "mirror-image" code point. For code points - * with the "mirrored" property, implementations sometimes need a "poor man's" - * mapping to another code point such that the default glyph may serve as the - * mirror-image of the default glyph of the specified code point.
- * This is useful for text conversion to and from codepages with visual order, - * and for displays without glyph selection capabilities. - * - * @param ch code point whose mirror is to be retrieved - * @return another code point that may serve as a mirror-image substitute, or ch - * itself if there is no such mapping or ch does not have the "mirrored" - * property - * @stable ICU 2.1 - */ - public static int getMirror(int ch) { - return UBiDiProps.INSTANCE.getMirror(ch); - } - - /** - * Maps the specified character to its paired bracket character. For - * Bidi_Paired_Bracket_Type!=None, this is the same as getMirror(int). 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 UProperty#BIDI_PAIRED_BRACKET - * @see UProperty#BIDI_PAIRED_BRACKET_TYPE - * @see #getMirror(int) - * @stable ICU 52 - */ - public static int getBidiPairedBracket(int c) { - return UBiDiProps.INSTANCE.getPairedBracket(c); - } - - /** - * Returns the combining class of the argument codepoint - * - * @param ch code point whose combining is to be retrieved - * @return the combining class of the codepoint - * @stable ICU 2.1 - */ - public static int getCombiningClass(int ch) { - return Normalizer2.getNFDInstance().getCombiningClass(ch); - } - - /** - * Returns the version of Unicode data used. - * - * @return the unicode version number used - * @stable ICU 2.1 - */ - public static VersionInfo getUnicodeVersion() { - return UCharacterProperty.INSTANCE.m_unicodeVersion_; - } - - /** - * Returns a code point corresponding to the two UTF16 characters. - * - * @param lead the lead char - * @param trail the trail char - * @return code point if surrogate characters are valid. - * @exception IllegalArgumentException thrown when argument characters do not - * form a valid codepoint - * @stable ICU 2.1 - */ - public static int getCodePoint(char lead, char trail) { - if (UTF16.isLeadSurrogate(lead) && UTF16.isTrailSurrogate(trail)) { - return UCharacterProperty.getRawSupplementary(lead, trail); - } - throw new IllegalArgumentException("Illegal surrogate characters"); - } - - /** - * Returns 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 ch The code point. - * @return the Unicode version number - * @stable ICU 2.6 - */ - public static VersionInfo getAge(int ch) { - if (ch < MIN_VALUE || ch > MAX_VALUE) { - throw new IllegalArgumentException("Codepoint out of bounds"); - } - return UCharacterProperty.INSTANCE.getAge(ch); - } - - /** - * Returns the property value for an Unicode property type of 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:
-	 * int ea = UCharacter.getIntPropertyValue(c, UProperty.EAST_ASIAN_WIDTH);
-	 * int ideo = UCharacter.getIntPropertyValue(c, UProperty.IDEOGRAPHIC);
-	 * boolean b = (ideo == 1) ? true : false;
-	 * 
- * - * @param ch code point to test. - * @param type UProperty selector constant, identifies which binary property to - * check. Must be UProperty.BINARY_START <= type < - * UProperty.BINARY_LIMIT or UProperty.INT_START <= type < - * UProperty.INT_LIMIT or UProperty.MASK_START <= type < - * UProperty.MASK_LIMIT. - * @return numeric value that is directly the property value or, for enumerated - * properties, corresponds to the numeric value of the enumerated - * constant of the respective property value enumeration type (cast to - * enum type if necessary). Returns 0 or 1 (for false / true) for binary - * Unicode properties. Returns a bit-mask for mask properties. Returns 0 - * if 'type' is out of bounds or if the Unicode version does not have - * data for the property at all, or not for this code point. - * @see UProperty - * @see #hasBinaryProperty - * @see #getIntPropertyMinValue - * @see #getIntPropertyMaxValue - * @see #getUnicodeVersion - * @stable ICU 2.4 - */ - // for BiDiBase.java - public static int getIntPropertyValue(int ch, int type) { - return UCharacterProperty.INSTANCE.getIntPropertyValue(ch, type); - } - - // private constructor ----------------------------------------------- - - /** - * Private constructor to prevent instantiation - */ - private UCharacter() { - } - - /* - * Copied from UCharacterEnums.java - */ - - /** - * Character type Mn - * - * @stable ICU 2.1 - */ - public static final byte NON_SPACING_MARK = 6; - /** - * Character type Me - * - * @stable ICU 2.1 - */ - public static final byte ENCLOSING_MARK = 7; - /** - * Character type Mc - * - * @stable ICU 2.1 - */ - public static final byte COMBINING_SPACING_MARK = 8; - /** - * Character type count - * - * @stable ICU 2.1 - */ - public static final byte CHAR_CATEGORY_COUNT = 30; - - /** - * Directional type R - * - * @stable ICU 2.1 - */ - public static final int RIGHT_TO_LEFT = 1; - /** - * Directional type AL - * - * @stable ICU 2.1 - */ - public static final int RIGHT_TO_LEFT_ARABIC = 13; -} +/* + * Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** +******************************************************************************* +* Copyright (C) 1996-2014, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +*/ + +package jdk_internal.icu.lang; + +import jdk_internal.icu.impl.UBiDiProps; +import jdk_internal.icu.impl.UCharacterProperty; +import jdk_internal.icu.text.Normalizer2; +import jdk_internal.icu.text.UTF16; +import jdk_internal.icu.util.VersionInfo; + +/** + *

+ * The UCharacter class provides extensions to the + * + * java.lang.Character class. These extensions provide support for more + * Unicode properties and together with the UTF16 + * class, provide support for supplementary characters (those with code points + * above U+FFFF). Each ICU release supports the latest version of Unicode + * available at that time. + * + *

+ * Code points are represented in these API using ints. While it would be more + * convenient in Java to have a separate primitive datatype for them, ints + * suffice in the meantime. + * + *

+ * To use this class please add the jar file name icu4j.jar to the class path, + * since it contains data files which supply the information used by this + * file.
+ * E.g. In Windows
+ * set CLASSPATH=%CLASSPATH%;$JAR_FILE_PATH/ucharacter.jar.
+ * Otherwise, another method would be to copy the files uprops.dat and + * unames.icu from the icu4j source subdirectory + * $ICU4J_SRC/src/com.ibm.icu.impl.data to your class directory + * $ICU4J_CLASS/com.ibm.icu.impl.data. + * + *

+ * Aside from the additions for UTF-16 support, and the updated Unicode + * properties, the main differences between UCharacter and Character are: + *

    + *
  • UCharacter is not designed to be a char wrapper and does not have APIs to + * which involves management of that single char.
    + * These include: + *
      + *
    • char charValue(), + *
    • int compareTo(java.lang.Character, java.lang.Character), etc. + *
    + *
  • UCharacter does not include Character APIs that are deprecated, nor does + * it include the Java-specific character information, such as boolean + * isJavaIdentifierPart(char ch). + *
  • Character maps characters 'A' - 'Z' and 'a' - 'z' to the numeric values + * '10' - '35'. UCharacter also does this in digit and getNumericValue, to + * adhere to the java semantics of these methods. New methods unicodeDigit, and + * getUnicodeNumericValue do not treat the above code points as having numeric + * values. This is a semantic change from ICU4J 1.3.1. + *
+ *

+ * Further detail on differences can be determined using the program + * com.ibm.icu.dev.test.lang.UCharacterCompare + *

+ *

+ * In addition to Java compatibility functions, which calculate derived + * properties, this API provides low-level access to the Unicode Character + * Database. + *

+ *

+ * 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 + * (http://www.icu-project.org/userguide/properties.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: + * + *

{@code
+ * - alpha:     isUAlphabetic(c) or hasBinaryProperty(c, UProperty.ALPHABETIC)
+ * - lower:     isULowercase(c) or hasBinaryProperty(c, UProperty.LOWERCASE)
+ * - upper:     isUUppercase(c) or hasBinaryProperty(c, UProperty.UPPERCASE)
+ * - punct:     ((1<
+ * 

+ *

+ * The C/POSIX character classes are also available in UnicodeSet patterns, + * using patterns like [:graph:] or \p{graph}. + *

+ * + * There are several ICU (and Java) whitespace functions. Comparison: + *
    + *
  • 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 and ZWSP) + *
  • isWhitespace: Java isWhitespace; Z + whitespace ISO controls but + * excluding no-break spaces + *
  • isSpaceChar: just Z (including no-break spaces) + *
+ *

+ *

+ * This class is not subclassable. + *

+ * + * @author Syn Wee Quek + * @stable ICU 2.1 + * @see com.ibm.icu.lang.UCharacterEnums + */ + +public final class UCharacter { + + /** + * Joining Group constants. + * + * @see UProperty#JOINING_GROUP + * @stable ICU 2.4 + */ + public static interface JoiningGroup { + /** + * @stable ICU 2.4 + */ + public static final int NO_JOINING_GROUP = 0; + } + + /** + * Numeric Type constants. + * + * @see UProperty#NUMERIC_TYPE + * @stable ICU 2.4 + */ + public static interface NumericType { + /** + * @stable ICU 2.4 + */ + public static final int NONE = 0; + /** + * @stable ICU 2.4 + */ + public static final int DECIMAL = 1; + /** + * @stable ICU 2.4 + */ + public static final int DIGIT = 2; + /** + * @stable ICU 2.4 + */ + public static final int NUMERIC = 3; + /** + * @stable ICU 2.4 + */ + public static final int COUNT = 4; + } + + /** + * Hangul Syllable Type constants. + * + * @see UProperty#HANGUL_SYLLABLE_TYPE + * @stable ICU 2.6 + */ + public static interface HangulSyllableType { + /** + * @stable ICU 2.6 + */ + public static final int NOT_APPLICABLE = 0; /* [NA] */ /* See note !! */ + /** + * @stable ICU 2.6 + */ + public static final int LEADING_JAMO = 1; /* [L] */ + /** + * @stable ICU 2.6 + */ + public static final int VOWEL_JAMO = 2; /* [V] */ + /** + * @stable ICU 2.6 + */ + public static final int TRAILING_JAMO = 3; /* [T] */ + /** + * @stable ICU 2.6 + */ + public static final int LV_SYLLABLE = 4; /* [LV] */ + /** + * @stable ICU 2.6 + */ + public static final int LVT_SYLLABLE = 5; /* [LVT] */ + /** + * @stable ICU 2.6 + */ + public static final int COUNT = 6; + } + + // public data members ----------------------------------------------- + + /** + * The lowest Unicode code point value. + * + * @stable ICU 2.1 + */ + public static final int MIN_VALUE = UTF16.CODEPOINT_MIN_VALUE; + + /** + * The highest Unicode code point value (scalar value) according to the Unicode + * Standard. This is a 21-bit value (21 bits, rounded up).
+ * Up-to-date Unicode implementation of java.lang.Character.MAX_VALUE + * + * @stable ICU 2.1 + */ + public static final int MAX_VALUE = UTF16.CODEPOINT_MAX_VALUE; + + // public methods ---------------------------------------------------- + + /** + * Returns the numeric value of a decimal digit code point.
+ * This method observes the semantics of + * java.lang.Character.digit(). Note that this will return positive + * values for code points for which isDigit returns false, just like + * java.lang.Character.
+ * Semantic Change: In release 1.3.1 and prior, this did not treat the + * European letters as having a digit value, and also treated numeric letters + * and other numbers as digits. This has been changed to conform to the java + * semantics.
+ * A code point is a valid digit if and only if: + *
    + *
  • ch is a decimal digit or one of the european letters, and + *
  • the value of ch is less than the specified radix. + *
+ * + * @param ch the code point to query + * @param radix the radix + * @return the numeric value represented by the code point in the specified + * radix, or -1 if the code point is not a decimal digit or if its value + * is too large for the radix + * @stable ICU 2.1 + */ + public static int digit(int ch, int radix) { + if (2 <= radix && radix <= 36) { + int value = digit(ch); + if (value < 0) { + // ch is not a decimal digit, try latin letters + value = UCharacterProperty.getEuropeanDigit(ch); + } + return (value < radix) ? value : -1; + } else { + return -1; // invalid radix + } + } + + /** + * Returns the numeric value of a decimal digit code point.
+ * This is a convenience overload of digit(int, int) that provides + * a decimal radix.
+ * Semantic Change: In release 1.3.1 and prior, this treated numeric + * letters and other numbers as digits. This has been changed to conform to the + * java semantics. + * + * @param ch the code point to query + * @return the numeric value represented by the code point, or -1 if the code + * point is not a decimal digit or if its value is too large for a + * decimal radix + * @stable ICU 2.1 + */ + public static int digit(int ch) { + return UCharacterProperty.INSTANCE.digit(ch); + } + + /** + * Returns a value indicating a code point's Unicode category. Up-to-date + * Unicode implementation of java.lang.Character.getType() except for the above + * mentioned code points that had their category changed.
+ * Return results are constants from the interface + * UCharacterCategory
+ * NOTE: the UCharacterCategory values are not compatible with + * those returned by java.lang.Character.getType. UCharacterCategory values + * match the ones used in ICU4C, while java.lang.Character type values, though + * similar, skip the value 17. + *

+ * + * @param ch code point whose type is to be determined + * @return category which is a value of UCharacterCategory + * @stable ICU 2.1 + */ + public static int getType(int ch) { + return UCharacterProperty.INSTANCE.getType(ch); + } + + /** + * Returns the Bidirection property of a code point. For example, 0x0041 (letter + * A) has the LEFT_TO_RIGHT directional property.
+ * Result returned belongs to the interface + * UCharacterDirection + * + * @param ch the code point to be determined its direction + * @return direction constant from UCharacterDirection. + * @stable ICU 2.1 + */ + public static int getDirection(int ch) { + return UBiDiProps.INSTANCE.getClass(ch); + } + + /** + * Maps the specified code point to a "mirror-image" code point. For code points + * with the "mirrored" property, implementations sometimes need a "poor man's" + * mapping to another code point such that the default glyph may serve as the + * mirror-image of the default glyph of the specified code point.
+ * This is useful for text conversion to and from codepages with visual order, + * and for displays without glyph selection capabilities. + * + * @param ch code point whose mirror is to be retrieved + * @return another code point that may serve as a mirror-image substitute, or ch + * itself if there is no such mapping or ch does not have the "mirrored" + * property + * @stable ICU 2.1 + */ + public static int getMirror(int ch) { + return UBiDiProps.INSTANCE.getMirror(ch); + } + + /** + * Maps the specified character to its paired bracket character. For + * Bidi_Paired_Bracket_Type!=None, this is the same as getMirror(int). 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 UProperty#BIDI_PAIRED_BRACKET + * @see UProperty#BIDI_PAIRED_BRACKET_TYPE + * @see #getMirror(int) + * @stable ICU 52 + */ + public static int getBidiPairedBracket(int c) { + return UBiDiProps.INSTANCE.getPairedBracket(c); + } + + /** + * Returns the combining class of the argument codepoint + * + * @param ch code point whose combining is to be retrieved + * @return the combining class of the codepoint + * @stable ICU 2.1 + */ + public static int getCombiningClass(int ch) { + return Normalizer2.getNFDInstance().getCombiningClass(ch); + } + + /** + * Returns the version of Unicode data used. + * + * @return the unicode version number used + * @stable ICU 2.1 + */ + public static VersionInfo getUnicodeVersion() { + return UCharacterProperty.INSTANCE.m_unicodeVersion_; + } + + /** + * Returns a code point corresponding to the two UTF16 characters. + * + * @param lead the lead char + * @param trail the trail char + * @return code point if surrogate characters are valid. + * @exception IllegalArgumentException thrown when argument characters do not + * form a valid codepoint + * @stable ICU 2.1 + */ + public static int getCodePoint(char lead, char trail) { + if (UTF16.isLeadSurrogate(lead) && UTF16.isTrailSurrogate(trail)) { + return UCharacterProperty.getRawSupplementary(lead, trail); + } + throw new IllegalArgumentException("Illegal surrogate characters"); + } + + /** + * Returns 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 ch The code point. + * @return the Unicode version number + * @stable ICU 2.6 + */ + public static VersionInfo getAge(int ch) { + if (ch < MIN_VALUE || ch > MAX_VALUE) { + throw new IllegalArgumentException("Codepoint out of bounds"); + } + return UCharacterProperty.INSTANCE.getAge(ch); + } + + /** + * Returns the property value for an Unicode property type of 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:
+	 * int ea = UCharacter.getIntPropertyValue(c, UProperty.EAST_ASIAN_WIDTH);
+	 * int ideo = UCharacter.getIntPropertyValue(c, UProperty.IDEOGRAPHIC);
+	 * boolean b = (ideo == 1) ? true : false;
+	 * 
+ * + * @param ch code point to test. + * @param type UProperty selector constant, identifies which binary property to + * check. Must be UProperty.BINARY_START <= type < + * UProperty.BINARY_LIMIT or UProperty.INT_START <= type < + * UProperty.INT_LIMIT or UProperty.MASK_START <= type < + * UProperty.MASK_LIMIT. + * @return numeric value that is directly the property value or, for enumerated + * properties, corresponds to the numeric value of the enumerated + * constant of the respective property value enumeration type (cast to + * enum type if necessary). Returns 0 or 1 (for false / true) for binary + * Unicode properties. Returns a bit-mask for mask properties. Returns 0 + * if 'type' is out of bounds or if the Unicode version does not have + * data for the property at all, or not for this code point. + * @see UProperty + * @see #hasBinaryProperty + * @see #getIntPropertyMinValue + * @see #getIntPropertyMaxValue + * @see #getUnicodeVersion + * @stable ICU 2.4 + */ + // for BiDiBase.java + public static int getIntPropertyValue(int ch, int type) { + return UCharacterProperty.INSTANCE.getIntPropertyValue(ch, type); + } + + // private constructor ----------------------------------------------- + + /** + * Private constructor to prevent instantiation + */ + private UCharacter() { + } + + /* + * Copied from UCharacterEnums.java + */ + + /** + * Character type Mn + * + * @stable ICU 2.1 + */ + public static final byte NON_SPACING_MARK = 6; + /** + * Character type Me + * + * @stable ICU 2.1 + */ + public static final byte ENCLOSING_MARK = 7; + /** + * Character type Mc + * + * @stable ICU 2.1 + */ + public static final byte COMBINING_SPACING_MARK = 8; + /** + * Character type count + * + * @stable ICU 2.1 + */ + public static final byte CHAR_CATEGORY_COUNT = 30; + + /** + * Directional type R + * + * @stable ICU 2.1 + */ + public static final int RIGHT_TO_LEFT = 1; + /** + * Directional type AL + * + * @stable ICU 2.1 + */ + public static final int RIGHT_TO_LEFT_ARABIC = 13; +} diff --git a/src/main/java/jdk_internal/icu/lang/UCharacterDirection.java b/src/main/java/jdk_internal/icu/lang/UCharacterDirection.java index af9f455d..593cc50c 100755 --- a/src/main/java/jdk_internal/icu/lang/UCharacterDirection.java +++ b/src/main/java/jdk_internal/icu/lang/UCharacterDirection.java @@ -1,113 +1,113 @@ -/* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -/* -/** -******************************************************************************* -* Copyright (C) 1996-2004, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ -// CHANGELOG -// 2005-05-19 Edward Wang -// - copy this file from icu4jsrc_3_2/src/com/ibm/icu/lang/UCharacterDirection.java -// - move from package com.ibm.icu.lang to package sun.net.idn -// - -package jdk_internal.icu.lang; - -/** - * Enumerated Unicode character linguistic direction constants. Used as return - * results from UCharacter - *

- * This class is not subclassable - *

- * - * @author Syn Wee Quek - * @stable ICU 2.1 - */ - -@SuppressWarnings("deprecation") -public final class UCharacterDirection implements UCharacterEnums.ECharacterDirection { - - // private constructor ========================================= - /// CLOVER:OFF - /** - * Private constructor to prevent initialisation - */ - private UCharacterDirection() { - } - /// CLOVER:ON - - /** - * Gets the name of the argument direction - * - * @param dir direction type to retrieve name - * @return directional name - * @stable ICU 2.1 - */ - public static String toString(int dir) { - switch (dir) { - case LEFT_TO_RIGHT: - return "Left-to-Right"; - case RIGHT_TO_LEFT: - return "Right-to-Left"; - case EUROPEAN_NUMBER: - return "European Number"; - case EUROPEAN_NUMBER_SEPARATOR: - return "European Number Separator"; - case EUROPEAN_NUMBER_TERMINATOR: - return "European Number Terminator"; - case ARABIC_NUMBER: - return "Arabic Number"; - case COMMON_NUMBER_SEPARATOR: - return "Common Number Separator"; - case BLOCK_SEPARATOR: - return "Paragraph Separator"; - case SEGMENT_SEPARATOR: - return "Segment Separator"; - case WHITE_SPACE_NEUTRAL: - return "Whitespace"; - case OTHER_NEUTRAL: - return "Other Neutrals"; - case LEFT_TO_RIGHT_EMBEDDING: - return "Left-to-Right Embedding"; - case LEFT_TO_RIGHT_OVERRIDE: - return "Left-to-Right Override"; - case RIGHT_TO_LEFT_ARABIC: - return "Right-to-Left Arabic"; - case RIGHT_TO_LEFT_EMBEDDING: - return "Right-to-Left Embedding"; - case RIGHT_TO_LEFT_OVERRIDE: - return "Right-to-Left Override"; - case POP_DIRECTIONAL_FORMAT: - return "Pop Directional Format"; - case DIR_NON_SPACING_MARK: - return "Non-Spacing Mark"; - case BOUNDARY_NEUTRAL: - return "Boundary Neutral"; - } - return "Unassigned"; - } -} +/* + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* +/** +******************************************************************************* +* Copyright (C) 1996-2004, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +*/ +// CHANGELOG +// 2005-05-19 Edward Wang +// - copy this file from icu4jsrc_3_2/src/com/ibm/icu/lang/UCharacterDirection.java +// - move from package com.ibm.icu.lang to package sun.net.idn +// + +package jdk_internal.icu.lang; + +/** + * Enumerated Unicode character linguistic direction constants. Used as return + * results from UCharacter + *

+ * This class is not subclassable + *

+ * + * @author Syn Wee Quek + * @stable ICU 2.1 + */ + +@SuppressWarnings("deprecation") +public final class UCharacterDirection implements UCharacterEnums.ECharacterDirection { + + // private constructor ========================================= + /// CLOVER:OFF + /** + * Private constructor to prevent initialisation + */ + private UCharacterDirection() { + } + /// CLOVER:ON + + /** + * Gets the name of the argument direction + * + * @param dir direction type to retrieve name + * @return directional name + * @stable ICU 2.1 + */ + public static String toString(int dir) { + switch (dir) { + case LEFT_TO_RIGHT: + return "Left-to-Right"; + case RIGHT_TO_LEFT: + return "Right-to-Left"; + case EUROPEAN_NUMBER: + return "European Number"; + case EUROPEAN_NUMBER_SEPARATOR: + return "European Number Separator"; + case EUROPEAN_NUMBER_TERMINATOR: + return "European Number Terminator"; + case ARABIC_NUMBER: + return "Arabic Number"; + case COMMON_NUMBER_SEPARATOR: + return "Common Number Separator"; + case BLOCK_SEPARATOR: + return "Paragraph Separator"; + case SEGMENT_SEPARATOR: + return "Segment Separator"; + case WHITE_SPACE_NEUTRAL: + return "Whitespace"; + case OTHER_NEUTRAL: + return "Other Neutrals"; + case LEFT_TO_RIGHT_EMBEDDING: + return "Left-to-Right Embedding"; + case LEFT_TO_RIGHT_OVERRIDE: + return "Left-to-Right Override"; + case RIGHT_TO_LEFT_ARABIC: + return "Right-to-Left Arabic"; + case RIGHT_TO_LEFT_EMBEDDING: + return "Right-to-Left Embedding"; + case RIGHT_TO_LEFT_OVERRIDE: + return "Right-to-Left Override"; + case POP_DIRECTIONAL_FORMAT: + return "Pop Directional Format"; + case DIR_NON_SPACING_MARK: + return "Non-Spacing Mark"; + case BOUNDARY_NEUTRAL: + return "Boundary Neutral"; + } + return "Unassigned"; + } +} diff --git a/src/main/java/jdk_internal/icu/lang/UCharacterEnums.java b/src/main/java/jdk_internal/icu/lang/UCharacterEnums.java index 1e196dc5..62dc2bc6 100755 --- a/src/main/java/jdk_internal/icu/lang/UCharacterEnums.java +++ b/src/main/java/jdk_internal/icu/lang/UCharacterEnums.java @@ -1,666 +1,666 @@ -/* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -/* -/** - ******************************************************************************* - * Copyright (C) 2004, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - */ -// CHANGELOG -// 2005-05-19 Edward Wang -// - copy this file from icu4jsrc_3_2/src/com/ibm/icu/lang/UCharacterEnums.java -// - move from package com.ibm.icu.lang to package sun.net.idn -// -// 2011-09-06 Kurchi Subhra Hazra -// - Added @Deprecated tag to the following: -// - class UCharacterEnums -// - interfaces ECharacterCategory, ECharacterDirection -// - fields INITIAL_QUOTE_PUNCTUATION, FINAL_QUOTE_PUNCTUATION, -// DIRECTIONALITY_LEFT_TO_RIGHT, DIRECTIONALITY_RIGHT_TO_LEFT, -// DIRECTIONALITY_EUROPEAN_NUMBER, DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR -// DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR, DIRECTIONALITY_ARABIC_NUMBER, -// DIRECTIONALITY_COMMON_NUMBER_SEPARATOR, DIRECTIONALITY_PARAGRAPH_SEPARATOR, -// DIRECTIONALITY_SEGMENT_SEPARATOR, DIRECTIONALITY_WHITESPACE, -// DIRECTIONALITY_OTHER_NEUTRALS, DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING, -// DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE, DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC, -// DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING, DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE, -// DIRECTIONALITY_POP_DIRECTIONAL_FORMAT, DIRECTIONALITY_NON_SPACING_MARK, -// DIRECTIONALITY_BOUNDARY_NEUTRAL, DIRECTIONALITY_UNDEFINED -// - -package jdk_internal.icu.lang; - -/** - * A container for the different 'enumerated types' used by UCharacter. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - -@Deprecated -class UCharacterEnums { - - /** This is just a namespace, it is not instantiatable. */ - private UCharacterEnums() { - }; - - /** - * 'Enum' for the CharacterCategory constants. These constants are compatible in - * name but not in value with those defined in - * java.lang.Character. - * - * @see UCharacterCategory - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static interface ECharacterCategory { - /** - * Unassigned character type - * - * @stable ICU 2.1 - */ - public static final int UNASSIGNED = 0; - - /** - * Character type Cn Not Assigned (no characters in [UnicodeData.txt] have this - * property) - * - * @stable ICU 2.6 - */ - public static final int GENERAL_OTHER_TYPES = 0; - - /** - * Character type Lu - * - * @stable ICU 2.1 - */ - public static final int UPPERCASE_LETTER = 1; - - /** - * Character type Ll - * - * @stable ICU 2.1 - */ - public static final int LOWERCASE_LETTER = 2; - - /** - * Character type Lt - * - * @stable ICU 2.1 - */ - - public static final int TITLECASE_LETTER = 3; - - /** - * Character type Lm - * - * @stable ICU 2.1 - */ - public static final int MODIFIER_LETTER = 4; - - /** - * Character type Lo - * - * @stable ICU 2.1 - */ - public static final int OTHER_LETTER = 5; - - /** - * Character type Mn - * - * @stable ICU 2.1 - */ - public static final int NON_SPACING_MARK = 6; - - /** - * Character type Me - * - * @stable ICU 2.1 - */ - public static final int ENCLOSING_MARK = 7; - - /** - * Character type Mc - * - * @stable ICU 2.1 - */ - public static final int COMBINING_SPACING_MARK = 8; - - /** - * Character type Nd - * - * @stable ICU 2.1 - */ - public static final int DECIMAL_DIGIT_NUMBER = 9; - - /** - * Character type Nl - * - * @stable ICU 2.1 - */ - public static final int LETTER_NUMBER = 10; - - /** - * Character type No - * - * @stable ICU 2.1 - */ - public static final int OTHER_NUMBER = 11; - - /** - * Character type Zs - * - * @stable ICU 2.1 - */ - public static final int SPACE_SEPARATOR = 12; - - /** - * Character type Zl - * - * @stable ICU 2.1 - */ - public static final int LINE_SEPARATOR = 13; - - /** - * Character type Zp - * - * @stable ICU 2.1 - */ - public static final int PARAGRAPH_SEPARATOR = 14; - - /** - * Character type Cc - * - * @stable ICU 2.1 - */ - public static final int CONTROL = 15; - - /** - * Character type Cf - * - * @stable ICU 2.1 - */ - public static final int FORMAT = 16; - - /** - * Character type Co - * - * @stable ICU 2.1 - */ - public static final int PRIVATE_USE = 17; - - /** - * Character type Cs - * - * @stable ICU 2.1 - */ - public static final int SURROGATE = 18; - - /** - * Character type Pd - * - * @stable ICU 2.1 - */ - public static final int DASH_PUNCTUATION = 19; - - /** - * Character type Ps - * - * @stable ICU 2.1 - */ - public static final int START_PUNCTUATION = 20; - - /** - * Character type Pe - * - * @stable ICU 2.1 - */ - public static final int END_PUNCTUATION = 21; - - /** - * Character type Pc - * - * @stable ICU 2.1 - */ - public static final int CONNECTOR_PUNCTUATION = 22; - - /** - * Character type Po - * - * @stable ICU 2.1 - */ - public static final int OTHER_PUNCTUATION = 23; - - /** - * Character type Sm - * - * @stable ICU 2.1 - */ - public static final int MATH_SYMBOL = 24; - - /** - * Character type Sc - * - * @stable ICU 2.1 - */ - public static final int CURRENCY_SYMBOL = 25; - - /** - * Character type Sk - * - * @stable ICU 2.1 - */ - public static final int MODIFIER_SYMBOL = 26; - - /** - * Character type So - * - * @stable ICU 2.1 - */ - public static final int OTHER_SYMBOL = 27; - - /** - * Character type Pi - * - * @see #INITIAL_QUOTE_PUNCTUATION - * @stable ICU 2.1 - */ - public static final int INITIAL_PUNCTUATION = 28; - - /** - * Character type Pi This name is compatible with java.lang.Character's name for - * this type. - * - * @see #INITIAL_PUNCTUATION - * @draft ICU 2.8 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final int INITIAL_QUOTE_PUNCTUATION = 28; - - /** - * Character type Pf - * - * @see #FINAL_QUOTE_PUNCTUATION - * @stable ICU 2.1 - */ - public static final int FINAL_PUNCTUATION = 29; - - /** - * Character type Pf This name is compatible with java.lang.Character's name for - * this type. - * - * @see #FINAL_PUNCTUATION - * @draft ICU 2.8 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final int FINAL_QUOTE_PUNCTUATION = 29; - - /** - * Character type count - * - * @stable ICU 2.1 - */ - public static final int CHAR_CATEGORY_COUNT = 30; - } - - /** - * 'Enum' for the CharacterDirection constants. There are two sets of names, - * those used in ICU, and those used in the JDK. The JDK constants are - * compatible in name but not in value with those defined in - * java.lang.Character. - * - * @see UCharacterDirection - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - - @Deprecated - public static interface ECharacterDirection { - /** - * Directional type L - * - * @stable ICU 2.1 - */ - public static final int LEFT_TO_RIGHT = 0; - - /** - * JDK-compatible synonum for LEFT_TO_RIGHT. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_LEFT_TO_RIGHT = (byte) LEFT_TO_RIGHT; - - /** - * Directional type R - * - * @stable ICU 2.1 - */ - public static final int RIGHT_TO_LEFT = 1; - - /** - * JDK-compatible synonum for RIGHT_TO_LEFT. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_RIGHT_TO_LEFT = (byte) RIGHT_TO_LEFT; - - /** - * Directional type EN - * - * @stable ICU 2.1 - */ - public static final int EUROPEAN_NUMBER = 2; - - /** - * JDK-compatible synonum for EUROPEAN_NUMBER. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_EUROPEAN_NUMBER = (byte) EUROPEAN_NUMBER; - - /** - * Directional type ES - * - * @stable ICU 2.1 - */ - public static final int EUROPEAN_NUMBER_SEPARATOR = 3; - - /** - * JDK-compatible synonum for EUROPEAN_NUMBER_SEPARATOR. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR = (byte) EUROPEAN_NUMBER_SEPARATOR; - - /** - * Directional type ET - * - * @stable ICU 2.1 - */ - public static final int EUROPEAN_NUMBER_TERMINATOR = 4; - - /** - * JDK-compatible synonum for EUROPEAN_NUMBER_TERMINATOR. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR = (byte) EUROPEAN_NUMBER_TERMINATOR; - - /** - * Directional type AN - * - * @stable ICU 2.1 - */ - public static final int ARABIC_NUMBER = 5; - - /** - * JDK-compatible synonum for ARABIC_NUMBER. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_ARABIC_NUMBER = (byte) ARABIC_NUMBER; - - /** - * Directional type CS - * - * @stable ICU 2.1 - */ - public static final int COMMON_NUMBER_SEPARATOR = 6; - - /** - * JDK-compatible synonum for COMMON_NUMBER_SEPARATOR. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_COMMON_NUMBER_SEPARATOR = (byte) COMMON_NUMBER_SEPARATOR; - - /** - * Directional type B - * - * @stable ICU 2.1 - */ - public static final int BLOCK_SEPARATOR = 7; - - /** - * JDK-compatible synonum for BLOCK_SEPARATOR. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_PARAGRAPH_SEPARATOR = (byte) BLOCK_SEPARATOR; - - /** - * Directional type S - * - * @stable ICU 2.1 - */ - public static final int SEGMENT_SEPARATOR = 8; - - /** - * JDK-compatible synonum for SEGMENT_SEPARATOR. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_SEGMENT_SEPARATOR = (byte) SEGMENT_SEPARATOR; - - /** - * Directional type WS - * - * @stable ICU 2.1 - */ - public static final int WHITE_SPACE_NEUTRAL = 9; - - /** - * JDK-compatible synonum for WHITE_SPACE_NEUTRAL. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_WHITESPACE = (byte) WHITE_SPACE_NEUTRAL; - - /** - * Directional type ON - * - * @stable ICU 2.1 - */ - public static final int OTHER_NEUTRAL = 10; - - /** - * JDK-compatible synonum for OTHER_NEUTRAL. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_OTHER_NEUTRALS = (byte) OTHER_NEUTRAL; - - /** - * Directional type LRE - * - * @stable ICU 2.1 - */ - public static final int LEFT_TO_RIGHT_EMBEDDING = 11; - - /** - * JDK-compatible synonum for LEFT_TO_RIGHT_EMBEDDING. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING = (byte) LEFT_TO_RIGHT_EMBEDDING; - - /** - * Directional type LRO - * - * @stable ICU 2.1 - */ - public static final int LEFT_TO_RIGHT_OVERRIDE = 12; - - /** - * JDK-compatible synonum for LEFT_TO_RIGHT_OVERRIDE. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE = (byte) LEFT_TO_RIGHT_OVERRIDE; - - /** - * Directional type AL - * - * @stable ICU 2.1 - */ - public static final int RIGHT_TO_LEFT_ARABIC = 13; - - /** - * JDK-compatible synonum for RIGHT_TO_LEFT_ARABIC. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC = (byte) RIGHT_TO_LEFT_ARABIC; - - /** - * Directional type RLE - * - * @stable ICU 2.1 - */ - public static final int RIGHT_TO_LEFT_EMBEDDING = 14; - - /** - * JDK-compatible synonum for RIGHT_TO_LEFT_EMBEDDING. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING = (byte) RIGHT_TO_LEFT_EMBEDDING; - - /** - * Directional type RLO - * - * @stable ICU 2.1 - */ - public static final int RIGHT_TO_LEFT_OVERRIDE = 15; - - /** - * JDK-compatible synonum for RIGHT_TO_LEFT_OVERRIDE. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE = (byte) RIGHT_TO_LEFT_OVERRIDE; - - /** - * Directional type PDF - * - * @stable ICU 2.1 - */ - public static final int POP_DIRECTIONAL_FORMAT = 16; - - /** - * JDK-compatible synonum for POP_DIRECTIONAL_FORMAT. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_POP_DIRECTIONAL_FORMAT = (byte) POP_DIRECTIONAL_FORMAT; - - /** - * Directional type NSM - * - * @stable ICU 2.1 - */ - public static final int DIR_NON_SPACING_MARK = 17; - - /** - * JDK-compatible synonum for DIR_NON_SPACING_MARK. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_NON_SPACING_MARK = (byte) DIR_NON_SPACING_MARK; - - /** - * Directional type BN - * - * @stable ICU 2.1 - */ - public static final int BOUNDARY_NEUTRAL = 18; - - /** - * JDK-compatible synonum for BOUNDARY_NEUTRAL. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_BOUNDARY_NEUTRAL = (byte) BOUNDARY_NEUTRAL; - - /** - * Number of directional types - * - * @stable ICU 2.1 - */ - public static final int CHAR_DIRECTION_COUNT = 19; - - /** - * Undefined bidirectional character type. Undefined char values - * have undefined directionality in the Unicode specification. - * - * @draft ICU 3.0 - * @deprecated This is a draft API and might change in a future release of ICU. - */ - @Deprecated - public static final byte DIRECTIONALITY_UNDEFINED = -1; - } -} +/* + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* +/** + ******************************************************************************* + * Copyright (C) 2004, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + */ +// CHANGELOG +// 2005-05-19 Edward Wang +// - copy this file from icu4jsrc_3_2/src/com/ibm/icu/lang/UCharacterEnums.java +// - move from package com.ibm.icu.lang to package sun.net.idn +// +// 2011-09-06 Kurchi Subhra Hazra +// - Added @Deprecated tag to the following: +// - class UCharacterEnums +// - interfaces ECharacterCategory, ECharacterDirection +// - fields INITIAL_QUOTE_PUNCTUATION, FINAL_QUOTE_PUNCTUATION, +// DIRECTIONALITY_LEFT_TO_RIGHT, DIRECTIONALITY_RIGHT_TO_LEFT, +// DIRECTIONALITY_EUROPEAN_NUMBER, DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR +// DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR, DIRECTIONALITY_ARABIC_NUMBER, +// DIRECTIONALITY_COMMON_NUMBER_SEPARATOR, DIRECTIONALITY_PARAGRAPH_SEPARATOR, +// DIRECTIONALITY_SEGMENT_SEPARATOR, DIRECTIONALITY_WHITESPACE, +// DIRECTIONALITY_OTHER_NEUTRALS, DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING, +// DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE, DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC, +// DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING, DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE, +// DIRECTIONALITY_POP_DIRECTIONAL_FORMAT, DIRECTIONALITY_NON_SPACING_MARK, +// DIRECTIONALITY_BOUNDARY_NEUTRAL, DIRECTIONALITY_UNDEFINED +// + +package jdk_internal.icu.lang; + +/** + * A container for the different 'enumerated types' used by UCharacter. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + +@Deprecated +class UCharacterEnums { + + /** This is just a namespace, it is not instantiatable. */ + private UCharacterEnums() { + }; + + /** + * 'Enum' for the CharacterCategory constants. These constants are compatible in + * name but not in value with those defined in + * java.lang.Character. + * + * @see UCharacterCategory + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static interface ECharacterCategory { + /** + * Unassigned character type + * + * @stable ICU 2.1 + */ + public static final int UNASSIGNED = 0; + + /** + * Character type Cn Not Assigned (no characters in [UnicodeData.txt] have this + * property) + * + * @stable ICU 2.6 + */ + public static final int GENERAL_OTHER_TYPES = 0; + + /** + * Character type Lu + * + * @stable ICU 2.1 + */ + public static final int UPPERCASE_LETTER = 1; + + /** + * Character type Ll + * + * @stable ICU 2.1 + */ + public static final int LOWERCASE_LETTER = 2; + + /** + * Character type Lt + * + * @stable ICU 2.1 + */ + + public static final int TITLECASE_LETTER = 3; + + /** + * Character type Lm + * + * @stable ICU 2.1 + */ + public static final int MODIFIER_LETTER = 4; + + /** + * Character type Lo + * + * @stable ICU 2.1 + */ + public static final int OTHER_LETTER = 5; + + /** + * Character type Mn + * + * @stable ICU 2.1 + */ + public static final int NON_SPACING_MARK = 6; + + /** + * Character type Me + * + * @stable ICU 2.1 + */ + public static final int ENCLOSING_MARK = 7; + + /** + * Character type Mc + * + * @stable ICU 2.1 + */ + public static final int COMBINING_SPACING_MARK = 8; + + /** + * Character type Nd + * + * @stable ICU 2.1 + */ + public static final int DECIMAL_DIGIT_NUMBER = 9; + + /** + * Character type Nl + * + * @stable ICU 2.1 + */ + public static final int LETTER_NUMBER = 10; + + /** + * Character type No + * + * @stable ICU 2.1 + */ + public static final int OTHER_NUMBER = 11; + + /** + * Character type Zs + * + * @stable ICU 2.1 + */ + public static final int SPACE_SEPARATOR = 12; + + /** + * Character type Zl + * + * @stable ICU 2.1 + */ + public static final int LINE_SEPARATOR = 13; + + /** + * Character type Zp + * + * @stable ICU 2.1 + */ + public static final int PARAGRAPH_SEPARATOR = 14; + + /** + * Character type Cc + * + * @stable ICU 2.1 + */ + public static final int CONTROL = 15; + + /** + * Character type Cf + * + * @stable ICU 2.1 + */ + public static final int FORMAT = 16; + + /** + * Character type Co + * + * @stable ICU 2.1 + */ + public static final int PRIVATE_USE = 17; + + /** + * Character type Cs + * + * @stable ICU 2.1 + */ + public static final int SURROGATE = 18; + + /** + * Character type Pd + * + * @stable ICU 2.1 + */ + public static final int DASH_PUNCTUATION = 19; + + /** + * Character type Ps + * + * @stable ICU 2.1 + */ + public static final int START_PUNCTUATION = 20; + + /** + * Character type Pe + * + * @stable ICU 2.1 + */ + public static final int END_PUNCTUATION = 21; + + /** + * Character type Pc + * + * @stable ICU 2.1 + */ + public static final int CONNECTOR_PUNCTUATION = 22; + + /** + * Character type Po + * + * @stable ICU 2.1 + */ + public static final int OTHER_PUNCTUATION = 23; + + /** + * Character type Sm + * + * @stable ICU 2.1 + */ + public static final int MATH_SYMBOL = 24; + + /** + * Character type Sc + * + * @stable ICU 2.1 + */ + public static final int CURRENCY_SYMBOL = 25; + + /** + * Character type Sk + * + * @stable ICU 2.1 + */ + public static final int MODIFIER_SYMBOL = 26; + + /** + * Character type So + * + * @stable ICU 2.1 + */ + public static final int OTHER_SYMBOL = 27; + + /** + * Character type Pi + * + * @see #INITIAL_QUOTE_PUNCTUATION + * @stable ICU 2.1 + */ + public static final int INITIAL_PUNCTUATION = 28; + + /** + * Character type Pi This name is compatible with java.lang.Character's name for + * this type. + * + * @see #INITIAL_PUNCTUATION + * @draft ICU 2.8 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final int INITIAL_QUOTE_PUNCTUATION = 28; + + /** + * Character type Pf + * + * @see #FINAL_QUOTE_PUNCTUATION + * @stable ICU 2.1 + */ + public static final int FINAL_PUNCTUATION = 29; + + /** + * Character type Pf This name is compatible with java.lang.Character's name for + * this type. + * + * @see #FINAL_PUNCTUATION + * @draft ICU 2.8 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final int FINAL_QUOTE_PUNCTUATION = 29; + + /** + * Character type count + * + * @stable ICU 2.1 + */ + public static final int CHAR_CATEGORY_COUNT = 30; + } + + /** + * 'Enum' for the CharacterDirection constants. There are two sets of names, + * those used in ICU, and those used in the JDK. The JDK constants are + * compatible in name but not in value with those defined in + * java.lang.Character. + * + * @see UCharacterDirection + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + + @Deprecated + public static interface ECharacterDirection { + /** + * Directional type L + * + * @stable ICU 2.1 + */ + public static final int LEFT_TO_RIGHT = 0; + + /** + * JDK-compatible synonum for LEFT_TO_RIGHT. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_LEFT_TO_RIGHT = (byte) LEFT_TO_RIGHT; + + /** + * Directional type R + * + * @stable ICU 2.1 + */ + public static final int RIGHT_TO_LEFT = 1; + + /** + * JDK-compatible synonum for RIGHT_TO_LEFT. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_RIGHT_TO_LEFT = (byte) RIGHT_TO_LEFT; + + /** + * Directional type EN + * + * @stable ICU 2.1 + */ + public static final int EUROPEAN_NUMBER = 2; + + /** + * JDK-compatible synonum for EUROPEAN_NUMBER. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_EUROPEAN_NUMBER = (byte) EUROPEAN_NUMBER; + + /** + * Directional type ES + * + * @stable ICU 2.1 + */ + public static final int EUROPEAN_NUMBER_SEPARATOR = 3; + + /** + * JDK-compatible synonum for EUROPEAN_NUMBER_SEPARATOR. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR = (byte) EUROPEAN_NUMBER_SEPARATOR; + + /** + * Directional type ET + * + * @stable ICU 2.1 + */ + public static final int EUROPEAN_NUMBER_TERMINATOR = 4; + + /** + * JDK-compatible synonum for EUROPEAN_NUMBER_TERMINATOR. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR = (byte) EUROPEAN_NUMBER_TERMINATOR; + + /** + * Directional type AN + * + * @stable ICU 2.1 + */ + public static final int ARABIC_NUMBER = 5; + + /** + * JDK-compatible synonum for ARABIC_NUMBER. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_ARABIC_NUMBER = (byte) ARABIC_NUMBER; + + /** + * Directional type CS + * + * @stable ICU 2.1 + */ + public static final int COMMON_NUMBER_SEPARATOR = 6; + + /** + * JDK-compatible synonum for COMMON_NUMBER_SEPARATOR. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_COMMON_NUMBER_SEPARATOR = (byte) COMMON_NUMBER_SEPARATOR; + + /** + * Directional type B + * + * @stable ICU 2.1 + */ + public static final int BLOCK_SEPARATOR = 7; + + /** + * JDK-compatible synonum for BLOCK_SEPARATOR. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_PARAGRAPH_SEPARATOR = (byte) BLOCK_SEPARATOR; + + /** + * Directional type S + * + * @stable ICU 2.1 + */ + public static final int SEGMENT_SEPARATOR = 8; + + /** + * JDK-compatible synonum for SEGMENT_SEPARATOR. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_SEGMENT_SEPARATOR = (byte) SEGMENT_SEPARATOR; + + /** + * Directional type WS + * + * @stable ICU 2.1 + */ + public static final int WHITE_SPACE_NEUTRAL = 9; + + /** + * JDK-compatible synonum for WHITE_SPACE_NEUTRAL. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_WHITESPACE = (byte) WHITE_SPACE_NEUTRAL; + + /** + * Directional type ON + * + * @stable ICU 2.1 + */ + public static final int OTHER_NEUTRAL = 10; + + /** + * JDK-compatible synonum for OTHER_NEUTRAL. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_OTHER_NEUTRALS = (byte) OTHER_NEUTRAL; + + /** + * Directional type LRE + * + * @stable ICU 2.1 + */ + public static final int LEFT_TO_RIGHT_EMBEDDING = 11; + + /** + * JDK-compatible synonum for LEFT_TO_RIGHT_EMBEDDING. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING = (byte) LEFT_TO_RIGHT_EMBEDDING; + + /** + * Directional type LRO + * + * @stable ICU 2.1 + */ + public static final int LEFT_TO_RIGHT_OVERRIDE = 12; + + /** + * JDK-compatible synonum for LEFT_TO_RIGHT_OVERRIDE. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE = (byte) LEFT_TO_RIGHT_OVERRIDE; + + /** + * Directional type AL + * + * @stable ICU 2.1 + */ + public static final int RIGHT_TO_LEFT_ARABIC = 13; + + /** + * JDK-compatible synonum for RIGHT_TO_LEFT_ARABIC. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC = (byte) RIGHT_TO_LEFT_ARABIC; + + /** + * Directional type RLE + * + * @stable ICU 2.1 + */ + public static final int RIGHT_TO_LEFT_EMBEDDING = 14; + + /** + * JDK-compatible synonum for RIGHT_TO_LEFT_EMBEDDING. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING = (byte) RIGHT_TO_LEFT_EMBEDDING; + + /** + * Directional type RLO + * + * @stable ICU 2.1 + */ + public static final int RIGHT_TO_LEFT_OVERRIDE = 15; + + /** + * JDK-compatible synonum for RIGHT_TO_LEFT_OVERRIDE. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE = (byte) RIGHT_TO_LEFT_OVERRIDE; + + /** + * Directional type PDF + * + * @stable ICU 2.1 + */ + public static final int POP_DIRECTIONAL_FORMAT = 16; + + /** + * JDK-compatible synonum for POP_DIRECTIONAL_FORMAT. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_POP_DIRECTIONAL_FORMAT = (byte) POP_DIRECTIONAL_FORMAT; + + /** + * Directional type NSM + * + * @stable ICU 2.1 + */ + public static final int DIR_NON_SPACING_MARK = 17; + + /** + * JDK-compatible synonum for DIR_NON_SPACING_MARK. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_NON_SPACING_MARK = (byte) DIR_NON_SPACING_MARK; + + /** + * Directional type BN + * + * @stable ICU 2.1 + */ + public static final int BOUNDARY_NEUTRAL = 18; + + /** + * JDK-compatible synonum for BOUNDARY_NEUTRAL. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_BOUNDARY_NEUTRAL = (byte) BOUNDARY_NEUTRAL; + + /** + * Number of directional types + * + * @stable ICU 2.1 + */ + public static final int CHAR_DIRECTION_COUNT = 19; + + /** + * Undefined bidirectional character type. Undefined char values + * have undefined directionality in the Unicode specification. + * + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_UNDEFINED = -1; + } +} diff --git a/src/main/java/jdk_internal/icu/text/BidiBase.java b/src/main/java/jdk_internal/icu/text/BidiBase.java index 9f4f24dc..07367296 100755 --- a/src/main/java/jdk_internal/icu/text/BidiBase.java +++ b/src/main/java/jdk_internal/icu/text/BidiBase.java @@ -1,4729 +1,4729 @@ -/* - * Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* -******************************************************************************* -* Copyright (C) 2001-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -*/ - -/* 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_L - * 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. - */ - -package jdk_internal.icu.text; - -import java.lang.reflect.Array; -import java.util.Arrays; - -import jdk_internal.bidi.AttributedCharacterIterator; -import jdk_internal.bidi.Bidi; -import jdk_internal.bidi.NumericShaper; -import jdk_internal.bidi.TextAttribute; -import jdk_internal.icu.impl.UBiDiProps; -import jdk_internal.icu.lang.UCharacter; - -/** - * - *

Bidi algorithm for ICU

- * - * This is an implementation of the Unicode Bidirectional Algorithm. The - * algorithm is defined in the - * Unicode Standard Annex #9: - * Unicode Bidirectional Algorithm. - *

- * - * Note: Libraries that perform a bidirectional algorithm and reorder strings - * accordingly are sometimes called "Storage Layout Engines". ICU's Bidi and - * shaping (ArabicShaping) classes can be used at the core of such "Storage - * Layout Engines". - * - *

General remarks about the API:

- * - * 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 methods 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. - * - *

Basic concept: paragraph

A piece of text can be divided into several - * paragraphs by characters with the Bidi class Block Separator. - * For handling of paragraphs, see: - *
    - *
  • {@link #countParagraphs} - *
  • {@link #getParaLevel} - *
  • {@link #getParagraph} - *
  • {@link #getParagraphByIndex} - *
- * - *

Basic concept: text direction

The direction of a piece of text may - * be: - *
    - *
  • {@link #LTR} - *
  • {@link #RTL} - *
  • {@link #MIXED} - *
  • {@link #NEUTRAL} - *
- * - *

Basic concept: levels

- * - * Levels in this API represent embedding levels according to the Unicode - * Bidirectional Algorithm. Their low-order bit (even/odd value) indicates the - * visual direction. - *

- * - * Levels can be abstract values when used for the paraLevel and - * embeddingLevels arguments of setPara(); there: - *

    - *
  • the high-order bit 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 - * LEVEL_DEFAULT_LTR and LEVEL_DEFAULT_RTL.
  • - *
- * - *

- * The related constants are not real, valid level values. - * DEFAULT_XXX can be used to specify a default for the paragraph - * level for when the setPara() method shall determine it but there - * is no strongly typed character in the input. - *

- * - * Note that the value for LEVEL_DEFAULT_LTR is even and the one - * for LEVEL_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 MAX_EXPLICIT_LEVEL is odd. - * - *

- * See Also: - *

    - *
  • {@link #LEVEL_DEFAULT_LTR} - *
  • {@link #LEVEL_DEFAULT_RTL} - *
  • {@link #LEVEL_OVERRIDE} - *
  • {@link #MAX_EXPLICIT_LEVEL} - *
  • {@link #setPara} - *
- * - *

Basic concept: Reordering Mode

Reordering mode values indicate which - * variant of the Bidi algorithm to use. - * - *

- * See Also: - *

    - *
  • {@link #setReorderingMode} - *
  • {@link #REORDER_DEFAULT} - *
  • {@link #REORDER_NUMBERS_SPECIAL} - *
  • {@link #REORDER_GROUP_NUMBERS_WITH_R} - *
  • {@link #REORDER_RUNS_ONLY} - *
  • {@link #REORDER_INVERSE_NUMBERS_AS_L} - *
  • {@link #REORDER_INVERSE_LIKE_DIRECT} - *
  • {@link #REORDER_INVERSE_FOR_NUMBERS_SPECIAL} - *
- * - *

Basic concept: Reordering Options

Reordering options can be applied - * during Bidi text transformations. - * - *

- * See Also: - *

    - *
  • {@link #setReorderingOptions} - *
  • {@link #OPTION_DEFAULT} - *
  • {@link #OPTION_INSERT_MARKS} - *
  • {@link #OPTION_REMOVE_CONTROLS} - *
  • {@link #OPTION_STREAMING} - *
- * - * - * @author Simon Montagu, Matitiahu Allouche (ported from C code written by - * Markus W. Scherer) - * @stable ICU 3.8 - * - * - *

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
- *
- *  package com.ibm.icu.dev.test.bidi;
- *
- *  import com.ibm.icu.text.Bidi;
- *  import com.ibm.icu.text.BidiRun;
- *
- *  public class Sample {
- *
- *      static final int styleNormal = 0;
- *      static final int styleSelected = 1;
- *      static final int styleBold = 2;
- *      static final int styleItalics = 4;
- *      static final int styleSuper=8;
- *      static final int styleSub = 16;
- *
- *      static class StyleRun {
- *          int limit;
- *          int style;
- *
- *          public StyleRun(int limit, int style) {
- *              this.limit = limit;
- *              this.style = style;
- *         }
- *         }
- *
- *         static class Bounds {
- *         int start;
- *         int limit;
- *
- *         public Bounds(int start, int limit) {
- *         this.start = start;
- *         this.limit = limit;
- *         }
- *         }
- *
- *         static int getTextWidth(String text, int start, int limit, StyleRun[] styleRuns, int styleRunCount) {
- *         // simplistic way to compute the width
- *         return limit - start;
- *         }
- *
- *         // set limit and StyleRun limit for a line
- *         	// from text[start] and from styleRuns[styleRunStart]
- *         	// using Bidi.getLogicalRun(...)
- *         	// returns line width
- *         static int getLineBreak(String text, Bounds line, Bidi para, StyleRun styleRuns[], Bounds styleRun) {
- *         // dummy return
- *         return 0;
- *         }
- *
- *         // render runs on a line sequentially, always from left to right
- *
- *         // prepare rendering a new line
- *         static void startLine(byte textDirection, int lineWidth) {
- *         System.out.println();
- *         }
- *
- *         // render a run of text and advance to the right by the run width
- *         	// the text[start..limit-1] is always in logical order
- *         static void renderRun(String text, int start, int limit, byte textDirection, int 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
- *         static void renderDirectionalRun(String text, int start, int limit, byte direction, StyleRun styleRuns[],
- *         int styleRunCount) {
- *         int i;
- *
- *         // iterate over style runs
- *         if (direction == Bidi.LTR) {
- *         int styleLimit;
- *         for (i = 0; i < styleRunCount; ++i) {
- *         styleLimit = styleRuns[i].limit;
- *         if (start < styleLimit) {
- *         if (styleLimit > limit) {
- *         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 < start) {
- *         styleStart = start;
- *         }
- *         renderRun(text, styleStart, limit, direction, styleRuns[i].style);
- *         if (styleStart == start) {
- *         break;
- *         }
- *         limit = styleStart;
- *         }
- *         }
- *         }
- *         }
- *
- *         // the line object represents text[start..limit-1]
- *         static void renderLine(Bidi line, String text, int start, int limit, StyleRun styleRuns[], int styleRunCount) {
- *         byte direction = line.getDirection();
- *         if (direction != Bidi.MIXED) {
- *         // unidirectional
- *         if (styleRunCount <= 1) {
- *         renderRun(text, start, limit, direction, styleRuns[0].style);
- *         } else {
- *         renderDirectionalRun(text, start, limit, direction, styleRuns, styleRunCount);
- *         }
- *         } else {
- *         // mixed-directional
- *         int count, i;
- *         BidiRun run;
- *
- *         try {
- *         count = line.countRuns();
- *         } catch (IllegalStateException e) {
- *         e.printStackTrace();
- *         return;
- *         }
- *         if (styleRunCount <= 1) {
- *         int style = styleRuns[0].style;
- *
- *         // iterate over directional runs
- *         for (i = 0; i < count; ++i) {
- *         run = line.getVisualRun(i);
- *         renderRun(text, run.getStart(), run.getLimit(), run.getDirection(), style);
- *         }
- *         } else {
- *         // iterate over both directional and style runs
- *         for (i = 0; i < count; ++i) {
- *         run = line.getVisualRun(i);
- *         renderDirectionalRun(text, run.getStart(), run.getLimit(), run.getDirection(), styleRuns,
- *         styleRunCount);
- *         }
- *         }
- *         }
- *         }
- *
- *         static void renderParagraph(String text, byte textDirection, StyleRun styleRuns[], int styleRunCount,
- *         int lineWidth) {
- *         int length = text.length();
- *         Bidi para = new Bidi();
- *         try {
- *         para.setPara(text, textDirection != 0 ? Bidi.LEVEL_DEFAULT_RTL : Bidi.LEVEL_DEFAULT_LTR, null);
- *         } catch (Exception e) {
- *         e.printStackTrace();
- *         return;
- *         }
- *         byte paraLevel = (byte) (1 & para.getParaLevel());
- *         StyleRun styleRun = new StyleRun(length, styleNormal);
- *
- *         if (styleRuns == null || styleRunCount <= 0) {
- *         styleRuns = new StyleRun[1];
- *         styleRunCount = 1;
- *         styleRuns[0] = styleRun;
- *         }
- *         // assume styleRuns[styleRunCount-1].limit>=length
- *
- *         int 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);
- *         } else {
- *         // we need to render several lines
- *         Bidi line = new Bidi(length, 0);
- *         int start = 0, limit;
- *         int styleRunStart = 0, styleRunLimit;
- *
- *         for (;;) {
- *         limit = length;
- *         styleRunLimit = styleRunCount;
- *         width = getLineBreak(text, new Bounds(start, limit), para, styleRuns,
- *         new Bounds(styleRunStart, styleRunLimit));
- *         try {
- *         line = para.setLine(start, limit);
- *         } catch (Exception e) {
- *         e.printStackTrace();
- *         return;
- *         }
- *         // prepare rendering a new line
- *         				// from either left or right
- *         startLine(paraLevel, width);
- *
- *         if (styleRunStart > 0) {
- *         int newRunCount = styleRuns.length - styleRunStart;
- *         StyleRun[] newRuns = new StyleRun[newRunCount];
- *         System.arraycopy(styleRuns, styleRunStart, newRuns, 0, newRunCount);
- *         renderLine(line, text, start, limit, newRuns, styleRunLimit - styleRunStart);
- *         } else {
- *         renderLine(line, text, start, limit, styleRuns, styleRunLimit - styleRunStart);
- *         }
- *         if (limit == length) {
- *         break;
- *         }
- *         start = limit;
- *         styleRunStart = styleRunLimit - 1;
- *         if (start >= styleRuns[styleRunStart].limit) {
- *         ++styleRunStart;
- *         }
- *         }
- *         }
- *         }
- *
- *         public static void main(String[] args) {
- *         renderParagraph("Some Latin text...", Bidi.LTR, null, 0, 80);
- *         renderParagraph("Some Hebrew text...", Bidi.RTL, null, 0, 60);
- *         }
- *         }
- *
- * }
- */ - -/* - * 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(). - */ - -// Original filename in ICU4J: Bidi.java -public class BidiBase { - - static class Point { - int pos; /* position in text */ - int flag; /* flag for LRM/RLM, before/after */ - } - - static class InsertPoints { - int size; - int confirmed; - Point[] points = new Point[0]; - } - - static class Opening { - int position; /* position of opening bracket */ - int match; /* matching char or -position of closing bracket */ - int contextPos; /* position of last strong char found before opening */ - short flags; /* bits for L or R/AL found within the pair */ - byte contextDir; /* L or R according to last strong char before opening */ - } - - static class IsoRun { - int contextPos; /* position of char determining context */ - short start; /* index of first opening entry for this run */ - short limit; /* index after last opening entry for this run */ - byte level; /* level of this run */ - byte lastStrong; /* bidi class of last strong char found in this run */ - byte lastBase; /* bidi class of last base char found in this run */ - byte contextDir; /* L or R to use as context for following openings */ - } - - static class BracketData { - Opening[] openings = new Opening[SIMPLE_PARAS_COUNT]; - int 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 = new IsoRun[MAX_EXPLICIT_LEVEL + 2]; - boolean isNumbersSpecial; /* reordering mode for NUMBERS_SPECIAL */ - } - - static class Isolate { - int startON; - int start1; - short stateImp; - short state; - } - - /** - * 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 - * REORDER_INVERSE_LIKE_DIRECT or - * 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 rightmost or - * leftmost strong character of the source text is RTL or Arabic Letter, the - * direction will be LTR otherwise. - *

- * - * If reordering option 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 #REORDER_INVERSE_LIKE_DIRECT - * @see #REORDER_INVERSE_FOR_NUMBERS_SPECIAL - * @stable ICU 3.8 - */ - public static final byte LEVEL_DEFAULT_LTR = (byte) 0x7e; - - /** - * 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 - * REORDER_INVERSE_LIKE_DIRECT or - * 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 rightmost 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 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 #REORDER_INVERSE_LIKE_DIRECT - * @see #REORDER_INVERSE_FOR_NUMBERS_SPECIAL - * @stable ICU 3.8 - */ - public static final byte LEVEL_DEFAULT_RTL = (byte) 0x7f; - - /** - * Maximum explicit embedding level. (The maximum resolved level can be up to - * MAX_EXPLICIT_LEVEL+1). - * - * @stable ICU 3.8 - */ - public static final byte MAX_EXPLICIT_LEVEL = 125; - - /** - * Bit flag for level input. Overrides directional properties. - * - * @stable ICU 3.8 - */ - public static final byte LEVEL_OVERRIDE = (byte) 0x80; - - /** - * Special value which can be returned by the mapping methods 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 - * 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 - * OPTION_INSERT_MARKS. - * - * @see #getVisualIndex - * @see #getVisualMap - * @see #getLogicalIndex - * @see #getLogicalMap - * @see #OPTION_INSERT_MARKS - * @see #OPTION_REMOVE_CONTROLS - * @stable ICU 3.8 - */ - public static final int MAP_NOWHERE = -1; - - /** - * Left-to-right text. - *

    - *
  • As return value for 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 getBaseDirection(), it means that the - * first strong character of the source string has a left-to-right direction. - *
- * - * @stable ICU 3.8 - */ - public static final byte LTR = 0; - - /** - * Right-to-left text. - *
    - *
  • As return value for 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 getBaseDirection(), it means that the - * first strong character of the source string has a right-to-left direction. - *
- * - * @stable ICU 3.8 - */ - public static final byte RTL = 1; - - /** - * Mixed-directional text. - *

- * As return value for getDirection(), it means that the source - * string contains both left-to-right and right-to-left characters. - * - * @stable ICU 3.8 - */ - public static final byte MIXED = 2; - - /** - * option bit for writeReordered(): keep combining characters after their base - * characters in RTL runs - * - * @see #writeReordered - * @stable ICU 3.8 - */ - public static final short KEEP_BASE_COMBINING = 1; - - /** - * option bit for writeReordered(): replace characters with the "mirrored" - * property in RTL runs by their mirror-image mappings - * - * @see #writeReordered - * @stable ICU 3.8 - */ - public static final short DO_MIRRORING = 2; - - /** - * option bit for 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 #setInverse - * @see #writeReordered - * @stable ICU 3.8 - */ - public static final short INSERT_LRM_FOR_NUMERIC = 4; - - /** - * option bit for writeReordered(): remove Bidi control characters (this does - * not affect INSERT_LRM_FOR_NUMERIC) - * - *

- * This option does not imply corresponding adjustment of the index mappings. - *

- * - * @see #writeReordered - * @see #INSERT_LRM_FOR_NUMERIC - * @stable ICU 3.8 - */ - public static final short REMOVE_BIDI_CONTROLS = 8; - - /** - * option bit for writeReordered(): write the output in reverse order - * - *

- * This has the same effect as calling writeReordered() first - * without this option, and then calling 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 #writeReordered - * @stable ICU 3.8 - */ - public static final short OUTPUT_REVERSE = 16; - - /** - * Reordering mode: Regular Logical to Visual Bidi algorithm according to - * Unicode. - * - * @see #setReorderingMode - * @stable ICU 3.8 - */ - private static final short REORDER_DEFAULT = 0; - - /** - * Reordering mode: Logical to Visual algorithm which handles numbers in a way - * which mimicks the behavior of Windows XP. - * - * @see #setReorderingMode - * @stable ICU 3.8 - */ - private static final short REORDER_NUMBERS_SPECIAL = 1; - - /** - * Reordering mode: Logical to Visual algorithm grouping numbers with adjacent R - * characters (reversible algorithm). - * - * @see #setReorderingMode - * @stable ICU 3.8 - */ - private static final short REORDER_GROUP_NUMBERS_WITH_R = 2; - - /** - * Reordering mode: 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 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. - * - * @see #OPTION_INSERT_MARKS - * @see #setReorderingMode - * @stable ICU 3.8 - */ - static final short REORDER_RUNS_ONLY = 3; - - /** - * Reordering mode: Visual to Logical algorithm which handles numbers like L - * (same algorithm as selected by setInverse(true). - * - * @see #setInverse - * @see #setReorderingMode - * @stable ICU 3.8 - */ - static final short REORDER_INVERSE_NUMBERS_AS_L = 4; - - /** - * Reordering mode: Visual to Logical algorithm equivalent to the regular - * Logical to Visual algorithm. - * - * @see #setReorderingMode - * @stable ICU 3.8 - */ - static final short REORDER_INVERSE_LIKE_DIRECT = 5; - - /** - * Reordering mode: Inverse Bidi (Visual to Logical) algorithm for the - * REORDER_NUMBERS_SPECIAL Bidi algorithm. - * - * @see #setReorderingMode - * @stable ICU 3.8 - */ - static final short REORDER_INVERSE_FOR_NUMBERS_SPECIAL = 6; - - /* - * Reordering mode values must be ordered so that all the regular logical to - * visual modes come first, and all inverse Bidi modes come last. - */ - private static final short REORDER_LAST_LOGICAL_TO_VISUAL = REORDER_NUMBERS_SPECIAL; - - /** - * Option bit for 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 setPara. - *

- * - *

- * This option is significant only with reordering modes which generate a result - * with Logical order, specifically. - *

- *
    - *
  • REORDER_RUNS_ONLY
  • - *
  • REORDER_INVERSE_NUMBERS_AS_L
  • - *
  • REORDER_INVERSE_LIKE_DIRECT
  • - *
  • REORDER_INVERSE_FOR_NUMBERS_SPECIAL
  • - *
- * - *

- * If this option is set in conjunction with reordering mode - * REORDER_INVERSE_NUMBERS_AS_L or with calling - * setInverse(true), it implies option - * INSERT_LRM_FOR_NUMERIC in calls to method - * 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 OPTION_REMOVE_CONTROLS set before - * calling setPara() or option REMOVE_BIDI_CONTROLS in - * writeReordered), the result will be identical to the source text - * in the first transformation. - * - *

- * This option will be ignored if specified together with option - * OPTION_REMOVE_CONTROLS. It inhibits option - * REMOVE_BIDI_CONTROLS in calls to method - * writeReordered() and it implies option - * INSERT_LRM_FOR_NUMERIC in calls to method - * writeReordered() if the reordering mode is - * REORDER_INVERSE_NUMBERS_AS_L. - *

- * - * @see #setReorderingMode - * @see #setReorderingOptions - * @see #INSERT_LRM_FOR_NUMERIC - * @see #REMOVE_BIDI_CONTROLS - * @see #OPTION_REMOVE_CONTROLS - * @see #REORDER_RUNS_ONLY - * @see #REORDER_INVERSE_NUMBERS_AS_L - * @see #REORDER_INVERSE_LIKE_DIRECT - * @see #REORDER_INVERSE_FOR_NUMBERS_SPECIAL - * @stable ICU 3.8 - */ - static final int OPTION_INSERT_MARKS = 1; - - /** - * Option bit for setReorderingOptions: remove Bidi control - * characters - * - *

- * This option must be set or reset before calling setPara. - *

- * - *

- * This option nullifies option OPTION_INSERT_MARKS. It inhibits - * option INSERT_LRM_FOR_NUMERIC in calls to method - * writeReordered() and it implies option - * REMOVE_BIDI_CONTROLS in calls to that method. - *

- * - * @see #setReorderingMode - * @see #setReorderingOptions - * @see #OPTION_INSERT_MARKS - * @see #INSERT_LRM_FOR_NUMERIC - * @see #REMOVE_BIDI_CONTROLS - * @stable ICU 3.8 - */ - static final int OPTION_REMOVE_CONTROLS = 2; - - /** - * Option bit for setReorderingOptions: process the output as part - * of a stream to be continued - * - *

- * This option must be set or reset before calling 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, setPara() may process less than the - * full source text in order to truncate the text at a meaningful boundary. The - * caller should call getProcessedLength() immediately after - * calling 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 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 - * OPTION_STREAMING.
  • - *
- * In all cases, this option should be turned off before processing the last - * part of the text. - *

- * - *

- * When the OPTION_STREAMING option is used, it is recommended to - * call orderParagraphsLTR(true) before calling - * setPara() so that later paragraphs may be concatenated to - * previous paragraphs on the right. - *

- * - * @see #setReorderingMode - * @see #setReorderingOptions - * @see #getProcessedLength - * @stable ICU 3.8 - */ - private static final int OPTION_STREAMING = 4; - - /* - * 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 - * UCharacterDirection - */ - /* private */ static final byte L = 0; - private static final byte R = 1; - private static final byte EN = 2; - private static final byte ES = 3; - private static final byte ET = 4; - private static final byte AN = 5; - private static final byte CS = 6; - static final byte B = 7; - private static final byte S = 8; - private static final byte WS = 9; - private static final byte ON = 10; - private static final byte LRE = 11; - private static final byte LRO = 12; - private static final byte AL = 13; - private static final byte RLE = 14; - private static final byte RLO = 15; - private static final byte PDF = 16; - private static final byte NSM = 17; - private static final byte BN = 18; - private static final byte FSI = 19; - private static final byte LRI = 20; - private static final byte RLI = 21; - private static final byte PDI = 22; - private static final byte ENL = PDI + 1; /* EN after W7 */ - private static final byte ENR = ENL + 1; /* EN not subject to W7 */ - - // Number of directional types - private static final int CHAR_DIRECTION_COUNT = 23; - - /** - * Enumerated property Bidi_Paired_Bracket_Type (new in Unicode 6.3). Used in - * Unicode Standard Annex #9: - * Unicode Bidirectional Algorithm. Returns UCharacter.BidiPairedBracketType - * values. - * - * @stable ICU 52 - */ - public static final int BIDI_PAIRED_BRACKET_TYPE = 0x1015; - - /** - * Bidi Paired Bracket Type constants. - * - * @see UProperty#BIDI_PAIRED_BRACKET_TYPE - * @stable ICU 52 - */ - public static interface BidiPairedBracketType { - /** - * Not a paired bracket. - * - * @stable ICU 52 - */ - public static final int NONE = 0; - /** - * Open paired bracket. - * - * @stable ICU 52 - */ - public static final int OPEN = 1; - /** - * Close paired bracket. - * - * @stable ICU 52 - */ - public static final int CLOSE = 2; - /** - * @stable ICU 52 - */ - public static final int COUNT = 3; - } - - /* number of paras entries allocated initially */ - static final int SIMPLE_PARAS_COUNT = 10; - - private static final char CR = '\r'; - private static final char LF = '\n'; - - static final int LRM_BEFORE = 1; - static final int LRM_AFTER = 2; - static final int RLM_BEFORE = 4; - static final int RLM_AFTER = 8; - - /* flags for Opening.flags */ - static final byte FOUND_L = (byte) DirPropFlag(L); - static final byte FOUND_R = (byte) DirPropFlag(R); - - /* - * The following bit is used for the directional isolate status. Stack entries - * corresponding to isolate sequences are greater than ISOLATE. - */ - static final int ISOLATE = 0x0100; - - /* - * reference to parent paragraph object (reference 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 setPara or setLine - */ - BidiBase paraBidi; - - final UBiDiProps bdp; - - /* character array representing the current text */ - char[] text; - - /* length of the current text */ - int originalLength; - - /* - * if the option OPTION_STREAMING is set, this is the length of text actually - * processed by setPara, which may be shorter than the original - * length. Otherwise, it is identical to the original length. - */ - public int length; - - /* - * if option OPTION_REMOVE_CONTROLS is set, and/or Bidi marks are allowed to be - * inserted in one of the reordering modes, the length of the result string may - * be different from the processed length. - */ - int resultLength; - - /* indicators for whether memory may be allocated after construction */ - boolean mayAllocateText; - boolean mayAllocateRuns; - - /* arrays with one value per text-character */ - byte[] dirPropsMemory = new byte[1]; - byte[] levelsMemory = new byte[1]; - byte[] dirProps; - byte[] levels; - - /* are we performing an approximation of the "inverse Bidi" algorithm? */ - boolean isInverse; - - /* are we using the basic algorithm or its variation? */ - int reorderingMode; - - /* bitmask for reordering options */ - int reorderingOptions; - - /* must block separators receive level 0? */ - boolean orderParagraphsLTR; - - /* the paragraph level */ - byte paraLevel; - - /* original paraLevel when contextual */ - /* must be one of DEFAULT_xxx or 0 if not contextual */ - byte defaultParaLevel; - - /* the following is set in setPara, used in processPropertySeq */ - - ImpTabPair impTabPair; /* reference to levels state table pair */ - - /* the overall paragraph or line directionality */ - byte direction; - - /* flags is a bit set for which directional properties are in the text */ - int flags; - - /* lastArabicPos is index to the last AL in the text, -1 if none */ - int lastArabicPos; - - /* characters after trailingWSStart are WS and are */ - /* implicitly at the paraLevel (rule (L1)) - levels may not reflect that */ - int trailingWSStart; - - /* fields for paragraph handling, set in getDirProps() */ - int paraCount; - int[] paras_limit = new int[SIMPLE_PARAS_COUNT]; - byte[] paras_level = new byte[SIMPLE_PARAS_COUNT]; - - /* fields for line reordering */ - int runCount; /* ==-1: runs not set up yet */ - BidiRun[] runsMemory = new BidiRun[0]; - BidiRun[] runs; - - /* for non-mixed text, we only need a tiny array of runs (no allocation) */ - BidiRun[] simpleRuns = { new BidiRun() }; - - /* fields for managing isolate sequences */ - Isolate[] isolates; - - /* 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. - */ - int isolateCount; - - /* mapping of runs in logical order to visual order */ - int[] logicalToVisualRunsMap; - /* flag to indicate that the map has been updated */ - boolean isGoodLogicalToVisualRunsMap; - - /* for inverse Bidi with insertion of directional marks */ - InsertPoints insertPoints = new InsertPoints(); - - /* for option OPTION_REMOVE_CONTROLS */ - int controlCount; - - /* - * Sometimes, bit values are more appropriate to deal with directionality - * properties. Abbreviations in these method names refer to names used in the - * Bidi algorithm. - */ - static int DirPropFlag(byte dir) { - return (1 << dir); - } - - boolean testDirPropFlagAt(int flag, int index) { - return ((DirPropFlag(dirProps[index]) & flag) != 0); - } - - static final int DirPropFlagMultiRuns = DirPropFlag((byte) 31); - - /* to avoid some conditional statements, use tiny constant arrays */ - static final int DirPropFlagLR[] = { DirPropFlag(L), DirPropFlag(R) }; - static final int DirPropFlagE[] = { DirPropFlag(LRE), DirPropFlag(RLE) }; - static final int DirPropFlagO[] = { DirPropFlag(LRO), DirPropFlag(RLO) }; - - static final int DirPropFlagLR(byte level) { - return DirPropFlagLR[level & 1]; - } - - static final int DirPropFlagE(byte level) { - return DirPropFlagE[level & 1]; - } - - static final int DirPropFlagO(byte level) { - return DirPropFlagO[level & 1]; - } - - static final byte DirFromStrong(byte strong) { - return strong == L ? L : R; - } - - static final byte NoOverride(byte level) { - return (byte) (level & ~LEVEL_OVERRIDE); - } - - /* are there any characters that are LTR or RTL? */ - static final int MASK_LTR = DirPropFlag(L) | DirPropFlag(EN) | DirPropFlag(ENL) | DirPropFlag(ENR) | DirPropFlag(AN) - | DirPropFlag(LRE) | DirPropFlag(LRO) | DirPropFlag(LRI); - static final int MASK_RTL = DirPropFlag(R) | DirPropFlag(AL) | DirPropFlag(RLE) | DirPropFlag(RLO) - | DirPropFlag(RLI); - - static final int MASK_R_AL = DirPropFlag(R) | DirPropFlag(AL); - - /* explicit embedding codes */ - private static final int MASK_EXPLICIT = DirPropFlag(LRE) | DirPropFlag(LRO) | DirPropFlag(RLE) | DirPropFlag(RLO) - | DirPropFlag(PDF); - private static final int MASK_BN_EXPLICIT = DirPropFlag(BN) | MASK_EXPLICIT; - - /* explicit isolate codes */ - private static final int MASK_ISO = DirPropFlag(LRI) | DirPropFlag(RLI) | DirPropFlag(FSI) | DirPropFlag(PDI); - - /* paragraph and segment separators */ - private static final int MASK_B_S = DirPropFlag(B) | DirPropFlag(S); - - /* all types that are counted as White Space or Neutral in some steps */ - static final int MASK_WS = MASK_B_S | DirPropFlag(WS) | MASK_BN_EXPLICIT | MASK_ISO; - - /* types that are neutrals or could becomes neutrals in (Wn) */ - private static final int MASK_POSSIBLE_N = DirPropFlag(ON) | DirPropFlag(CS) | DirPropFlag(ES) | DirPropFlag(ET) - | MASK_WS; - - /* - * These types may be changed to "e", the embedding type (L or R) of the run, in - * the Bidi algorithm (N2) - */ - private static final int MASK_EMBEDDING = DirPropFlag(NSM) | MASK_POSSIBLE_N; - - /* - * the dirProp's L and R are defined to 0 and 1 values in - * UCharacterDirection.java - */ - private static byte GetLRFromLevel(byte level) { - return (byte) (level & 1); - } - - private static boolean IsDefaultLevel(byte level) { - return ((level & LEVEL_DEFAULT_LTR) == LEVEL_DEFAULT_LTR); - } - - static boolean IsBidiControlChar(int c) { - /* - * check for range 0x200c to 0x200f (ZWNJ, ZWJ, LRM, RLM) or 0x202a to 0x202e - * (LRE, RLE, PDF, LRO, RLO) - */ - return (((c & 0xfffffffc) == 0x200c) || ((c >= 0x202a) && (c <= 0x202e)) || ((c >= 0x2066) && (c <= 0x2069))); - } - - void verifyValidPara() { - if (!(this == this.paraBidi)) { - throw new IllegalStateException(); - } - } - - void verifyValidParaOrLine() { - BidiBase para = this.paraBidi; - /* verify Para */ - if (this == para) { - return; - } - /* verify Line */ - if ((para == null) || (para != para.paraBidi)) { - throw new IllegalStateException(); - } - } - - void verifyRange(int index, int start, int limit) { - if (index < start || index >= limit) { - throw new IllegalArgumentException("Value " + index + " is out of range " + start + " to " + limit); - } - } - - /** - * Allocate a Bidi object with preallocated memory for internal - * structures. This method provides a Bidi object like the default - * constructor but it also preallocates memory for internal structures according - * to the sizings supplied by the caller. - *

- * 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 the default constructor. - * - * @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. - * - * @throws IllegalArgumentException if maxLength or maxRunCount is less than 0 - * @stable ICU 3.8 - */ - public BidiBase(int maxLength, int maxRunCount) { - /* check the argument values */ - if (maxLength < 0 || maxRunCount < 0) { - throw new IllegalArgumentException(); - } - - /* - * reset the object, all reference variables null, all flags false, all sizes 0. - * In fact, we don't need to do anything, since class members are initialized as - * zero when an instance is created. - */ - /* - * mayAllocateText = false; mayAllocateRuns = false; orderParagraphsLTR = false; - * paraCount = 0; runCount = 0; trailingWSStart = 0; flags = 0; paraLevel = 0; - * defaultParaLevel = 0; direction = 0; - */ - /* get Bidi properties */ - bdp = UBiDiProps.INSTANCE; - - /* allocate memory for arrays as requested */ - if (maxLength > 0) { - getInitialDirPropsMemory(maxLength); - getInitialLevelsMemory(maxLength); - } else { - mayAllocateText = true; - } - - if (maxRunCount > 0) { - // if maxRunCount == 1, use simpleRuns[] - if (maxRunCount > 1) { - getInitialRunsMemory(maxRunCount); - } - } else { - mayAllocateRuns = true; - } - } - - /* - * We are allowed to allocate memory if object==null or mayAllocate==true for - * each array that we need. - * - * Assume sizeNeeded>0. If object != null, then assume size > 0. - */ - private Object getMemory(String label, Object array, Class arrayClass, boolean mayAllocate, int sizeNeeded) { - int len = Array.getLength(array); - - /* we have at least enough memory and must not allocate */ - if (sizeNeeded == len) { - return array; - } - if (!mayAllocate) { - /* we must not allocate */ - if (sizeNeeded <= len) { - return array; - } - throw new OutOfMemoryError("Failed to allocate memory for " + label); - } - /* we may try to grow or shrink */ - /* - * FOOD FOR THOUGHT: when shrinking it should be possible to avoid the - * allocation altogether and rely on this.length - */ - try { - return Array.newInstance(arrayClass, sizeNeeded); - } catch (Exception e) { - throw new OutOfMemoryError("Failed to allocate memory for " + label); - } - } - - /* helper methods for each allocated array */ - private void getDirPropsMemory(boolean mayAllocate, int len) { - Object array = getMemory("DirProps", dirPropsMemory, Byte.TYPE, mayAllocate, len); - dirPropsMemory = (byte[]) array; - } - - void getDirPropsMemory(int len) { - getDirPropsMemory(mayAllocateText, len); - } - - private void getLevelsMemory(boolean mayAllocate, int len) { - Object array = getMemory("Levels", levelsMemory, Byte.TYPE, mayAllocate, len); - levelsMemory = (byte[]) array; - } - - void getLevelsMemory(int len) { - getLevelsMemory(mayAllocateText, len); - } - - private void getRunsMemory(boolean mayAllocate, int len) { - Object array = getMemory("Runs", runsMemory, BidiRun.class, mayAllocate, len); - runsMemory = (BidiRun[]) array; - } - - void getRunsMemory(int len) { - getRunsMemory(mayAllocateRuns, len); - } - - /* additional methods used by constructor - always allow allocation */ - private void getInitialDirPropsMemory(int len) { - getDirPropsMemory(true, len); - } - - private void getInitialLevelsMemory(int len) { - getLevelsMemory(true, len); - } - - private void getInitialRunsMemory(int len) { - getRunsMemory(true, len); - } - - /** - * Is this Bidi object set to perform the inverse Bidi algorithm? - *

- * Note: calling this method after setting the reordering mode with - * setReorderingMode will return true if the - * reordering mode was set to REORDER_INVERSE_NUMBERS_AS_L, - * false for all other values. - *

- * - * @return true if the Bidi object is set to perform - * the inverse Bidi algorithm by handling numbers as L. - * - * @see #setInverse - * @see #setReorderingMode - * @see #REORDER_INVERSE_NUMBERS_AS_L - * @stable ICU 3.8 - */ - public boolean isInverse() { - return isInverse; - } - - /* perform (P2)..(P3) ------------------------------------------------------- */ - - /* - * Check that there are enough entries in the arrays paras_limit and paras_level - */ - private void checkParaCount() { - int[] saveLimits; - byte[] saveLevels; - int count = paraCount; - if (count <= paras_level.length) - return; - int oldLength = paras_level.length; - saveLimits = paras_limit; - saveLevels = paras_level; - try { - paras_limit = new int[count * 2]; - paras_level = new byte[count * 2]; - } catch (Exception e) { - throw new OutOfMemoryError("Failed to allocate memory for paras"); - } - System.arraycopy(saveLimits, 0, paras_limit, 0, oldLength); - System.arraycopy(saveLevels, 0, paras_level, 0, oldLength); - } - - /* - * Get the directional properties for the text, calculate the flags bit-set, and - * determine the paragraph level if necessary (in paras_level[i]). 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 final int NOT_SEEKING_STRONG = 0; /* 0: not contextual paraLevel, not after FSI */ - static final int SEEKING_STRONG_FOR_PARA = 1; /* 1: looking for first strong char in para */ - static final int SEEKING_STRONG_FOR_FSI = 2; /* 2: looking for first strong after FSI */ - static final int LOOKING_FOR_PDI = 3; /* 3: found strong after FSI, looking for PDI */ - - private void getDirProps() { - int i = 0, i0, i1; - flags = 0; /* collect all directionalities in the text */ - int uchar; - byte dirProp; - byte defaultParaLevel = 0; /* initialize to avoid compiler warnings */ - boolean isDefaultLevel = IsDefaultLevel(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 - */ - boolean isDefaultLevelInverse = isDefaultLevel && (reorderingMode == REORDER_INVERSE_LIKE_DIRECT - || reorderingMode == REORDER_INVERSE_FOR_NUMBERS_SPECIAL); - lastArabicPos = -1; - int controlCount = 0; - boolean removeBidiControls = (reorderingOptions & OPTION_REMOVE_CONTROLS) != 0; - - byte state; - byte 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 - */ - int[] isolateStartStack = new int[MAX_EXPLICIT_LEVEL + 1]; - /* - * The following stack contains the last known state before encountering the - * initiator of an isolate sequence - */ - byte[] previousStateStack = new byte[MAX_EXPLICIT_LEVEL + 1]; - int stackLast = -1; - - if ((reorderingOptions & OPTION_STREAMING) != 0) - length = 0; - defaultParaLevel = (byte) (paraLevel & 1); - - if (isDefaultLevel) { - paras_level[0] = defaultParaLevel; - lastStrong = defaultParaLevel; - state = SEEKING_STRONG_FOR_PARA; - } else { - paras_level[0] = paraLevel; - state = NOT_SEEKING_STRONG; - } - /* count paragraphs and determine the paragraph level (P2..P3) */ - /* - * see comment on constant fields: the LEVEL_DEFAULT_XXX values are designed so - * that their low-order bit alone yields the intended default - */ - - for (i = 0; i < originalLength; /* i is incremented in the loop */) { - i0 = i; /* index of first code unit */ - uchar = UTF16.charAt(text, 0, originalLength, i); - i += UTF16.getCharCount(uchar); - i1 = i - 1; /* index of last code unit, gets the directional property */ - - dirProp = (byte) getCustomizedClass(uchar); - flags |= DirPropFlag(dirProp); - dirProps[i1] = dirProp; - if (i1 > i0) { /* set previous code units' properties to BN */ - flags |= DirPropFlag(BN); - do { - dirProps[--i1] = BN; - } while (i1 > i0); - } - if (removeBidiControls && IsBidiControlChar(uchar)) { - controlCount++; - } - if (dirProp == L) { - if (state == SEEKING_STRONG_FOR_PARA) { - paras_level[paraCount - 1] = 0; - state = NOT_SEEKING_STRONG; - } else if (state == SEEKING_STRONG_FOR_FSI) { - if (stackLast <= MAX_EXPLICIT_LEVEL) { - /* no need for next statement, already set by default */ - /* dirProps[isolateStartStack[stackLast]] = LRI; */ - flags |= DirPropFlag(LRI); - } - state = LOOKING_FOR_PDI; - } - lastStrong = L; - continue; - } - if (dirProp == R || dirProp == AL) { - if (state == SEEKING_STRONG_FOR_PARA) { - paras_level[paraCount - 1] = 1; - state = NOT_SEEKING_STRONG; - } else if (state == SEEKING_STRONG_FOR_FSI) { - if (stackLast <= MAX_EXPLICIT_LEVEL) { - dirProps[isolateStartStack[stackLast]] = RLI; - flags |= DirPropFlag(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 <= 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 <= MAX_EXPLICIT_LEVEL) { - /* no need for next statement, already set by default */ - /* dirProps[isolateStartStack[stackLast]] = LRI; */ - flags |= DirPropFlag(LRI); - } - } - if (stackLast >= 0) { - if (stackLast <= MAX_EXPLICIT_LEVEL) - state = previousStateStack[stackLast]; - stackLast--; - } - continue; - } - if (dirProp == B) { - if (i < originalLength && uchar == CR && text[i] == LF) /* do nothing on the CR */ - continue; - paras_limit[paraCount - 1] = i; - if (isDefaultLevelInverse && lastStrong == R) - paras_level[paraCount - 1] = 1; - if ((reorderingOptions & OPTION_STREAMING) != 0) { - /* - * When streaming, we only process whole paragraphs thus some updates are only - * done on paragraph boundaries - */ - length = i; /* i is index to next character */ - this.controlCount = controlCount; - } - if (i < originalLength) { /* B not last char in text */ - paraCount++; - checkParaCount(); /* check that there is enough memory for a new para entry */ - if (isDefaultLevel) { - paras_level[paraCount - 1] = defaultParaLevel; - state = SEEKING_STRONG_FOR_PARA; - lastStrong = defaultParaLevel; - } else { - paras_level[paraCount - 1] = paraLevel; - state = NOT_SEEKING_STRONG; - } - stackLast = -1; - } - continue; - } - } - /* +Ignore still open isolate sequences with overflow */ - if (stackLast > MAX_EXPLICIT_LEVEL) { - stackLast = 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 |= DirPropFlag(LRI); - break; - } - state = previousStateStack[stackLast]; - stackLast--; - } - /* When streaming, ignore text after the last paragraph separator */ - if ((reorderingOptions & OPTION_STREAMING) != 0) { - if (length < originalLength) - paraCount--; - } else { - paras_limit[paraCount - 1] = originalLength; - this.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) { - paras_level[paraCount - 1] = 1; - } - if (isDefaultLevel) { - paraLevel = paras_level[0]; - } - /* - * The following is needed to resolve the text direction for default level - * paragraphs containing no strong character - */ - for (i = 0; i < paraCount; i++) - flags |= DirPropFlagLR(paras_level[i]); - - if (orderParagraphsLTR && (flags & DirPropFlag(B)) != 0) { - flags |= DirPropFlag(L); - } - } - - /* determine the paragraph level at position index */ - byte GetParaLevelAt(int pindex) { - if (defaultParaLevel == 0 || pindex < paras_limit[0]) - return paraLevel; - int i; - for (i = 1; i < paraCount; i++) - if (pindex < paras_limit[i]) - break; - if (i >= paraCount) - i = paraCount - 1; - return paras_level[i]; - } - - /* 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. - */ - - private void bracketInit(BracketData bd) { - bd.isoRunLast = 0; - bd.isoRuns[0] = new IsoRun(); - bd.isoRuns[0].start = 0; - bd.isoRuns[0].limit = 0; - bd.isoRuns[0].level = GetParaLevelAt(0); - bd.isoRuns[0].lastStrong = bd.isoRuns[0].lastBase = bd.isoRuns[0].contextDir = (byte) (GetParaLevelAt(0) & 1); - bd.isoRuns[0].contextPos = 0; - bd.openings = new Opening[SIMPLE_PARAS_COUNT]; - bd.isNumbersSpecial = reorderingMode == REORDER_NUMBERS_SPECIAL - || reorderingMode == REORDER_INVERSE_FOR_NUMBERS_SPECIAL; - } - - /* paragraph boundary */ - private void bracketProcessB(BracketData bd, byte level) { - bd.isoRunLast = 0; - bd.isoRuns[0].limit = 0; - bd.isoRuns[0].level = level; - bd.isoRuns[0].lastStrong = bd.isoRuns[0].lastBase = bd.isoRuns[0].contextDir = (byte) (level & 1); - bd.isoRuns[0].contextPos = 0; - } - - /* LRE, LRO, RLE, RLO, PDF */ - private void bracketProcessBoundary(BracketData bd, int lastCcPos, byte contextLevel, byte embeddingLevel) { - IsoRun pLastIsoRun = bd.isoRuns[bd.isoRunLast]; - if ((DirPropFlag(dirProps[lastCcPos]) & MASK_ISO) != 0) /* after an isolate */ - return; - if (NoOverride(embeddingLevel) > NoOverride(contextLevel)) /* not a PDF */ - contextLevel = embeddingLevel; - pLastIsoRun.limit = pLastIsoRun.start; - pLastIsoRun.level = embeddingLevel; - pLastIsoRun.lastStrong = pLastIsoRun.lastBase = pLastIsoRun.contextDir = (byte) (contextLevel & 1); - pLastIsoRun.contextPos = lastCcPos; - } - - /* LRI or RLI */ - private void bracketProcessLRI_RLI(BracketData bd, byte level) { - IsoRun pLastIsoRun = bd.isoRuns[bd.isoRunLast]; - short lastLimit; - pLastIsoRun.lastBase = ON; - lastLimit = pLastIsoRun.limit; - bd.isoRunLast++; - pLastIsoRun = bd.isoRuns[bd.isoRunLast]; - if (pLastIsoRun == null) - pLastIsoRun = bd.isoRuns[bd.isoRunLast] = new IsoRun(); - pLastIsoRun.start = pLastIsoRun.limit = lastLimit; - pLastIsoRun.level = level; - pLastIsoRun.lastStrong = pLastIsoRun.lastBase = pLastIsoRun.contextDir = (byte) (level & 1); - pLastIsoRun.contextPos = 0; - } - - /* PDI */ - private void bracketProcessPDI(BracketData bd) { - IsoRun pLastIsoRun; - bd.isoRunLast--; - pLastIsoRun = bd.isoRuns[bd.isoRunLast]; - pLastIsoRun.lastBase = ON; - } - - /* newly found opening bracket: create an openings entry */ - private void bracketAddOpening(BracketData bd, char match, int position) { - IsoRun pLastIsoRun = bd.isoRuns[bd.isoRunLast]; - Opening pOpening; - if (pLastIsoRun.limit >= bd.openings.length) { /* no available new entry */ - Opening[] saveOpenings = bd.openings; - int count; - try { - count = bd.openings.length; - bd.openings = new Opening[count * 2]; - } catch (Exception e) { - throw new OutOfMemoryError("Failed to allocate memory for openings"); - } - System.arraycopy(saveOpenings, 0, bd.openings, 0, count); - } - pOpening = bd.openings[pLastIsoRun.limit]; - if (pOpening == null) - pOpening = bd.openings[pLastIsoRun.limit] = new Opening(); - pOpening.position = position; - pOpening.match = match; - pOpening.contextDir = pLastIsoRun.contextDir; - pOpening.contextPos = pLastIsoRun.contextPos; - pOpening.flags = 0; - pLastIsoRun.limit++; - } - - /* - * change N0c1 to N0c2 when a preceding bracket is assigned the embedding level - */ - private void fixN0c(BracketData bd, int openingIndex, int newPropPosition, byte newProp) { - /* This function calls itself recursively */ - IsoRun pLastIsoRun = bd.isoRuns[bd.isoRunLast]; - Opening qOpening; - int k, openingPosition, closingPosition; - for (k = openingIndex + 1; k < pLastIsoRun.limit; k++) { - qOpening = bd.openings[k]; - if (qOpening.match >= 0) /* not an N0c match */ - continue; - if (newPropPosition < qOpening.contextPos) - 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; return L or R if N0b or N0c, ON if N0d */ - private byte bracketProcessClosing(BracketData bd, int openIdx, int position) { - IsoRun pLastIsoRun = bd.isoRuns[bd.isoRunLast]; - Opening pOpening, qOpening; - byte direction; - boolean stable; - byte newProp; - pOpening = bd.openings[openIdx]; - direction = (byte) (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) > 0) - || (direction == 1 && (pOpening.flags & FOUND_R) > 0)) { /* N0b */ - newProp = direction; - } else if ((pOpening.flags & (FOUND_L | FOUND_R)) != 0) { /* N0c */ - /* - * it is stable if there is no preceding text or in conditions too complicated - * and not worth checking - */ - stable = (openIdx == pLastIsoRun.start); - if (direction != pOpening.contextDir) - newProp = pOpening.contextDir; /* N0c1 */ - else - newProp = direction; /* N0c2 */ - } else { - /* forget this and any brackets nested within this pair */ - pLastIsoRun.limit = (short) openIdx; - return ON; /* N0d */ - } - dirProps[pOpening.position] = newProp; - dirProps[position] = newProp; - /* Update nested N0c pairs that may be affected */ - fixN0c(bd, openIdx, pOpening.position, newProp); - if (stable) { - pLastIsoRun.limit = (short) 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 { - int 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; k < pLastIsoRun.limit; 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 */ - private void bracketProcessChar(BracketData bd, int position) { - IsoRun pLastIsoRun = bd.isoRuns[bd.isoRunLast]; - byte dirProp, newProp; - byte level; - dirProp = dirProps[position]; - if (dirProp == ON) { - char c, match; - int 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 = 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 = newProp; - pLastIsoRun.contextPos = position; - level = levels[position]; - if ((level & LEVEL_OVERRIDE) != 0) { /* X4, X5 */ - short flag; - int i; - newProp = (byte) (level & 1); - pLastIsoRun.lastStrong = newProp; - flag = (short) DirPropFlag(newProp); - for (i = pLastIsoRun.start; i < idx; i++) - bd.openings[i].flags |= flag; - /* matching brackets are not overridden by LRO/RLO */ - levels[position] &= ~LEVEL_OVERRIDE; - } - /* matching brackets are not overridden by LRO/RLO */ - levels[bd.openings[idx].position] &= ~LEVEL_OVERRIDE; - return; - } - /* - * 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 != 0) { - match = (char) UCharacter.getBidiPairedBracket(c); /* get the matching char */ - } else { - match = 0; - } - if (match != c && /* has a matching char */ - UCharacter.getIntPropertyValue(c, BIDI_PAIRED_BRACKET_TYPE) == - /* opening bracket */ BidiPairedBracketType.OPEN) { - /* - * special case: process synonyms create an opening entry for each synonym - */ - if (match == 0x232A) { /* RIGHT-POINTING ANGLE BRACKET */ - bracketAddOpening(bd, (char) 0x3009, position); - } else if (match == 0x3009) { /* RIGHT ANGLE BRACKET */ - bracketAddOpening(bd, (char) 0x232A, position); - } - bracketAddOpening(bd, match, position); - } - } - level = levels[position]; - if ((level & LEVEL_OVERRIDE) != 0) { /* X4, X5 */ - newProp = (byte) (level & 1); - if (dirProp != S && dirProp != WS && dirProp != ON) - dirProps[position] = newProp; - pLastIsoRun.lastBase = newProp; - pLastIsoRun.lastStrong = newProp; - pLastIsoRun.contextDir = newProp; - pLastIsoRun.contextPos = position; - } else if (dirProp <= R || dirProp == AL) { - newProp = DirFromStrong(dirProp); - pLastIsoRun.lastBase = dirProp; - pLastIsoRun.lastStrong = dirProp; - pLastIsoRun.contextDir = 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 = L; - pLastIsoRun.contextPos = position; - } else { - newProp = R; /* N0 */ - if (pLastIsoRun.lastStrong == AL) - dirProps[position] = AN; /* W2 */ - else - dirProps[position] = ENR; - pLastIsoRun.contextDir = R; - pLastIsoRun.contextPos = position; - } - } else if (dirProp == AN) { - newProp = R; /* N0 */ - pLastIsoRun.lastBase = AN; - pLastIsoRun.contextDir = 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) { - int i; - short flag = (short) DirPropFlag(DirFromStrong(newProp)); - for (i = pLastIsoRun.start; i < pLastIsoRun.limit; i++) - if (position > bd.openings[i].position) - bd.openings[i].flags |= flag; - } - } - - /* perform (X1)..(X9) ------------------------------------------------------- */ - - /* determine if the text is mixed-directional or single-directional */ - private byte directionFromFlags() { - - /* if the text contains AN and neutrals, then some neutrals may become RTL */ - if (!((flags & MASK_RTL) != 0 || ((flags & DirPropFlag(AN)) != 0 && (flags & MASK_POSSIBLE_N) != 0))) { - return LTR; - } else if ((flags & MASK_LTR) == 0) { - return RTL; - } else { - return 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 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 MAX_EXPLICIT_LEVEL is odd. - * - * Returns the direction - * - */ - private byte resolveExplicitLevels() { - int i = 0; - byte dirProp; - byte level = GetParaLevelAt(0); - byte dirct; - isolateCount = 0; - - /* determine if the text is mixed-directional or single-directional */ - dirct = directionFromFlags(); - - /* we may not need to resolve any explicit levels */ - if (dirct != MIXED) { - /* not mixed directionality: levels don't matter - trailingWSStart will be 0 */ - return dirct; - } - - if (reorderingMode > REORDER_LAST_LOGICAL_TO_VISUAL) { - /* inverse BiDi: mixed, but all characters are at the same embedding level */ - /* set all levels to the paragraph level */ - int paraIndex, start, limit; - for (paraIndex = 0; paraIndex < paraCount; paraIndex++) { - if (paraIndex == 0) - start = 0; - else - start = paras_limit[paraIndex - 1]; - limit = paras_limit[paraIndex]; - level = paras_level[paraIndex]; - for (i = start; i < limit; i++) - levels[i] = level; - } - return dirct; /* no bracket matching for inverse BiDi */ - } - if ((flags & (MASK_EXPLICIT | MASK_ISO)) == 0) { - /* no embeddings, set all levels to the paragraph level */ - /* we still have to perform bracket matching */ - int paraIndex, start, limit; - BracketData bracketData = new BracketData(); - bracketInit(bracketData); - for (paraIndex = 0; paraIndex < paraCount; paraIndex++) { - if (paraIndex == 0) - start = 0; - else - start = paras_limit[paraIndex - 1]; - limit = paras_limit[paraIndex]; - level = paras_level[paraIndex]; - for (i = start; i < limit; i++) { - levels[i] = level; - dirProp = dirProps[i]; - if (dirProp == BN) - continue; - if (dirProp == B) { - if ((i + 1) < length) { - if (text[i] == CR && text[i + 1] == LF) - continue; /* skip CR when followed by LF */ - bracketProcessB(bracketData, level); - } - continue; - } - bracketProcessChar(bracketData, i); - } - } - return dirct; - } - /* continue to perform (Xn) */ - - /* - * (X1) level is set for all codes, embeddingLevel keeps track of the push/pop - * operations - */ - /* - * both variables may carry the LEVEL_OVERRIDE flag to indicate the override - * status - */ - byte embeddingLevel = level, newLevel; - byte previousLevel = level; /* previous level for regular (not CC) characters */ - int lastCcPos = 0; /* index of last effective LRx,RLx, PDx */ - - /* - * The following stack remembers the embedding level and the ISOLATE flag of - * level runs. stackLast points to its current entry. - */ - short[] stack = new short[MAX_EXPLICIT_LEVEL + 2]; /* - * we never push anything >= MAX_EXPLICIT_LEVEL but we need - * one more entry as base - */ - int stackLast = 0; - int overflowIsolateCount = 0; - int overflowEmbeddingCount = 0; - int validIsolateCount = 0; - BracketData bracketData = new BracketData(); - bracketInit(bracketData); - stack[0] = level; /* initialize base entry to para level, no override, no isolate */ - - /* recalculate the flags */ - flags = 0; - - for (i = 0; i < length; i++) { - dirProp = dirProps[i]; - switch (dirProp) { - case LRE: - case RLE: - case LRO: - case RLO: - /* (X2, X3, X4, X5) */ - flags |= DirPropFlag(BN); - levels[i] = previousLevel; - if (dirProp == LRE || dirProp == LRO) { - /* least greater even level */ - newLevel = (byte) ((embeddingLevel + 2) & ~(LEVEL_OVERRIDE | 1)); - } else { - /* least greater odd level */ - newLevel = (byte) ((NoOverride(embeddingLevel) + 1) | 1); - } - if (newLevel <= MAX_EXPLICIT_LEVEL && overflowIsolateCount == 0 && overflowEmbeddingCount == 0) { - lastCcPos = i; - embeddingLevel = newLevel; - if (dirProp == LRO || dirProp == RLO) - embeddingLevel |= LEVEL_OVERRIDE; - stackLast++; - stack[stackLast] = embeddingLevel; - /* - * we don't need to set LEVEL_OVERRIDE off for LRE and RLE since this has - * already been done for newLevel which is the source for embeddingLevel. - */ - } else { - if (overflowIsolateCount == 0) - overflowEmbeddingCount++; - } - break; - case PDF: - /* (X7) */ - flags |= DirPropFlag(BN); - levels[i] = previousLevel; - /* handle all the overflow cases first */ - if (overflowIsolateCount > 0) { - break; - } - if (overflowEmbeddingCount > 0) { - overflowEmbeddingCount--; - break; - } - if (stackLast > 0 && stack[stackLast] < ISOLATE) { /* not an isolate entry */ - lastCcPos = i; - stackLast--; - embeddingLevel = (byte) stack[stackLast]; - } - break; - case LRI: - case RLI: - flags |= DirPropFlag(ON) | DirPropFlagLR(embeddingLevel); - levels[i] = NoOverride(embeddingLevel); - if (NoOverride(embeddingLevel) != NoOverride(previousLevel)) { - bracketProcessBoundary(bracketData, lastCcPos, previousLevel, embeddingLevel); - flags |= DirPropFlagMultiRuns; - } - previousLevel = embeddingLevel; - /* (X5a, X5b) */ - if (dirProp == LRI) - /* least greater even level */ - newLevel = (byte) ((embeddingLevel + 2) & ~(LEVEL_OVERRIDE | 1)); - else - /* least greater odd level */ - newLevel = (byte) ((NoOverride(embeddingLevel) + 1) | 1); - if (newLevel <= MAX_EXPLICIT_LEVEL && overflowIsolateCount == 0 && overflowEmbeddingCount == 0) { - flags |= DirPropFlag(dirProp); - lastCcPos = i; - validIsolateCount++; - if (validIsolateCount > isolateCount) - isolateCount = validIsolateCount; - embeddingLevel = newLevel; - /* - * we can increment stackLast without checking because newLevel will exceed - * UBIDI_MAX_EXPLICIT_LEVEL before stackLast overflows - */ - stackLast++; - stack[stackLast] = (short) (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 (NoOverride(embeddingLevel) != NoOverride(previousLevel)) { - bracketProcessBoundary(bracketData, lastCcPos, previousLevel, embeddingLevel); - flags |= DirPropFlagMultiRuns; - } - /* (X6a) */ - if (overflowIsolateCount > 0) { - overflowIsolateCount--; - /* make it WS so that it is handled by adjustWSLevels() */ - dirProps[i] = WS; - } else if (validIsolateCount > 0) { - flags |= DirPropFlag(PDI); - lastCcPos = i; - overflowEmbeddingCount = 0; - while (stack[stackLast] < ISOLATE) /* pop embedding entries */ - stackLast--; /* until the last isolate entry */ - stackLast--; /* pop also the last isolate entry */ - validIsolateCount--; - bracketProcessPDI(bracketData); - } else - /* make it WS so that it is handled by adjustWSLevels() */ - dirProps[i] = WS; - embeddingLevel = (byte) (stack[stackLast] & ~ISOLATE); - flags |= DirPropFlag(ON) | DirPropFlagLR(embeddingLevel); - previousLevel = embeddingLevel; - levels[i] = NoOverride(embeddingLevel); - break; - case B: - flags |= DirPropFlag(B); - levels[i] = GetParaLevelAt(i); - if ((i + 1) < length) { - if (text[i] == CR && text[i + 1] == LF) - break; /* skip CR when followed by LF */ - overflowEmbeddingCount = overflowIsolateCount = 0; - validIsolateCount = 0; - stackLast = 0; - previousLevel = embeddingLevel = GetParaLevelAt(i + 1); - stack[0] = embeddingLevel; /* initialize base entry to para level, no override, no isolate */ - bracketProcessB(bracketData, embeddingLevel); - } - break; - case BN: - /* BN, LRE, RLE, and PDF are supposed to be removed (X9) */ - /* they will get their levels set correctly in adjustWSLevels() */ - levels[i] = previousLevel; - flags |= DirPropFlag(BN); - break; - default: - /* all other types are normal characters and get the "real" level */ - if (NoOverride(embeddingLevel) != NoOverride(previousLevel)) { - bracketProcessBoundary(bracketData, lastCcPos, previousLevel, embeddingLevel); - flags |= DirPropFlagMultiRuns; - if ((embeddingLevel & LEVEL_OVERRIDE) != 0) - flags |= DirPropFlagO(embeddingLevel); - else - flags |= DirPropFlagE(embeddingLevel); - } - previousLevel = embeddingLevel; - levels[i] = embeddingLevel; - bracketProcessChar(bracketData, i); - /* the dirProp may have been changed in bracketProcessChar() */ - flags |= DirPropFlag(dirProps[i]); - break; - } - } - if ((flags & MASK_EMBEDDING) != 0) { - flags |= DirPropFlagLR(paraLevel); - } - if (orderParagraphsLTR && (flags & DirPropFlag(B)) != 0) { - flags |= DirPropFlag(L); - } - /* again, determine if the text is mixed-directional or single-directional */ - dirct = directionFromFlags(); - - return dirct; - } - - /* - * 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. - */ - private byte checkExplicitLevels() { - byte dirProp; - int i; - int isolateCount = 0; - - this.flags = 0; /* collect all directionalities in the text */ - byte level; - this.isolateCount = 0; - - for (i = 0; i < length; ++i) { - if (levels[i] == 0) { - levels[i] = paraLevel; - } - - // for backward compatibility - if (MAX_EXPLICIT_LEVEL < (levels[i] & 0x7f)) { - if ((levels[i] & LEVEL_OVERRIDE) != 0) { - levels[i] = (byte) (paraLevel | LEVEL_OVERRIDE); - } else { - levels[i] = paraLevel; - } - } - - level = levels[i]; - dirProp = dirProps[i]; - if (dirProp == LRI || dirProp == RLI) { - isolateCount++; - if (isolateCount > this.isolateCount) - this.isolateCount = isolateCount; - } else if (dirProp == PDI) { - isolateCount--; - } else if (dirProp == B) { - isolateCount = 0; - } - if ((level & LEVEL_OVERRIDE) != 0) { - /* keep the override flag in levels[i] but adjust the flags */ - level &= ~LEVEL_OVERRIDE; /* make the range check below simpler */ - flags |= DirPropFlagO(level); - } else { - /* set the flags */ - flags |= DirPropFlagE(level) | DirPropFlag(dirProp); - } - if ((level < GetParaLevelAt(i) && !((0 == level) && (dirProp == B))) || (MAX_EXPLICIT_LEVEL < level)) { - /* level out of bounds */ - throw new IllegalArgumentException("level " + level + " out of bounds at " + i); - } - } - if ((flags & MASK_EMBEDDING) != 0) { - flags |= DirPropFlagLR(paraLevel); - } - /* determine if the text is mixed-directional or single-directional */ - return directionFromFlags(); - } - - /*********************************************************************/ - /* 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 "_(x,y)" where x represents an action */ - /* to perform and y represents the next state. */ - /* */ - /*********************************************************************/ - - /* Definitions and type for properties state tables */ - /*********************************************************************/ - private static final int IMPTABPROPS_COLUMNS = 16; - private static final int IMPTABPROPS_RES = IMPTABPROPS_COLUMNS - 1; - - private static short GetStateProps(short cell) { - return (short) (cell & 0x1f); - } - - private static short GetActionProps(short cell) { - return (short) (cell >> 5); - } - - private static final short 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 }; - private static final short _L = 0; - private static final short _R = 1; - private static final short _EN = 2; - private static final short _AN = 3; - private static final short _ON = 4; - private static final short _S = 5; - private static final short _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. */ - /* */ - /* */ - private static final short impTabProps[][] = { - /* 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, _ON }, - /* 1 L */ { 1, 32 + 2, 32 + 4, 32 + 5, 32 + 7, 32 + 15, 32 + 17, 32 + 7, 32 + 9, 32 + 7, 1, 1, 32 + 3, - 32 + 18, 32 + 21, _L }, - /* 2 R */ { 32 + 1, 2, 32 + 4, 32 + 5, 32 + 7, 32 + 15, 32 + 17, 32 + 7, 32 + 9, 32 + 7, 2, 2, 32 + 3, - 32 + 18, 32 + 21, _R }, - /* 3 AL */ { 32 + 1, 32 + 2, 32 + 6, 32 + 6, 32 + 8, 32 + 16, 32 + 17, 32 + 8, 32 + 8, 32 + 8, 3, 3, 3, - 32 + 18, 32 + 21, _R }, - /* 4 EN */ { 32 + 1, 32 + 2, 4, 32 + 5, 32 + 7, 32 + 15, 32 + 17, 64 + 10, 11, 64 + 10, 4, 4, 32 + 3, 18, - 21, _EN }, - /* 5 AN */ { 32 + 1, 32 + 2, 32 + 4, 5, 32 + 7, 32 + 15, 32 + 17, 32 + 7, 32 + 9, 64 + 12, 5, 5, 32 + 3, - 32 + 18, 32 + 21, _AN }, - /* 6 AL:EN/AN */ { 32 + 1, 32 + 2, 6, 6, 32 + 8, 32 + 16, 32 + 17, 32 + 8, 32 + 8, 64 + 13, 6, 6, 32 + 3, - 18, 21, _AN }, - /* 7 ON */ { 32 + 1, 32 + 2, 32 + 4, 32 + 5, 7, 32 + 15, 32 + 17, 7, 64 + 14, 7, 7, 7, 32 + 3, 32 + 18, - 32 + 21, _ON }, - /* 8 AL:ON */ { 32 + 1, 32 + 2, 32 + 6, 32 + 6, 8, 32 + 16, 32 + 17, 8, 8, 8, 8, 8, 32 + 3, 32 + 18, - 32 + 21, _ON }, - /* 9 ET */ { 32 + 1, 32 + 2, 4, 32 + 5, 7, 32 + 15, 32 + 17, 7, 9, 7, 9, 9, 32 + 3, 18, 21, _ON }, - /* 10 EN+ES/CS */ { 96 + 1, 96 + 2, 4, 96 + 5, 128 + 7, 96 + 15, 96 + 17, 128 + 7, 128 + 14, 128 + 7, 10, - 128 + 7, 96 + 3, 18, 21, _EN }, - /* 11 EN+ET */ { 32 + 1, 32 + 2, 4, 32 + 5, 32 + 7, 32 + 15, 32 + 17, 32 + 7, 11, 32 + 7, 11, 11, 32 + 3, - 18, 21, _EN }, - /* 12 AN+CS */ { 96 + 1, 96 + 2, 96 + 4, 5, 128 + 7, 96 + 15, 96 + 17, 128 + 7, 128 + 14, 128 + 7, 12, - 128 + 7, 96 + 3, 96 + 18, 96 + 21, _AN }, - /* 13 AL:EN/AN+CS */ { 96 + 1, 96 + 2, 6, 6, 128 + 8, 96 + 16, 96 + 17, 128 + 8, 128 + 8, 128 + 8, 13, - 128 + 8, 96 + 3, 18, 21, _AN }, - /* 14 ON+ET */ { 32 + 1, 32 + 2, 128 + 4, 32 + 5, 7, 32 + 15, 32 + 17, 7, 14, 7, 14, 14, 32 + 3, 128 + 18, - 128 + 21, _ON }, - /* 15 S */ { 32 + 1, 32 + 2, 32 + 4, 32 + 5, 32 + 7, 15, 32 + 17, 32 + 7, 32 + 9, 32 + 7, 15, 32 + 7, - 32 + 3, 32 + 18, 32 + 21, _S }, - /* 16 AL:S */ { 32 + 1, 32 + 2, 32 + 6, 32 + 6, 32 + 8, 16, 32 + 17, 32 + 8, 32 + 8, 32 + 8, 16, 32 + 8, - 32 + 3, 32 + 18, 32 + 21, _S }, - /* 17 B */ { 32 + 1, 32 + 2, 32 + 4, 32 + 5, 32 + 7, 32 + 15, 17, 32 + 7, 32 + 9, 32 + 7, 17, 32 + 7, - 32 + 3, 32 + 18, 32 + 21, _B }, - /* 18 ENL */ { 32 + 1, 32 + 2, 18, 32 + 5, 32 + 7, 32 + 15, 32 + 17, 64 + 19, 20, 64 + 19, 18, 18, 32 + 3, - 18, 21, _L }, - /* 19 ENL+ES/CS */ { 96 + 1, 96 + 2, 18, 96 + 5, 128 + 7, 96 + 15, 96 + 17, 128 + 7, 128 + 14, 128 + 7, 19, - 128 + 7, 96 + 3, 18, 21, _L }, - /* 20 ENL+ET */ { 32 + 1, 32 + 2, 18, 32 + 5, 32 + 7, 32 + 15, 32 + 17, 32 + 7, 20, 32 + 7, 20, 20, 32 + 3, - 18, 21, _L }, - /* 21 ENR */ { 32 + 1, 32 + 2, 21, 32 + 5, 32 + 7, 32 + 15, 32 + 17, 64 + 22, 23, 64 + 22, 21, 21, 32 + 3, - 18, 21, _AN }, - /* 22 ENR+ES/CS */ { 96 + 1, 96 + 2, 21, 96 + 5, 128 + 7, 96 + 15, 96 + 17, 128 + 7, 128 + 14, 128 + 7, 22, - 128 + 7, 96 + 3, 18, 21, _AN }, - /* 23 ENR+ET */ { 32 + 1, 32 + 2, 21, 32 + 5, 32 + 7, 32 + 15, 32 + 17, 32 + 7, 23, 32 + 7, 23, 23, 32 + 3, - 18, 21, _AN } }; - - /*********************************************************************/ - /* 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 "_(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 */ - /*********************************************************************/ - private static final int IMPTABLEVELS_COLUMNS = _B + 2; - private static final int IMPTABLEVELS_RES = IMPTABLEVELS_COLUMNS - 1; - - private static short GetState(byte cell) { - return (short) (cell & 0x0f); - } - - private static short GetAction(byte cell) { - return (short) (cell >> 4); - } - - private static class ImpTabPair { - byte[][][] imptab; - short[][] impact; - - ImpTabPair(byte[][] table1, byte[][] table2, short[] act1, short[] act2) { - imptab = new byte[][][] { table1, table2 }; - impact = new short[][] { act1, act2 }; - } - } - - /*********************************************************************/ - /* */ - /* 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(). */ - /* */ - - private static final byte 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, 0x14, 0x14, 0, 1 }, - /* 2 : AN */ { 0, 1, 0, 2, 0x15, 0x15, 0, 2 }, /* 3 : R+EN/AN */ { 0, 1, 3, 3, 0x14, 0x14, 0, 2 }, - /* 4 : R+ON */ { 0, 0x21, 0x33, 0x33, 4, 4, 0, 0 }, - /* 5 : AN+ON */ { 0, 0x21, 0, 0x32, 5, 5, 0, 0 } }; - - private static final byte 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, 0x14, 0x14, 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 */ { 0x21, 0, 0x21, 3, 4, 4, 0, 0 }, /* 5 : L+AN+ON */ { 1, 0, 1, 3, 5, 5, 0, 0 } }; - - private static final short[] impAct0 = { 0, 1, 2, 3, 4 }; - - private static final ImpTabPair impTab_DEFAULT = new ImpTabPair(impTabL_DEFAULT, impTabR_DEFAULT, impAct0, impAct0); - - private static final byte 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, 0x11, 0x11, 0, 0, 0, 0 }, /* 1 : L+EN/AN */ { 0, 0x42, 1, 1, 0, 0, 0, 0 }, - /* 2 : R */ { 0, 2, 4, 4, 0x13, 0x13, 0, 1 }, /* 3 : R+ON */ { 0, 0x22, 0x34, 0x34, 3, 3, 0, 0 }, - /* 4 : R+EN/AN */ { 0, 2, 4, 4, 0x13, 0x13, 0, 2 } }; - private static final ImpTabPair impTab_NUMBERS_SPECIAL = new ImpTabPair(impTabL_NUMBERS_SPECIAL, impTabR_DEFAULT, - impAct0, impAct0); - - private static final byte 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, 0x11, 0x11, 0, 0, 0, 0 }, /* 1 EN/AN */ { 0x20, 3, 1, 1, 2, 0x20, 0x20, 2 }, - /* 2 EN/AN+ON */ { 0x20, 3, 1, 1, 2, 0x20, 0x20, 1 }, /* 3 R */ { 0, 3, 5, 5, 0x14, 0, 0, 1 }, - /* 4 R+ON */ { 0x20, 3, 5, 5, 4, 0x20, 0x20, 1 }, /* 5 R+EN/AN */ { 0, 3, 5, 5, 0x14, 0, 0, 2 } }; - private static final byte 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, 0x14, 0x14, 0x13, 0, 0, 1 }, /* 3 L+ON */ { 0x22, 0, 4, 4, 3, 0, 0, 0 }, - /* 4 L+EN/AN */ { 0x22, 0, 4, 4, 3, 0, 0, 1 } }; - private static final ImpTabPair impTab_GROUP_NUMBERS_WITH_R = new ImpTabPair(impTabL_GROUP_NUMBERS_WITH_R, - impTabR_GROUP_NUMBERS_WITH_R, impAct0, impAct0); - - private static final byte 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, 0x14, 0x14, 0, 1 }, - /* 2 : AN */ { 0, 1, 0, 0, 0x15, 0x15, 0, 2 }, /* 3 : R+EN/AN */ { 0, 1, 0, 0, 0x14, 0x14, 0, 2 }, - /* 4 : R+ON */ { 0x20, 1, 0x20, 0x20, 4, 4, 0x20, 1 }, - /* 5 : AN+ON */ { 0x20, 1, 0x20, 0x20, 5, 5, 0x20, 1 } }; - private static final byte 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, 0x14, 0x14, 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 */ { 0x21, 0, 0x21, 0x21, 4, 4, 0, 0 }, /* 5 : L+AN+ON */ { 1, 0, 1, 1, 5, 5, 0, 0 } }; - private static final ImpTabPair impTab_INVERSE_NUMBERS_AS_L = new ImpTabPair(impTabL_INVERSE_NUMBERS_AS_L, - impTabR_INVERSE_NUMBERS_AS_L, impAct0, impAct0); - - private static final byte 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, 0x13, 0x13, 0, 1 }, - /* 2 : EN/AN */ { 1, 0, 2, 2, 0, 0, 0, 1 }, /* 3 : L+ON */ { 0x21, 0x30, 6, 4, 3, 3, 0x30, 0 }, - /* 4 : L+ON+AN */ { 0x21, 0x30, 6, 4, 5, 5, 0x30, 3 }, - /* 5 : L+AN+ON */ { 0x21, 0x30, 6, 4, 5, 5, 0x30, 2 }, - /* 6 : L+ON+EN */ { 0x21, 0x30, 6, 4, 3, 3, 0x30, 1 } }; - private static final short[] impAct1 = { 0, 1, 13, 14 }; - private static final ImpTabPair impTab_INVERSE_LIKE_DIRECT = new ImpTabPair(impTabL_DEFAULT, - impTabR_INVERSE_LIKE_DIRECT, impAct0, impAct1); - - private static final byte 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, 0x63, 0, 1, 0, 0, 0, 0 }, /* 1 : L+AN */ { 0, 0x63, 0, 1, 0x12, 0x30, 0, 4 }, - /* 2 : L+AN+ON */ { 0x20, 0x63, 0x20, 1, 2, 0x30, 0x20, 3 }, - /* 3 : R */ { 0, 0x63, 0x55, 0x56, 0x14, 0x30, 0, 3 }, - /* 4 : R+ON */ { 0x30, 0x43, 0x55, 0x56, 4, 0x30, 0x30, 3 }, - /* 5 : R+EN */ { 0x30, 0x43, 5, 0x56, 0x14, 0x30, 0x30, 4 }, - /* 6 : R+AN */ { 0x30, 0x43, 0x55, 6, 0x14, 0x30, 0x30, 4 } }; - private static final byte 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 */ { 0x13, 0, 1, 1, 0, 0, 0, 0 }, /* 1 : R+EN/AN */ { 0x23, 0, 1, 1, 2, 0x40, 0, 1 }, - /* 2 : R+EN/AN+ON */ { 0x23, 0, 1, 1, 2, 0x40, 0, 0 }, /* 3 : L */ { 3, 0, 3, 0x36, 0x14, 0x40, 0, 1 }, - /* 4 : L+ON */ { 0x53, 0x40, 5, 0x36, 4, 0x40, 0x40, 0 }, - /* 5 : L+ON+EN */ { 0x53, 0x40, 5, 0x36, 4, 0x40, 0x40, 1 }, - /* 6 : L+AN */ { 0x53, 0x40, 6, 6, 4, 0x40, 0x40, 3 } }; - private static final short[] impAct2 = { 0, 1, 2, 5, 6, 7, 8 }; - private static final short[] impAct3 = { 0, 1, 9, 10, 11, 12 }; - private static final ImpTabPair impTab_INVERSE_LIKE_DIRECT_WITH_MARKS = new ImpTabPair( - impTabL_INVERSE_LIKE_DIRECT_WITH_MARKS, impTabR_INVERSE_LIKE_DIRECT_WITH_MARKS, impAct2, impAct3); - - private static final ImpTabPair impTab_INVERSE_FOR_NUMBERS_SPECIAL = new ImpTabPair(impTabL_NUMBERS_SPECIAL, - impTabR_INVERSE_LIKE_DIRECT, impAct0, impAct1); - - private static final byte 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, 0x62, 1, 1, 0, 0, 0, 0 }, /* 1 : L+EN/AN */ { 0, 0x62, 1, 1, 0, 0x30, 0, 4 }, - /* 2 : R */ { 0, 0x62, 0x54, 0x54, 0x13, 0x30, 0, 3 }, - /* 3 : R+ON */ { 0x30, 0x42, 0x54, 0x54, 3, 0x30, 0x30, 3 }, - /* 4 : R+EN/AN */ { 0x30, 0x42, 4, 4, 0x13, 0x30, 0x30, 4 } }; - private static final ImpTabPair impTab_INVERSE_FOR_NUMBERS_SPECIAL_WITH_MARKS = new ImpTabPair( - impTabL_INVERSE_FOR_NUMBERS_SPECIAL_WITH_MARKS, impTabR_INVERSE_LIKE_DIRECT_WITH_MARKS, impAct2, impAct3); - - private static class LevState { - byte[][] impTab; /* level table pointer */ - short[] impAct; /* action map array */ - int startON; /* start of ON sequence */ - int startL2EN; /* start of level 2 sequence */ - int lastStrongRTL; /* index of last found R or AL */ - int runStart; /* start position of the run */ - short state; /* current state */ - byte runLevel; /* run level before implicit solving */ - } - - /*------------------------------------------------------------------------*/ - - static final int FIRSTALLOC = 10; - - /* - * param pos: position where to insert param flag: one of LRM_BEFORE, LRM_AFTER, - * RLM_BEFORE, RLM_AFTER - */ - private void addPoint(int pos, int flag) { - Point point = new Point(); - - int len = insertPoints.points.length; - if (len == 0) { - insertPoints.points = new Point[FIRSTALLOC]; - len = FIRSTALLOC; - } - if (insertPoints.size >= len) { /* no room for new point */ - Point[] savePoints = insertPoints.points; - insertPoints.points = new Point[len * 2]; - System.arraycopy(savePoints, 0, insertPoints.points, 0, len); - } - point.pos = pos; - point.flag = flag; - insertPoints.points[insertPoints.size] = point; - insertPoints.size++; - } - - private void setLevelsOutsideIsolates(int start, int limit, byte level) { - byte dirProp; - int isolateCount = 0, k; - for (k = start; k < limit; k++) { - dirProp = dirProps[k]; - if (dirProp == PDI) - isolateCount--; - if (isolateCount == 0) { - levels[k] = level; - } - if (dirProp == LRI || dirProp == RLI) - isolateCount++; - } - } - - /* perform rules (Wn), (Nn), and (In) on a run of the text ------------------ */ - - /* - * This implementation of the (Wn) rules applies all rules in one pass. In order - * to do so, it needs a look-ahead of typically 1 character (except for W5: - * sequences of ET) and keeps track of changes in a rule Wp that affect a later - * Wq (p= 0) { - addPoint(levState.startL2EN, LRM_BEFORE); - } - levState.startL2EN = -1; /* not within previous if since could also be -2 */ - /* check if we had any relevant EN/AN after R/AL */ - if ((insertPoints.points.length == 0) || (insertPoints.size <= insertPoints.confirmed)) { - /* nothing, just clean up */ - levState.lastStrongRTL = -1; - /* check if we have a pending conditional segment */ - level = impTab[oldStateSeq][IMPTABLEVELS_RES]; - if ((level & 1) != 0 && levState.startON > 0) { /* after ON */ - start = levState.startON; /* reset to basic run level */ - } - if (_prop == _S) { /* add LRM before S */ - addPoint(start0, LRM_BEFORE); - insertPoints.confirmed = insertPoints.size; - } - break; - } - /* reset previous RTL cont to level for LTR text */ - for (k = levState.lastStrongRTL + 1; k < start0; k++) { - /* reset odd level, leave runLevel+2 as is */ - levels[k] = (byte) ((levels[k] - 2) & ~1); - } - /* mark insert points as confirmed */ - insertPoints.confirmed = insertPoints.size; - levState.lastStrongRTL = -1; - if (_prop == _S) { /* add LRM before S */ - addPoint(start0, LRM_BEFORE); - insertPoints.confirmed = insertPoints.size; - } - break; - - case 6: /* R/AL after possible relevant EN/AN */ - /* just clean up */ - if (insertPoints.points.length > 0) - /* remove all non confirmed insert points */ - insertPoints.size = insertPoints.confirmed; - levState.startON = -1; - levState.startL2EN = -1; - levState.lastStrongRTL = limit - 1; - break; - - case 7: /* EN/AN after R/AL + possible cont */ - /* check for real AN */ - - if ((_prop == _AN) && (dirProps[start0] == AN) - && (reorderingMode != REORDER_INVERSE_FOR_NUMBERS_SPECIAL)) { - /* real AN */ - if (levState.startL2EN == -1) { /* if no relevant EN already found */ - /* just note the rightmost digit as a strong RTL */ - levState.lastStrongRTL = limit - 1; - break; - } - if (levState.startL2EN >= 0) { /* after EN, no AN */ - addPoint(levState.startL2EN, LRM_BEFORE); - levState.startL2EN = -2; - } - /* note AN */ - addPoint(start0, LRM_BEFORE); - break; - } - /* if first EN/AN after R/AL */ - if (levState.startL2EN == -1) { - levState.startL2EN = start0; - } - break; - - case 8: /* note location of latest R/AL */ - levState.lastStrongRTL = limit - 1; - levState.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) == 0); k--) { - } - if (k >= 0) { - addPoint(k, RLM_BEFORE); /* add RLM before */ - insertPoints.confirmed = insertPoints.size; /* confirm it */ - } - levState.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(start0, LRM_BEFORE); /* add LRM before */ - addPoint(start0, LRM_AFTER); /* add LRM after */ - break; - - case 11: /* R after L+ON/EN/AN */ - /* false alert, infirm LRMs around previous AN */ - insertPoints.size = insertPoints.confirmed; - if (_prop == _S) { /* add RLM before S */ - addPoint(start0, RLM_BEFORE); - insertPoints.confirmed = insertPoints.size; - } - break; - - case 12: /* L after L+ON/AN */ - level = (byte) (levState.runLevel + addLevel); - for (k = levState.startON; k < start0; k++) { - if (levels[k] < level) { - levels[k] = level; - } - } - insertPoints.confirmed = insertPoints.size; /* confirm inserts */ - levState.startON = start0; - break; - - case 13: /* L after L+ON+EN/AN/ON */ - level = levState.runLevel; - for (k = start0 - 1; k >= levState.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] = (byte) (level + 1); - } - break; - - case 14: /* R after L+ON+EN/AN/ON */ - level = (byte) (levState.runLevel + 1); - for (k = start0 - 1; k >= levState.startON; k--) { - if (levels[k] > level) { - levels[k] -= 2; - } - } - break; - - default: /* we should never get here */ - throw new IllegalStateException("Internal ICU error in processPropertySeq"); - } - } - if ((addLevel) != 0 || (start < start0)) { - level = (byte) (levState.runLevel + addLevel); - if (start >= levState.runStart) { - for (k = start; k < limit; k++) { - levels[k] = level; - } - } else { - setLevelsOutsideIsolates(start, limit, level); - } - } - } - - private void resolveImplicitLevels(int start, int limit, short sor, short eor) { - byte dirProp; - LevState levState = new LevState(); - int i, start1, start2; - short oldStateImp, stateImp, actionImp; - short gprop, resProp, cell; - boolean inverseRTL; - short nextStrongProp = R; - int 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 = ((start < lastArabicPos) && ((GetParaLevelAt(start) & 1) > 0) - && (reorderingMode == REORDER_INVERSE_LIKE_DIRECT - || reorderingMode == REORDER_INVERSE_FOR_NUMBERS_SPECIAL)); - /* initialize for property and levels state table */ - 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 = levels[start]; - levState.impTab = impTabPair.imptab[levState.runLevel & 1]; - levState.impAct = impTabPair.impact[levState.runLevel & 1]; - - /* - * 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) { - levState.startON = isolates[isolateCount].startON; - start1 = isolates[isolateCount].start1; - stateImp = isolates[isolateCount].stateImp; - levState.state = isolates[isolateCount].state; - isolateCount--; - } else { - levState.startON = -1; - start1 = start; - if (dirProps[start] == NSM) - stateImp = (short) (1 + sor); - else - stateImp = 0; - levState.state = 0; - processPropertySeq(levState, sor, start, start); - } - start2 = start; /* to make the Java compiler happy */ - - for (i = start; i <= limit; i++) { - if (i >= limit) { - int k; - for (k = limit - 1; k > start && (DirPropFlag(dirProps[k]) & MASK_BN_EXPLICIT) != 0; k--) - ; - dirProp = dirProps[k]; - if (dirProp == LRI || dirProp == RLI) - break; /* no forced closing for sequence ending with LRI/RLI */ - gprop = eor; - } else { - byte prop, prop1; - prop = dirProps[i]; - if (prop == B) - 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) */ - int j; - nextStrongProp = R; /* set default */ - nextStrongPos = limit; - for (j = i + 1; j < limit; j++) { - prop1 = dirProps[j]; - if (prop1 == L || prop1 == R || prop1 == AL) { - nextStrongProp = prop1; - nextStrongPos = j; - break; - } - } - } - if (nextStrongProp == AL) { - prop = AN; - } - } - } - gprop = groupProp[prop]; - } - oldStateImp = stateImp; - cell = impTabProps[oldStateImp][gprop]; - stateImp = GetStateProps(cell); /* isolate the new state */ - actionImp = GetActionProps(cell); /* isolate the action */ - if ((i == limit) && (actionImp == 0)) { - /* there is an unprocessed sequence if its property == eor */ - actionImp = 1; /* process the last sequence */ - } - if (actionImp != 0) { - resProp = impTabProps[oldStateImp][IMPTABPROPS_RES]; - switch (actionImp) { - case 1: /* process current seq1, init new seq1 */ - processPropertySeq(levState, resProp, start1, i); - start1 = i; - break; - case 2: /* init new seq2 */ - start2 = i; - break; - case 3: /* process seq1, process seq2, init new seq1 */ - processPropertySeq(levState, resProp, start1, start2); - processPropertySeq(levState, _ON, start2, i); - start1 = i; - break; - case 4: /* process seq1, set seq1=seq2, init new seq2 */ - processPropertySeq(levState, resProp, start1, start2); - start1 = start2; - start2 = i; - break; - default: /* we should never get here */ - throw new IllegalStateException("Internal ICU error in resolveImplicitLevels"); - } - } - } - - /* look for the last char not a BN or LRE/RLE/LRO/RLO/PDF */ - for (i = limit - 1; i > start && (DirPropFlag(dirProps[i]) & MASK_BN_EXPLICIT) != 0; i--) - ; - dirProp = dirProps[i]; - if ((dirProp == LRI || dirProp == RLI) && limit < length) { - isolateCount++; - if (isolates[isolateCount] == null) - isolates[isolateCount] = new Isolate(); - isolates[isolateCount].stateImp = stateImp; - isolates[isolateCount].state = levState.state; - isolates[isolateCount].start1 = start1; - isolates[isolateCount].startON = levState.startON; - } else - processPropertySeq(levState, eor, limit, limit); - } - - /* perform (L1) and (X9) ---------------------------------------------------- */ - - /* - * Reset the embedding levels for some non-graphic characters (L1). This method - * also sets appropriate levels for BN, and explicit embedding types that are - * supposed to have been removed from the paragraph in (X9). - */ - private void adjustWSLevels() { - int i; - - if ((flags & MASK_WS) != 0) { - int flag; - i = trailingWSStart; - while (i > 0) { - /* reset a sequence of WS/BN before eop and B/S to the paragraph paraLevel */ - while (i > 0 && ((flag = DirPropFlag(dirProps[--i])) & MASK_WS) != 0) { - if (orderParagraphsLTR && (flag & DirPropFlag(B)) != 0) { - levels[i] = 0; - } else { - levels[i] = GetParaLevelAt(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 = DirPropFlag(dirProps[--i]); - if ((flag & MASK_BN_EXPLICIT) != 0) { - levels[i] = levels[i + 1]; - } else if (orderParagraphsLTR && (flag & DirPropFlag(B)) != 0) { - levels[i] = 0; - break; - } else if ((flag & MASK_B_S) != 0) { - levels[i] = GetParaLevelAt(i); - break; - } - } - } - } - } - - private void setParaSuccess() { - paraBidi = this; /* mark successful setPara */ - } - - private int Bidi_Min(int x, int y) { - return x < y ? x : y; - } - - private int Bidi_Abs(int x) { - return x >= 0 ? x : -x; - } - - void setParaRunsOnly(char[] parmText, byte parmParaLevel) { - int[] visualMap; - String visualText; - int saveLength, saveTrailingWSStart; - byte[] saveLevels; - byte saveDirection; - int i, j, visualStart, logicalStart, oldRunCount, runLength, addedRuns, insertRemove, start, limit, step, - indexOddBit, logicalPos, index, index1; - int saveOptions; - - reorderingMode = REORDER_DEFAULT; - int parmLength = parmText.length; - if (parmLength == 0) { - setPara(parmText, parmParaLevel, null); - reorderingMode = REORDER_RUNS_ONLY; - return; - } - /* obtain memory for mapping table and visual text */ - saveOptions = reorderingOptions; - if ((saveOptions & OPTION_INSERT_MARKS) > 0) { - reorderingOptions &= ~OPTION_INSERT_MARKS; - reorderingOptions |= OPTION_REMOVE_CONTROLS; - } - parmParaLevel &= 1; /* accept only 0 or 1 */ - setPara(parmText, parmParaLevel, null); - /* - * we cannot access directly levels since it is not yet set if direction is not - * MIXED - */ - saveLevels = new byte[this.length]; - System.arraycopy(getLevels(), 0, saveLevels, 0, this.length); - saveTrailingWSStart = trailingWSStart; - - /* - * 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 setPara (but must make - * provision for possible removal of Bidi controls. Alternatively, only use the - * dirProps array via customized classifier callback. - */ - visualText = writeReordered(DO_MIRRORING); - visualMap = getVisualMap(); - this.reorderingOptions = saveOptions; - saveLength = this.length; - saveDirection = this.direction; - - this.reorderingMode = REORDER_INVERSE_LIKE_DIRECT; - parmParaLevel ^= 1; - setPara(visualText, parmParaLevel, null); - BidiLine.getRuns(this); - /* check if some runs must be split, count how many splits */ - addedRuns = 0; - oldRunCount = this.runCount; - visualStart = 0; - for (i = 0; i < oldRunCount; i++, visualStart += runLength) { - runLength = runs[i].limit - visualStart; - if (runLength < 2) { - continue; - } - logicalStart = runs[i].start; - for (j = logicalStart + 1; j < logicalStart + runLength; j++) { - index = visualMap[j]; - index1 = visualMap[j - 1]; - if ((Bidi_Abs(index - index1) != 1) || (saveLevels[index] != saveLevels[index1])) { - addedRuns++; - } - } - } - if (addedRuns > 0) { - getRunsMemory(oldRunCount + addedRuns); - if (runCount == 1) { - /* because we switch from UBiDi.simpleRuns to UBiDi.runs */ - runsMemory[0] = runs[0]; - } else { - System.arraycopy(runs, 0, runsMemory, 0, runCount); - } - runs = runsMemory; - runCount += addedRuns; - for (i = oldRunCount; i < runCount; i++) { - if (runs[i] == null) { - runs[i] = new BidiRun(0, 0, (byte) 0); - } - } - } - /* split runs which are not consecutive in source text */ - int newI; - for (i = oldRunCount - 1; i >= 0; i--) { - newI = i + addedRuns; - runLength = i == 0 ? runs[0].limit : runs[i].limit - runs[i - 1].limit; - logicalStart = runs[i].start; - indexOddBit = runs[i].level & 1; - if (runLength < 2) { - if (addedRuns > 0) { - runs[newI].copyFrom(runs[i]); - } - logicalPos = visualMap[logicalStart]; - runs[newI].start = logicalPos; - runs[newI].level = (byte) (saveLevels[logicalPos] ^ indexOddBit); - continue; - } - if (indexOddBit > 0) { - start = logicalStart; - limit = logicalStart + runLength - 1; - step = 1; - } else { - start = logicalStart + runLength - 1; - limit = logicalStart; - step = -1; - } - for (j = start; j != limit; j += step) { - index = visualMap[j]; - index1 = visualMap[j + step]; - if ((Bidi_Abs(index - index1) != 1) || (saveLevels[index] != saveLevels[index1])) { - logicalPos = Bidi_Min(visualMap[start], index); - runs[newI].start = logicalPos; - runs[newI].level = (byte) (saveLevels[logicalPos] ^ indexOddBit); - runs[newI].limit = runs[i].limit; - runs[i].limit -= Bidi_Abs(j - start) + 1; - insertRemove = runs[i].insertRemove & (LRM_AFTER | RLM_AFTER); - runs[newI].insertRemove = insertRemove; - runs[i].insertRemove &= ~insertRemove; - start = j + step; - addedRuns--; - newI--; - } - } - if (addedRuns > 0) { - runs[newI].copyFrom(runs[i]); - } - logicalPos = Bidi_Min(visualMap[start], visualMap[limit]); - runs[newI].start = logicalPos; - runs[newI].level = (byte) (saveLevels[logicalPos] ^ indexOddBit); - } - - cleanup1: - /* restore initial paraLevel */ - this.paraLevel ^= 1; - cleanup2: - /* restore real text */ - this.text = parmText; - this.length = saveLength; - this.originalLength = parmLength; - this.direction = saveDirection; - this.levels = saveLevels; - this.trailingWSStart = saveTrailingWSStart; - if (runCount > 1) { - this.direction = MIXED; - } - cleanup3: this.reorderingMode = REORDER_RUNS_ONLY; - } - - /** - * Perform the Unicode Bidi algorithm. It is defined in the - * Unicode Standard Annex #9: - * Unicode Bidirectional Algorithm, version 13, also described in The - * Unicode Standard, Version 4.0 . - *

- * - * This method 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 method 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. - * - * Although the text is passed here as a String, it is stored - * internally as an array of characters. Therefore the documentation will refer - * to indexes of the characters in the text. - * - * @param text contains the text that the Bidi algorithm will be - * performed on. This text can be retrieved with - * getText() or - * getTextAsString.
- * - * @param paraLevel specifies the default level for the text; it is - * typically 0 (LTR) or 1 (RTL). If the method shall - * determine the paragraph level from the text, then - * paraLevel can be set to either - * LEVEL_DEFAULT_LTR or - * LEVEL_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 - * 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 LEVEL_OVERRIDE bit set.
- *
- * Except for that bit, it must be - * paraLevel<=embeddingLevels[]<=MAX_EXPLICIT_LEVEL, - * with one exception: a level of zero may be specified - * for a paragraph separator even if - * paraLevel>0 when multiple paragraphs - * are submitted in the same call to - * setPara().
- *
- * Caution: A reference to this array, - * not a copy of the levels, will be stored in the - * Bidi object; the - * embeddingLevels should not be modified to - * avoid unexpected results on subsequent Bidi - * operations. However, the setPara() and - * setLine() methods may modify some or all - * of the levels.
- *
- * Note: the - * embeddingLevels array must have one entry - * for each character in text. - * - * @throws IllegalArgumentException if the values in embeddingLevels are not - * within the allowed range - * - * @see #LEVEL_DEFAULT_LTR - * @see #LEVEL_DEFAULT_RTL - * @see #LEVEL_OVERRIDE - * @see #MAX_EXPLICIT_LEVEL - * @stable ICU 3.8 - */ - void setPara(String text, byte paraLevel, byte[] embeddingLevels) { - if (text == null) { - setPara(new char[0], paraLevel, embeddingLevels); - } else { - setPara(text.toCharArray(), paraLevel, embeddingLevels); - } - } - - /** - * Perform the Unicode Bidi algorithm. It is defined in the - * Unicode Standard Annex #9: - * Unicode Bidirectional Algorithm, version 13, also described in The - * Unicode Standard, Version 4.0 . - *

- * - * This method 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 method 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. - * - * The text is stored internally as an array of characters. Therefore the - * documentation will refer to indexes of the characters in the text. - * - * @param chars contains the text that the Bidi algorithm will be - * performed on. This text can be retrieved with - * getText() or - * getTextAsString.
- * - * @param paraLevel specifies the default level for the text; it is - * typically 0 (LTR) or 1 (RTL). If the method shall - * determine the paragraph level from the text, then - * paraLevel can be set to either - * LEVEL_DEFAULT_LTR or - * LEVEL_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 - * 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 LEVEL_OVERRIDE bit set.
- *
- * Except for that bit, it must be - * paraLevel<=embeddingLevels[]<=MAX_EXPLICIT_LEVEL, - * with one exception: a level of zero may be specified - * for a paragraph separator even if - * paraLevel>0 when multiple paragraphs - * are submitted in the same call to - * setPara().
- *
- * Caution: A reference to this array, - * not a copy of the levels, will be stored in the - * Bidi object; the - * embeddingLevels should not be modified to - * avoid unexpected results on subsequent Bidi - * operations. However, the setPara() and - * setLine() methods may modify some or all - * of the levels.
- *
- * Note: the - * embeddingLevels array must have one entry - * for each character in text. - * - * @throws IllegalArgumentException if the values in embeddingLevels are not - * within the allowed range - * - * @see #LEVEL_DEFAULT_LTR - * @see #LEVEL_DEFAULT_RTL - * @see #LEVEL_OVERRIDE - * @see #MAX_EXPLICIT_LEVEL - * @stable ICU 3.8 - */ - void setPara(char[] chars, byte paraLevel, byte[] embeddingLevels) { - /* check the argument values */ - if (paraLevel < LEVEL_DEFAULT_LTR) { - verifyRange(paraLevel, 0, MAX_EXPLICIT_LEVEL + 1); - } - if (chars == null) { - chars = new char[0]; - } - - /* special treatment for RUNS_ONLY mode */ - if (reorderingMode == REORDER_RUNS_ONLY) { - setParaRunsOnly(chars, paraLevel); - return; - } - - /* initialize the Bidi object */ - this.paraBidi = null; /* mark unfinished setPara */ - this.text = chars; - this.length = this.originalLength = this.resultLength = text.length; - this.paraLevel = paraLevel; - this.direction = (byte) (paraLevel & 1); - this.paraCount = 1; - - /* - * Allocate zero-length arrays instead of setting to null here; then checks for - * null in various places can be eliminated. - */ - dirProps = new byte[0]; - levels = new byte[0]; - runs = new BidiRun[0]; - isGoodLogicalToVisualRunsMap = false; - insertPoints.size = 0; /* clean up from last call */ - insertPoints.confirmed = 0; /* clean up from last call */ - - /* - * Save the original paraLevel if contextual; otherwise, set to 0. - */ - defaultParaLevel = IsDefaultLevel(paraLevel) ? paraLevel : 0; - - if (length == 0) { - /* - * For an empty paragraph, create a Bidi object with the paraLevel and the flags - * and the direction set but without allocating zero-length arrays. There is - * nothing more to do. - */ - if (IsDefaultLevel(paraLevel)) { - this.paraLevel &= 1; - defaultParaLevel = 0; - } - flags = DirPropFlagLR(paraLevel); - runCount = 0; - paraCount = 0; - setParaSuccess(); - return; - } - - runCount = -1; - - /* - * Get the directional properties, the flags bit-set, and determine the - * paragraph level if necessary. - */ - getDirPropsMemory(length); - dirProps = dirPropsMemory; - getDirProps(); - /* the processed length may have changed if OPTION_STREAMING is set */ - 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 */ - getLevelsMemory(length); - levels = levelsMemory; - direction = resolveExplicitLevels(); - } else { - /* - * set BN for all explicit codes, check that all levels are 0 or - * paraLevel..MAX_EXPLICIT_LEVEL - */ - levels = embeddingLevels; - direction = checkExplicitLevels(); - } - - /* allocate isolate memory */ - if (isolateCount > 0) { - if (isolates == null || isolates.length < isolateCount) - isolates = new Isolate[isolateCount + 3]; /* keep some reserve */ - } - isolateCount = -1; /* current isolates stack entry == none */ - - /* - * The steps after (X9) in the Bidi algorithm are performed only if the - * paragraph text has mixed directionality! - */ - switch (direction) { - case LTR: - /* all levels are implicitly at paraLevel (important for getLevels()) */ - trailingWSStart = 0; - break; - case RTL: - /* all levels are implicitly at paraLevel (important for getLevels()) */ - trailingWSStart = 0; - break; - default: - /* - * Choose the right implicit state table - */ - switch (reorderingMode) { - case REORDER_DEFAULT: - this.impTabPair = impTab_DEFAULT; - break; - case REORDER_NUMBERS_SPECIAL: - this.impTabPair = impTab_NUMBERS_SPECIAL; - break; - case REORDER_GROUP_NUMBERS_WITH_R: - this.impTabPair = impTab_GROUP_NUMBERS_WITH_R; - break; - case REORDER_RUNS_ONLY: - /* we should never get here */ - throw new InternalError("Internal ICU error in setPara"); - /* break; */ - case REORDER_INVERSE_NUMBERS_AS_L: - this.impTabPair = impTab_INVERSE_NUMBERS_AS_L; - break; - case REORDER_INVERSE_LIKE_DIRECT: - if ((reorderingOptions & OPTION_INSERT_MARKS) != 0) { - this.impTabPair = impTab_INVERSE_LIKE_DIRECT_WITH_MARKS; - } else { - this.impTabPair = impTab_INVERSE_LIKE_DIRECT; - } - break; - case REORDER_INVERSE_FOR_NUMBERS_SPECIAL: - if ((reorderingOptions & OPTION_INSERT_MARKS) != 0) { - this.impTabPair = impTab_INVERSE_FOR_NUMBERS_SPECIAL_WITH_MARKS; - } else { - this.impTabPair = impTab_INVERSE_FOR_NUMBERS_SPECIAL; - } - break; - } - /* - * 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 && paraCount <= 1 && (flags & DirPropFlagMultiRuns) == 0) { - resolveImplicitLevels(0, length, GetLRFromLevel(GetParaLevelAt(0)), - GetLRFromLevel(GetParaLevelAt(length - 1))); - } else { - /* sor, eor: start and end types of same-level-run */ - int start, limit = 0; - byte level, nextLevel; - short sor, eor; - - /* - * determine the first sor and set eor to it because of the loop body (sor=eor - * there) - */ - level = GetParaLevelAt(0); - nextLevel = levels[0]; - if (level < nextLevel) { - eor = GetLRFromLevel(nextLevel); - } else { - eor = GetLRFromLevel(level); - } - - do { - /* determine start and limit of the run (end points just behind the run) */ - - /* the values for this run's start are the same as for the previous run's end */ - start = limit; - level = nextLevel; - if ((start > 0) && (dirProps[start - 1] == B)) { - /* except if this is a new paragraph, then set sor = para level */ - sor = GetLRFromLevel(GetParaLevelAt(start)); - } else { - sor = eor; - } - - /* search for the limit of this run */ - while ((++limit < length) - && ((levels[limit] == level) || ((DirPropFlag(dirProps[limit]) & MASK_BN_EXPLICIT) != 0))) { - } - - /* get the correct level of the next run */ - if (limit < length) { - nextLevel = levels[limit]; - } else { - nextLevel = GetParaLevelAt(length - 1); - } - - /* determine eor from max(level, nextLevel); sor is last run's eor */ - if (NoOverride(level) < NoOverride(nextLevel)) { - eor = GetLRFromLevel(nextLevel); - } else { - eor = GetLRFromLevel(level); - } - - /* - * if the run consists of overridden directional types, then there are no - * implicit types to be resolved - */ - if ((level & LEVEL_OVERRIDE) == 0) { - resolveImplicitLevels(start, limit, sor, eor); - } else { - /* remove the LEVEL_OVERRIDE flags */ - do { - levels[start++] &= ~LEVEL_OVERRIDE; - } while (start < limit); - } - } while (limit < length); - } - - /* reset the embedding levels for some non-graphic characters (L1), (X9) */ - adjustWSLevels(); - - break; - } - - /* - * add RLM for inverse Bidi with contextual orientation resolving to RTL which - * would not round-trip otherwise - */ - if ((defaultParaLevel > 0) && ((reorderingOptions & OPTION_INSERT_MARKS) != 0) - && ((reorderingMode == REORDER_INVERSE_LIKE_DIRECT) - || (reorderingMode == REORDER_INVERSE_FOR_NUMBERS_SPECIAL))) { - int start, last; - byte level; - byte dirProp; - for (int i = 0; i < paraCount; i++) { - last = paras_limit[i] - 1; - level = paras_level[i]; - if (level == 0) - continue; /* LTR paragraph */ - start = i == 0 ? 0 : paras_limit[i - 1]; - for (int j = last; j >= start; j--) { - dirProp = dirProps[j]; - if (dirProp == L) { - if (j < last) { - while (dirProps[last] == B) { - last--; - } - } - addPoint(last, RLM_BEFORE); - break; - } - if ((DirPropFlag(dirProp) & MASK_R_AL) != 0) { - break; - } - } - } - } - - if ((reorderingOptions & OPTION_REMOVE_CONTROLS) != 0) { - resultLength -= controlCount; - } else { - resultLength += insertPoints.size; - } - setParaSuccess(); - } - - /** - * Perform the Unicode Bidi algorithm on a given paragraph, as defined in the - * Unicode Standard Annex #9: - * Unicode Bidirectional Algorithm, version 13, also described in The - * Unicode Standard, Version 4.0 . - *

- * - * This method takes a paragraph of text and computes the - * left-right-directionality of each character. The text should not contain any - * Unicode block separators. - *

- * - * The RUN_DIRECTION attribute in the text, if present, determines the base - * direction (left-to-right or right-to-left). If not present, the base - * direction is computed using the Unicode Bidirectional Algorithm, defaulting - * to left-to-right if there are no strong directional characters in the text. - * This attribute, if present, must be applied to all the text in the paragraph. - *

- * - * The BIDI_EMBEDDING attribute in the text, if present, represents embedding - * level information. Negative values from -1 to -62 indicate overrides at the - * absolute value of the level. Positive values from 1 to 62 indicate - * embeddings. Where values are zero or not defined, the base embedding level as - * determined by the base direction is assumed. - *

- * - * The NUMERIC_SHAPING attribute in the text, if present, converts European - * digits to other decimal digits before running the bidi algorithm. This - * attribute, if present, must be applied to all the text in the paragraph. - * - * If the entire text is all of the same directionality, then the method 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. - *

- * - * @param paragraph a paragraph of text with optional character and paragraph - * attribute information - * @stable ICU 3.8 - */ - public void setPara(AttributedCharacterIterator paragraph) { - byte paraLvl; - char ch = paragraph.first(); - Boolean runDirection = (Boolean) paragraph.getAttribute(TextAttribute.RUN_DIRECTION); - Object shaper = paragraph.getAttribute(TextAttribute.NUMERIC_SHAPING); - - if (runDirection == null) { - paraLvl = LEVEL_DEFAULT_LTR; - } else { - paraLvl = (runDirection.equals(TextAttribute.RUN_DIRECTION_LTR)) ? LTR : RTL; - } - - byte[] lvls = null; - int len = paragraph.getEndIndex() - paragraph.getBeginIndex(); - byte[] embeddingLevels = new byte[len]; - char[] txt = new char[len]; - int i = 0; - while (ch != AttributedCharacterIterator.DONE) { - txt[i] = ch; - Integer embedding = (Integer) paragraph.getAttribute(TextAttribute.BIDI_EMBEDDING); - if (embedding != null) { - byte level = embedding.byteValue(); - if (level == 0) { - /* no-op */ - } else if (level < 0) { - lvls = embeddingLevels; - embeddingLevels[i] = (byte) ((0 - level) | LEVEL_OVERRIDE); - } else { - lvls = embeddingLevels; - embeddingLevels[i] = level; - } - } - ch = paragraph.next(); - ++i; - } - - if (shaper != null) { - ((NumericShaper) shaper).shape(txt, 0, len); - } - setPara(txt, paraLvl, lvls); - } - - /** - * Specify whether block separators must be allocated level zero, so that - * successive paragraphs will progress from left to right. This method must be - * called before 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 ordarParaLTR specifies whether paragraph separators (B) must receive - * level 0, so that successive paragraphs progress from left - * to right. - * - * @see #setPara - * @stable ICU 3.8 - */ - public void orderParagraphsLTR(boolean ordarParaLTR) { - orderParagraphsLTR = ordarParaLTR; - } - - /** - * Get the directionality of the text. - * - * @return a value of LTR, RTL or MIXED - * that indicates if the entire text represented by this object is - * unidirectional, and which direction, or if it is mixed-directional. - * - * @throws IllegalStateException if this call is not preceded by a successful - * call to setPara or - * setLine - * - * @see #LTR - * @see #RTL - * @see #MIXED - * @stable ICU 3.8 - */ - public byte getDirection() { - verifyValidParaOrLine(); - return direction; - } - - /** - * Get the length of the text. - * - * @return The length of the text that the Bidi object was created - * for. - * - * @throws IllegalStateException if this call is not preceded by a successful - * call to setPara or - * setLine - * @stable ICU 3.8 - */ - public int getLength() { - verifyValidParaOrLine(); - return originalLength; - } - - /* paragraphs API methods ------------------------------------------------- */ - - /** - * Get the paragraph level of the text. - * - * @return The paragraph level. If there are multiple paragraphs, their level - * may vary if the required paraLevel is LEVEL_DEFAULT_LTR or - * LEVEL_DEFAULT_RTL. In that case, the level of the first paragraph is - * returned. - * - * @throws IllegalStateException if this call is not preceded by a successful - * call to setPara or - * setLine - * - * @see #LEVEL_DEFAULT_LTR - * @see #LEVEL_DEFAULT_RTL - * @see #getParagraph - * @see #getParagraphByIndex - * @stable ICU 3.8 - */ - public byte getParaLevel() { - verifyValidParaOrLine(); - return paraLevel; - } - - /** - * Retrieves the Bidi class for a given code point. - *

- * If a BidiClassifier is defined and returns a value other than - * CLASS_DEFAULT, that value is used; otherwise the default class - * determination mechanism is invoked. - *

- * - * @param c The code point to get a Bidi class for. - * - * @return The Bidi class for the character c that is in effect for - * this Bidi instance. - * - * @stable ICU 3.8 - */ - public int getCustomizedClass(int c) { - int dir; - - dir = bdp.getClass(c); - if (dir >= CHAR_DIRECTION_COUNT) - dir = ON; - return dir; - } - - /** - * setLine() returns a Bidi object 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 Bidi 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 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 Bidi object that represents a - * line. - *

- * - * Important: the line Bidi object may reference - * data within the global text Bidi object. You should not alter - * the content of the global text object until you are finished using the line - * object. - * - * @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). - * - * @return a Bidi object that will now represent a line of the - * text. - * - * @throws IllegalStateException if this call is not preceded by a successful - * call to setPara - * @throws IllegalArgumentException if start and limit are not in the range - * 0<=start<limit<=getProcessedLength(), - * or if the specified line crosses a paragraph - * boundary - * - * @see #setPara - * @see #getProcessedLength - * @stable ICU 3.8 - */ - public Bidi setLine(Bidi bidi, BidiBase bidiBase, Bidi newBidi, BidiBase newBidiBase, int start, int limit) { - verifyValidPara(); - verifyRange(start, 0, limit); - verifyRange(limit, 0, length + 1); - - return BidiLine.setLine(this, newBidi, newBidiBase, start, limit); - } - - /** - * Get the level for one character. - * - * @param charIndex the index of a character. - * - * @return The level for the character at charIndex. - * - * @throws IllegalStateException if this call is not preceded by a successful - * call to setPara or - * setLine - * @throws IllegalArgumentException if charIndex is not in the range - * 0<=charIndex<getProcessedLength() - * - * @see #getProcessedLength - * @stable ICU 3.8 - */ - public byte getLevelAt(int charIndex) { - // for backward compatibility - if (charIndex < 0 || charIndex >= length) { - return (byte) getBaseLevel(); - } - - verifyValidParaOrLine(); - verifyRange(charIndex, 0, length); - return BidiLine.getLevelAt(this, charIndex); - } - - /** - * Get an array of levels for each character. - *

- * - * Note that this method may allocate memory under some circumstances, unlike - * getLevelAt(). - * - * @return The levels array for the text, or null if an error - * occurs. - * - * @throws IllegalStateException if this call is not preceded by a successful - * call to setPara or - * setLine - * @stable ICU 3.8 - */ - byte[] getLevels() { - verifyValidParaOrLine(); - if (length <= 0) { - return new byte[0]; - } - return BidiLine.getLevels(this); - } - - /** - * Get the number of runs. This method may invoke the actual reordering on the - * Bidi object, after setPara() may have resolved only - * the levels of the text. Therefore, countRuns() may have to - * allocate memory, and may throw an exception if it fails to do so. - * - * @return The number of runs. - * - * @throws IllegalStateException if this call is not preceded by a successful - * call to setPara or - * setLine - * @stable ICU 3.8 - */ - public int countRuns() { - verifyValidParaOrLine(); - BidiLine.getRuns(this); - return runCount; - } - - /** - * - * Get a BidiRun object according to its index. BidiRun methods may - * be used to retrieve the run's logical start, length and level, which can be - * even for an LTR run or odd for an RTL run. 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. - *

- * countRuns() is normally called before the runs are retrieved. - * - *

- * Example: - * - *

-	 * Bidi bidi = new Bidi();
-	 * String text = "abc 123 DEFG xyz";
-	 * bidi.setPara(text, Bidi.RTL, null);
-	 * int i, count = bidi.countRuns(), logicalStart, visualIndex = 0, length;
-	 * BidiRun run;
-	 * for (i = 0; i < count; ++i) {
-	 * 	run = bidi.getVisualRun(i);
-	 * 	logicalStart = run.getStart();
-	 * 	length = run.getLength();
-	 * 	if (Bidi.LTR == run.getEmbeddingLevel()) {
-	 * 		do { // LTR
-	 * 			show_char(text.charAt(logicalStart++), visualIndex++);
-	 * 		} while (--length > 0);
-	 * 	} else {
-	 * 		logicalStart += length; // logicalLimit
-	 * 		do { // RTL
-	 * 			show_char(text.charAt(--logicalStart), visualIndex++);
-	 * 		} while (--length > 0);
-	 * 	}
-	 * }
-	 * 
- *

- * 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 {@link #writeReordered}, optionally with the - * {@link #KEEP_BASE_COMBINING} option, can be considered in order - * to avoid these issues. - * - * @param runIndex is the number of the run in visual order, in the range - * [0..countRuns()-1]. - * - * @return a BidiRun object containing the details of the run. The - * directionality of the run is LTR==0 or - * RTL==1, never MIXED. - * - * @throws IllegalStateException if this call is not preceded by a successful - * call to setPara or - * setLine - * @throws IllegalArgumentException if runIndex is not in the range - * 0<=runIndex<countRuns() - * - * @see #countRuns() - * @see com.ibm.icu.text.BidiRun - * @see com.ibm.icu.text.BidiRun#getStart() - * @see com.ibm.icu.text.BidiRun#getLength() - * @see com.ibm.icu.text.BidiRun#getEmbeddingLevel() - * @stable ICU 3.8 - */ - BidiRun getVisualRun(int runIndex) { - verifyValidParaOrLine(); - BidiLine.getRuns(this); - verifyRange(runIndex, 0, runCount); - return BidiLine.getVisualRun(this, runIndex); - } - - /** - * Get a visual-to-logical index map (array) for the characters in the - * Bidi (paragraph or line) object. - *

- * Some values in the map may be MAP_NOWHERE if the corresponding - * text characters are Bidi marks inserted in the visual output by the option - * OPTION_INSERT_MARKS. - *

- * When the visual output is altered by using options of - * writeReordered() such as INSERT_LRM_FOR_NUMERIC, - * KEEP_BASE_COMBINING, OUTPUT_REVERSE, - * REMOVE_BIDI_CONTROLS, the logical positions returned may not be - * correct. It is advised to use, when possible, reordering options such as - * {@link #OPTION_INSERT_MARKS} and {@link #OPTION_REMOVE_CONTROLS}. - * - * @return an array of getResultLength() indexes which will reflect - * the reordering of the characters.
- *
- * The index map will result in - * indexMap[visualIndex]==logicalIndex, where - * indexMap represents the returned array. - * - * @throws IllegalStateException if this call is not preceded by a successful - * call to setPara or - * setLine - * - * @see #getLogicalMap - * @see #getLogicalIndex - * @see #getResultLength - * @see #MAP_NOWHERE - * @see #OPTION_INSERT_MARKS - * @see #writeReordered - * @stable ICU 3.8 - */ - private int[] getVisualMap() { - /* countRuns() checks successful call to setPara/setLine */ - countRuns(); - if (resultLength <= 0) { - return new int[0]; - } - return BidiLine.getVisualMap(this); - } - - /** - * This is a convenience method that does not use a Bidi 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 getVisualMap() on a - * Bidi object. - * - * @param levels is an array of levels that have been determined by the - * application. - * - * @return an array of levels.length indexes which will reflect the - * reordering of the characters. - *

- * The index map will result in - * indexMap[visualIndex]==logicalIndex, where - * indexMap represents the returned array. - * - * @stable ICU 3.8 - */ - private static int[] reorderVisual(byte[] levels) { - return BidiLine.reorderVisual(levels); - } - - /** - * 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, the base direction - * is right-to-left. - * - * @stable ICU 3.8 - */ - public static final int DIRECTION_DEFAULT_RIGHT_TO_LEFT = LEVEL_DEFAULT_RTL; - - /** - * Create Bidi from the given text, embedding, and direction information. The - * embeddings array may be null. If present, the values represent embedding - * level information. Negative values from -1 to -61 indicate overrides at the - * absolute value of the level. Positive values from 1 to 61 indicate - * embeddings. Where values are zero, the base embedding level as determined by - * the base direction is assumed. - *

- * - * Note: this constructor calls setPara() internally. - * - * @param text an array containing the paragraph of text to process. - * @param textStart the index into the text array of the start of the - * paragraph. - * @param embeddings an array containing embedding values for each - * character in the paragraph. This can be null, in which - * case it is assumed that there is no external embedding - * information. - * @param embStart the index into the embedding array of the start of the - * paragraph. - * @param paragraphLength the length of the paragraph in the text and embeddings - * arrays. - * @param flags a collection of flags that control the algorithm. The - * algorithm understands the flags - * DIRECTION_LEFT_TO_RIGHT, DIRECTION_RIGHT_TO_LEFT, - * DIRECTION_DEFAULT_LEFT_TO_RIGHT, and - * DIRECTION_DEFAULT_RIGHT_TO_LEFT. Other values are - * reserved. - * - * @throws IllegalArgumentException if the values in embeddings are not within - * the allowed range - * - * @see #DIRECTION_LEFT_TO_RIGHT - * @see #DIRECTION_RIGHT_TO_LEFT - * @see #DIRECTION_DEFAULT_LEFT_TO_RIGHT - * @see #DIRECTION_DEFAULT_RIGHT_TO_LEFT - * @stable ICU 3.8 - */ - public BidiBase(char[] text, int textStart, byte[] embeddings, int embStart, int paragraphLength, int flags) { - this(0, 0); - byte paraLvl; - switch (flags) { - case Bidi.DIRECTION_LEFT_TO_RIGHT: - default: - paraLvl = LTR; - break; - case Bidi.DIRECTION_RIGHT_TO_LEFT: - paraLvl = RTL; - break; - case Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT: - paraLvl = LEVEL_DEFAULT_LTR; - break; - case Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT: - paraLvl = LEVEL_DEFAULT_RTL; - break; - } - byte[] paraEmbeddings; - if (embeddings == null) { - paraEmbeddings = null; - } else { - paraEmbeddings = new byte[paragraphLength]; - byte lev; - for (int i = 0; i < paragraphLength; i++) { - lev = embeddings[i + embStart]; - if (lev < 0) { - lev = (byte) ((-lev) | LEVEL_OVERRIDE); - } else if (lev == 0) { - lev = paraLvl; - if (paraLvl > MAX_EXPLICIT_LEVEL) { - lev &= 1; - } - } - paraEmbeddings[i] = lev; - } - } - - char[] paraText = new char[paragraphLength]; - System.arraycopy(text, textStart, paraText, 0, paragraphLength); - setPara(paraText, paraLvl, paraEmbeddings); - } - - /** - * Return true if the line is not left-to-right or right-to-left. This means it - * either has mixed runs of left-to-right and right-to-left text, or the base - * direction differs from the direction of the only run of text. - * - * @return true if the line is not left-to-right or right-to-left. - * - * @throws IllegalStateException if this call is not preceded by a successful - * call to setPara - * @stable ICU 3.8 - */ - public boolean isMixed() { - return (!isLeftToRight() && !isRightToLeft()); - } - - /** - * Return true if the line is all left-to-right text and the base direction is - * left-to-right. - * - * @return true if the line is all left-to-right text and the base direction is - * left-to-right. - * - * @throws IllegalStateException if this call is not preceded by a successful - * call to setPara - * @stable ICU 3.8 - */ - public boolean isLeftToRight() { - return (getDirection() == LTR && (paraLevel & 1) == 0); - } - - /** - * Return true if the line is all right-to-left text, and the base direction is - * right-to-left - * - * @return true if the line is all right-to-left text, and the base direction is - * right-to-left - * - * @throws IllegalStateException if this call is not preceded by a successful - * call to setPara - * @stable ICU 3.8 - */ - public boolean isRightToLeft() { - return (getDirection() == RTL && (paraLevel & 1) == 1); - } - - /** - * Return true if the base direction is left-to-right - * - * @return true if the base direction is left-to-right - * - * @throws IllegalStateException if this call is not preceded by a successful - * call to setPara or - * setLine - * - * @stable ICU 3.8 - */ - public boolean baseIsLeftToRight() { - return (getParaLevel() == LTR); - } - - /** - * Return the base level (0 if left-to-right, 1 if right-to-left). - * - * @return the base level - * - * @throws IllegalStateException if this call is not preceded by a successful - * call to setPara or - * setLine - * - * @stable ICU 3.8 - */ - public int getBaseLevel() { - return getParaLevel(); - } - - /** - * Compute the logical to visual run mapping - */ - void getLogicalToVisualRunsMap() { - if (isGoodLogicalToVisualRunsMap) { - return; - } - int count = countRuns(); - if ((logicalToVisualRunsMap == null) || (logicalToVisualRunsMap.length < count)) { - logicalToVisualRunsMap = new int[count]; - } - int i; - long[] keys = new long[count]; - for (i = 0; i < count; i++) { - keys[i] = ((long) (runs[i].start) << 32) + i; - } - Arrays.sort(keys); - for (i = 0; i < count; i++) { - logicalToVisualRunsMap[i] = (int) (keys[i] & 0x00000000FFFFFFFF); - } - isGoodLogicalToVisualRunsMap = true; - } - - /** - * Return the level of the nth logical run in this line. - * - * @param run the index of the run, between 0 and countRuns()-1 - * - * @return the level of the run - * - * @throws IllegalStateException if this call is not preceded by a successful - * call to setPara or - * setLine - * @throws IllegalArgumentException if run is not in the range - * 0<=run<countRuns() - * @stable ICU 3.8 - */ - public int getRunLevel(int run) { - verifyValidParaOrLine(); - BidiLine.getRuns(this); - - // for backward compatibility - if (run < 0 || run >= runCount) { - return getParaLevel(); - } - - getLogicalToVisualRunsMap(); - return runs[logicalToVisualRunsMap[run]].level; - } - - /** - * Return the index of the character at the start of the nth logical run in this - * line, as an offset from the start of the line. - * - * @param run the index of the run, between 0 and countRuns() - * - * @return the start of the run - * - * @throws IllegalStateException if this call is not preceded by a successful - * call to setPara or - * setLine - * @throws IllegalArgumentException if run is not in the range - * 0<=run<countRuns() - * @stable ICU 3.8 - */ - public int getRunStart(int run) { - verifyValidParaOrLine(); - BidiLine.getRuns(this); - - // for backward compatibility - if (runCount == 1) { - return 0; - } else if (run == runCount) { - return length; - } - - getLogicalToVisualRunsMap(); - return runs[logicalToVisualRunsMap[run]].start; - } - - /** - * Return the index of the character past the end of the nth logical run in this - * line, as an offset from the start of the line. For example, this will return - * the length of the line for the last run on the line. - * - * @param run the index of the run, between 0 and countRuns() - * - * @return the limit of the run - * - * @throws IllegalStateException if this call is not preceded by a successful - * call to setPara or - * setLine - * @throws IllegalArgumentException if run is not in the range - * 0<=run<countRuns() - * @stable ICU 3.8 - */ - public int getRunLimit(int run) { - verifyValidParaOrLine(); - BidiLine.getRuns(this); - - // for backward compatibility - if (runCount == 1) { - return length; - } - - getLogicalToVisualRunsMap(); - int idx = logicalToVisualRunsMap[run]; - int len = idx == 0 ? runs[idx].limit : runs[idx].limit - runs[idx - 1].limit; - return runs[idx].start + len; - } - - /** - * Return true if the specified text requires bidi analysis. If this returns - * false, the text will display left-to-right. Clients can then avoid - * constructing a Bidi object. Text in the Arabic Presentation Forms area of - * Unicode is presumed to already be shaped and ordered for display, and so will - * not cause this method to return true. - * - * @param text the text containing the characters to test - * @param start the start of the range of characters to test - * @param limit the limit of the range of characters to test - * - * @return true if the range of characters requires bidi analysis - * - * @stable ICU 3.8 - */ - public static boolean requiresBidi(char[] text, int start, int limit) { - final int RTLMask = (1 << R | 1 << AL | 1 << RLE | 1 << RLO | 1 << AN); - - if (0 > start || start > limit || limit > text.length) { - throw new IllegalArgumentException("Value start " + start + " is out of range 0 to " + limit + ", or limit " - + limit + " is beyond the text length " + text.length); - } - - for (int i = start; i < limit; ++i) { - if (Character.isHighSurrogate(text[i]) && i < (limit - 1) && Character.isLowSurrogate(text[i + 1])) { - if (((1 << UCharacter.getDirection(Character.codePointAt(text, i))) & RTLMask) != 0) { - return true; - } - } else if (((1 << UCharacter.getDirection(text[i])) & RTLMask) != 0) { - return true; - } - } - - return false; - } - - /** - * Reorder the objects in the array into visual order based on their levels. - * This is a utility method to use when you have a collection of objects - * representing runs of text in logical order, each run containing text at a - * single level. The elements at index from - * objectStart up to objectStart + count in the - * objects array will be reordered into visual order assuming each run of text - * has the level indicated by the corresponding element in the levels array (at - * index - objectStart + levelStart). - * - * @param levels an array representing the bidi level of each object - * @param levelStart the start position in the levels array - * @param objects the array of objects to be reordered into visual order - * @param objectStart the start position in the objects array - * @param count the number of objects to reorder - * @stable ICU 3.8 - */ - public static void reorderVisually(byte[] levels, int levelStart, Object[] objects, int objectStart, int count) { - // for backward compatibility - if (0 > levelStart || levels.length <= levelStart) { - throw new IllegalArgumentException( - "Value levelStart " + levelStart + " is out of range 0 to " + (levels.length - 1)); - } - if (0 > objectStart || objects.length <= objectStart) { - throw new IllegalArgumentException( - "Value objectStart " + objectStart + " is out of range 0 to " + (objects.length - 1)); - } - if (0 > count || objects.length < (objectStart + count)) { - throw new IllegalArgumentException("Value count " + count + " is less than zero, or objectStart + count" - + " is beyond objects length " + objects.length); - } - - byte[] reorderLevels = new byte[count]; - System.arraycopy(levels, levelStart, reorderLevels, 0, count); - int[] indexMap = reorderVisual(reorderLevels); - Object[] temp = new Object[count]; - System.arraycopy(objects, objectStart, temp, 0, count); - for (int i = 0; i < count; ++i) { - objects[objectStart + i] = temp[indexMap[i]]; - } - } - - /** - * Take a Bidi object containing the reordering information for a - * piece of text (one or more paragraphs) set by setPara() or for a - * line of text set by setLine() and return a string containing the - * reordered text. - * - *

- * The text may have been aliased (only a reference was stored without copying - * the contents), thus it must not have been modified since the - * setPara() call. - *

- * - * This method 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 returned string. 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 descriptions of the return value and the - * options parameter, and of the option bit flags. - * - * @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: DO_MIRRORING, - * INSERT_LRM_FOR_NUMERIC, - * KEEP_BASE_COMBINING, OUTPUT_REVERSE, - * REMOVE_BIDI_CONTROLS, STREAMING - * - * @return The reordered text. If the INSERT_LRM_FOR_NUMERIC option - * is set, then the length of the returned string could be as large as - * getLength()+2*countRuns().
- * If the REMOVE_BIDI_CONTROLS option is set, then the - * length of the returned string may be less than - * getLength().
- * If none of these options is set, then the length of the returned - * string will be exactly getProcessedLength(). - * - * @throws IllegalStateException if this call is not preceded by a successful - * call to setPara or - * setLine - * - * @see #DO_MIRRORING - * @see #INSERT_LRM_FOR_NUMERIC - * @see #KEEP_BASE_COMBINING - * @see #OUTPUT_REVERSE - * @see #REMOVE_BIDI_CONTROLS - * @see #OPTION_STREAMING - * @see #getProcessedLength - * @stable ICU 3.8 - */ - public String writeReordered(int options) { - verifyValidParaOrLine(); - if (length == 0) { - /* nothing to do */ - return ""; - } - return BidiWriter.writeReordered(this, options); - } - - /** - * Display the bidi internal state, used in debugging. - */ - public String toString() { - StringBuilder buf = new StringBuilder(getClass().getName()); - - buf.append("[dir: "); - buf.append(direction); - buf.append(" baselevel: "); - buf.append(paraLevel); - buf.append(" length: "); - buf.append(length); - buf.append(" runs: "); - if (levels == null) { - buf.append("none"); - } else { - buf.append('['); - buf.append(levels[0]); - for (int i = 1; i < levels.length; i++) { - buf.append(' '); - buf.append(levels[i]); - } - buf.append(']'); - } - buf.append(" text: [0x"); - buf.append(Integer.toHexString(text[0])); - for (int i = 1; i < text.length; i++) { - buf.append(" 0x"); - buf.append(Integer.toHexString(text[i])); - } - buf.append("]]"); - - return buf.toString(); - } - -} +/* + * Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* +******************************************************************************* +* Copyright (C) 2001-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +*/ + +/* 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_L + * 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. + */ + +package jdk_internal.icu.text; + +import java.lang.reflect.Array; +import java.util.Arrays; + +import jdk_internal.bidi.AttributedCharacterIterator; +import jdk_internal.bidi.Bidi; +import jdk_internal.bidi.NumericShaper; +import jdk_internal.bidi.TextAttribute; +import jdk_internal.icu.impl.UBiDiProps; +import jdk_internal.icu.lang.UCharacter; + +/** + * + *

Bidi algorithm for ICU

+ * + * This is an implementation of the Unicode Bidirectional Algorithm. The + * algorithm is defined in the + * Unicode Standard Annex #9: + * Unicode Bidirectional Algorithm. + *

+ * + * Note: Libraries that perform a bidirectional algorithm and reorder strings + * accordingly are sometimes called "Storage Layout Engines". ICU's Bidi and + * shaping (ArabicShaping) classes can be used at the core of such "Storage + * Layout Engines". + * + *

General remarks about the API:

+ * + * 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 methods 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. + * + *

Basic concept: paragraph

A piece of text can be divided into several + * paragraphs by characters with the Bidi class Block Separator. + * For handling of paragraphs, see: + *
    + *
  • {@link #countParagraphs} + *
  • {@link #getParaLevel} + *
  • {@link #getParagraph} + *
  • {@link #getParagraphByIndex} + *
+ * + *

Basic concept: text direction

The direction of a piece of text may + * be: + *
    + *
  • {@link #LTR} + *
  • {@link #RTL} + *
  • {@link #MIXED} + *
  • {@link #NEUTRAL} + *
+ * + *

Basic concept: levels

+ * + * Levels in this API represent embedding levels according to the Unicode + * Bidirectional Algorithm. Their low-order bit (even/odd value) indicates the + * visual direction. + *

+ * + * Levels can be abstract values when used for the paraLevel and + * embeddingLevels arguments of setPara(); there: + *

    + *
  • the high-order bit 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 + * LEVEL_DEFAULT_LTR and LEVEL_DEFAULT_RTL.
  • + *
+ * + *

+ * The related constants are not real, valid level values. + * DEFAULT_XXX can be used to specify a default for the paragraph + * level for when the setPara() method shall determine it but there + * is no strongly typed character in the input. + *

+ * + * Note that the value for LEVEL_DEFAULT_LTR is even and the one + * for LEVEL_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 MAX_EXPLICIT_LEVEL is odd. + * + *

+ * See Also: + *

    + *
  • {@link #LEVEL_DEFAULT_LTR} + *
  • {@link #LEVEL_DEFAULT_RTL} + *
  • {@link #LEVEL_OVERRIDE} + *
  • {@link #MAX_EXPLICIT_LEVEL} + *
  • {@link #setPara} + *
+ * + *

Basic concept: Reordering Mode

Reordering mode values indicate which + * variant of the Bidi algorithm to use. + * + *

+ * See Also: + *

    + *
  • {@link #setReorderingMode} + *
  • {@link #REORDER_DEFAULT} + *
  • {@link #REORDER_NUMBERS_SPECIAL} + *
  • {@link #REORDER_GROUP_NUMBERS_WITH_R} + *
  • {@link #REORDER_RUNS_ONLY} + *
  • {@link #REORDER_INVERSE_NUMBERS_AS_L} + *
  • {@link #REORDER_INVERSE_LIKE_DIRECT} + *
  • {@link #REORDER_INVERSE_FOR_NUMBERS_SPECIAL} + *
+ * + *

Basic concept: Reordering Options

Reordering options can be applied + * during Bidi text transformations. + * + *

+ * See Also: + *

    + *
  • {@link #setReorderingOptions} + *
  • {@link #OPTION_DEFAULT} + *
  • {@link #OPTION_INSERT_MARKS} + *
  • {@link #OPTION_REMOVE_CONTROLS} + *
  • {@link #OPTION_STREAMING} + *
+ * + * + * @author Simon Montagu, Matitiahu Allouche (ported from C code written by + * Markus W. Scherer) + * @stable ICU 3.8 + * + * + *

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
+ *
+ *  package com.ibm.icu.dev.test.bidi;
+ *
+ *  import com.ibm.icu.text.Bidi;
+ *  import com.ibm.icu.text.BidiRun;
+ *
+ *  public class Sample {
+ *
+ *      static final int styleNormal = 0;
+ *      static final int styleSelected = 1;
+ *      static final int styleBold = 2;
+ *      static final int styleItalics = 4;
+ *      static final int styleSuper=8;
+ *      static final int styleSub = 16;
+ *
+ *      static class StyleRun {
+ *          int limit;
+ *          int style;
+ *
+ *          public StyleRun(int limit, int style) {
+ *              this.limit = limit;
+ *              this.style = style;
+ *         }
+ *         }
+ *
+ *         static class Bounds {
+ *         int start;
+ *         int limit;
+ *
+ *         public Bounds(int start, int limit) {
+ *         this.start = start;
+ *         this.limit = limit;
+ *         }
+ *         }
+ *
+ *         static int getTextWidth(String text, int start, int limit, StyleRun[] styleRuns, int styleRunCount) {
+ *         // simplistic way to compute the width
+ *         return limit - start;
+ *         }
+ *
+ *         // set limit and StyleRun limit for a line
+ *         	// from text[start] and from styleRuns[styleRunStart]
+ *         	// using Bidi.getLogicalRun(...)
+ *         	// returns line width
+ *         static int getLineBreak(String text, Bounds line, Bidi para, StyleRun styleRuns[], Bounds styleRun) {
+ *         // dummy return
+ *         return 0;
+ *         }
+ *
+ *         // render runs on a line sequentially, always from left to right
+ *
+ *         // prepare rendering a new line
+ *         static void startLine(byte textDirection, int lineWidth) {
+ *         System.out.println();
+ *         }
+ *
+ *         // render a run of text and advance to the right by the run width
+ *         	// the text[start..limit-1] is always in logical order
+ *         static void renderRun(String text, int start, int limit, byte textDirection, int 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
+ *         static void renderDirectionalRun(String text, int start, int limit, byte direction, StyleRun styleRuns[],
+ *         int styleRunCount) {
+ *         int i;
+ *
+ *         // iterate over style runs
+ *         if (direction == Bidi.LTR) {
+ *         int styleLimit;
+ *         for (i = 0; i < styleRunCount; ++i) {
+ *         styleLimit = styleRuns[i].limit;
+ *         if (start < styleLimit) {
+ *         if (styleLimit > limit) {
+ *         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 < start) {
+ *         styleStart = start;
+ *         }
+ *         renderRun(text, styleStart, limit, direction, styleRuns[i].style);
+ *         if (styleStart == start) {
+ *         break;
+ *         }
+ *         limit = styleStart;
+ *         }
+ *         }
+ *         }
+ *         }
+ *
+ *         // the line object represents text[start..limit-1]
+ *         static void renderLine(Bidi line, String text, int start, int limit, StyleRun styleRuns[], int styleRunCount) {
+ *         byte direction = line.getDirection();
+ *         if (direction != Bidi.MIXED) {
+ *         // unidirectional
+ *         if (styleRunCount <= 1) {
+ *         renderRun(text, start, limit, direction, styleRuns[0].style);
+ *         } else {
+ *         renderDirectionalRun(text, start, limit, direction, styleRuns, styleRunCount);
+ *         }
+ *         } else {
+ *         // mixed-directional
+ *         int count, i;
+ *         BidiRun run;
+ *
+ *         try {
+ *         count = line.countRuns();
+ *         } catch (IllegalStateException e) {
+ *         e.printStackTrace();
+ *         return;
+ *         }
+ *         if (styleRunCount <= 1) {
+ *         int style = styleRuns[0].style;
+ *
+ *         // iterate over directional runs
+ *         for (i = 0; i < count; ++i) {
+ *         run = line.getVisualRun(i);
+ *         renderRun(text, run.getStart(), run.getLimit(), run.getDirection(), style);
+ *         }
+ *         } else {
+ *         // iterate over both directional and style runs
+ *         for (i = 0; i < count; ++i) {
+ *         run = line.getVisualRun(i);
+ *         renderDirectionalRun(text, run.getStart(), run.getLimit(), run.getDirection(), styleRuns,
+ *         styleRunCount);
+ *         }
+ *         }
+ *         }
+ *         }
+ *
+ *         static void renderParagraph(String text, byte textDirection, StyleRun styleRuns[], int styleRunCount,
+ *         int lineWidth) {
+ *         int length = text.length();
+ *         Bidi para = new Bidi();
+ *         try {
+ *         para.setPara(text, textDirection != 0 ? Bidi.LEVEL_DEFAULT_RTL : Bidi.LEVEL_DEFAULT_LTR, null);
+ *         } catch (Exception e) {
+ *         e.printStackTrace();
+ *         return;
+ *         }
+ *         byte paraLevel = (byte) (1 & para.getParaLevel());
+ *         StyleRun styleRun = new StyleRun(length, styleNormal);
+ *
+ *         if (styleRuns == null || styleRunCount <= 0) {
+ *         styleRuns = new StyleRun[1];
+ *         styleRunCount = 1;
+ *         styleRuns[0] = styleRun;
+ *         }
+ *         // assume styleRuns[styleRunCount-1].limit>=length
+ *
+ *         int 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);
+ *         } else {
+ *         // we need to render several lines
+ *         Bidi line = new Bidi(length, 0);
+ *         int start = 0, limit;
+ *         int styleRunStart = 0, styleRunLimit;
+ *
+ *         for (;;) {
+ *         limit = length;
+ *         styleRunLimit = styleRunCount;
+ *         width = getLineBreak(text, new Bounds(start, limit), para, styleRuns,
+ *         new Bounds(styleRunStart, styleRunLimit));
+ *         try {
+ *         line = para.setLine(start, limit);
+ *         } catch (Exception e) {
+ *         e.printStackTrace();
+ *         return;
+ *         }
+ *         // prepare rendering a new line
+ *         				// from either left or right
+ *         startLine(paraLevel, width);
+ *
+ *         if (styleRunStart > 0) {
+ *         int newRunCount = styleRuns.length - styleRunStart;
+ *         StyleRun[] newRuns = new StyleRun[newRunCount];
+ *         System.arraycopy(styleRuns, styleRunStart, newRuns, 0, newRunCount);
+ *         renderLine(line, text, start, limit, newRuns, styleRunLimit - styleRunStart);
+ *         } else {
+ *         renderLine(line, text, start, limit, styleRuns, styleRunLimit - styleRunStart);
+ *         }
+ *         if (limit == length) {
+ *         break;
+ *         }
+ *         start = limit;
+ *         styleRunStart = styleRunLimit - 1;
+ *         if (start >= styleRuns[styleRunStart].limit) {
+ *         ++styleRunStart;
+ *         }
+ *         }
+ *         }
+ *         }
+ *
+ *         public static void main(String[] args) {
+ *         renderParagraph("Some Latin text...", Bidi.LTR, null, 0, 80);
+ *         renderParagraph("Some Hebrew text...", Bidi.RTL, null, 0, 60);
+ *         }
+ *         }
+ *
+ * }
+ */ + +/* + * 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(). + */ + +// Original filename in ICU4J: Bidi.java +public class BidiBase { + + static class Point { + int pos; /* position in text */ + int flag; /* flag for LRM/RLM, before/after */ + } + + static class InsertPoints { + int size; + int confirmed; + Point[] points = new Point[0]; + } + + static class Opening { + int position; /* position of opening bracket */ + int match; /* matching char or -position of closing bracket */ + int contextPos; /* position of last strong char found before opening */ + short flags; /* bits for L or R/AL found within the pair */ + byte contextDir; /* L or R according to last strong char before opening */ + } + + static class IsoRun { + int contextPos; /* position of char determining context */ + short start; /* index of first opening entry for this run */ + short limit; /* index after last opening entry for this run */ + byte level; /* level of this run */ + byte lastStrong; /* bidi class of last strong char found in this run */ + byte lastBase; /* bidi class of last base char found in this run */ + byte contextDir; /* L or R to use as context for following openings */ + } + + static class BracketData { + Opening[] openings = new Opening[SIMPLE_PARAS_COUNT]; + int 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 = new IsoRun[MAX_EXPLICIT_LEVEL + 2]; + boolean isNumbersSpecial; /* reordering mode for NUMBERS_SPECIAL */ + } + + static class Isolate { + int startON; + int start1; + short stateImp; + short state; + } + + /** + * 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 + * REORDER_INVERSE_LIKE_DIRECT or + * 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 rightmost or + * leftmost strong character of the source text is RTL or Arabic Letter, the + * direction will be LTR otherwise. + *

+ * + * If reordering option 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 #REORDER_INVERSE_LIKE_DIRECT + * @see #REORDER_INVERSE_FOR_NUMBERS_SPECIAL + * @stable ICU 3.8 + */ + public static final byte LEVEL_DEFAULT_LTR = (byte) 0x7e; + + /** + * 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 + * REORDER_INVERSE_LIKE_DIRECT or + * 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 rightmost 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 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 #REORDER_INVERSE_LIKE_DIRECT + * @see #REORDER_INVERSE_FOR_NUMBERS_SPECIAL + * @stable ICU 3.8 + */ + public static final byte LEVEL_DEFAULT_RTL = (byte) 0x7f; + + /** + * Maximum explicit embedding level. (The maximum resolved level can be up to + * MAX_EXPLICIT_LEVEL+1). + * + * @stable ICU 3.8 + */ + public static final byte MAX_EXPLICIT_LEVEL = 125; + + /** + * Bit flag for level input. Overrides directional properties. + * + * @stable ICU 3.8 + */ + public static final byte LEVEL_OVERRIDE = (byte) 0x80; + + /** + * Special value which can be returned by the mapping methods 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 + * 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 + * OPTION_INSERT_MARKS. + * + * @see #getVisualIndex + * @see #getVisualMap + * @see #getLogicalIndex + * @see #getLogicalMap + * @see #OPTION_INSERT_MARKS + * @see #OPTION_REMOVE_CONTROLS + * @stable ICU 3.8 + */ + public static final int MAP_NOWHERE = -1; + + /** + * Left-to-right text. + *

    + *
  • As return value for 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 getBaseDirection(), it means that the + * first strong character of the source string has a left-to-right direction. + *
+ * + * @stable ICU 3.8 + */ + public static final byte LTR = 0; + + /** + * Right-to-left text. + *
    + *
  • As return value for 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 getBaseDirection(), it means that the + * first strong character of the source string has a right-to-left direction. + *
+ * + * @stable ICU 3.8 + */ + public static final byte RTL = 1; + + /** + * Mixed-directional text. + *

+ * As return value for getDirection(), it means that the source + * string contains both left-to-right and right-to-left characters. + * + * @stable ICU 3.8 + */ + public static final byte MIXED = 2; + + /** + * option bit for writeReordered(): keep combining characters after their base + * characters in RTL runs + * + * @see #writeReordered + * @stable ICU 3.8 + */ + public static final short KEEP_BASE_COMBINING = 1; + + /** + * option bit for writeReordered(): replace characters with the "mirrored" + * property in RTL runs by their mirror-image mappings + * + * @see #writeReordered + * @stable ICU 3.8 + */ + public static final short DO_MIRRORING = 2; + + /** + * option bit for 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 #setInverse + * @see #writeReordered + * @stable ICU 3.8 + */ + public static final short INSERT_LRM_FOR_NUMERIC = 4; + + /** + * option bit for writeReordered(): remove Bidi control characters (this does + * not affect INSERT_LRM_FOR_NUMERIC) + * + *

+ * This option does not imply corresponding adjustment of the index mappings. + *

+ * + * @see #writeReordered + * @see #INSERT_LRM_FOR_NUMERIC + * @stable ICU 3.8 + */ + public static final short REMOVE_BIDI_CONTROLS = 8; + + /** + * option bit for writeReordered(): write the output in reverse order + * + *

+ * This has the same effect as calling writeReordered() first + * without this option, and then calling 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 #writeReordered + * @stable ICU 3.8 + */ + public static final short OUTPUT_REVERSE = 16; + + /** + * Reordering mode: Regular Logical to Visual Bidi algorithm according to + * Unicode. + * + * @see #setReorderingMode + * @stable ICU 3.8 + */ + private static final short REORDER_DEFAULT = 0; + + /** + * Reordering mode: Logical to Visual algorithm which handles numbers in a way + * which mimicks the behavior of Windows XP. + * + * @see #setReorderingMode + * @stable ICU 3.8 + */ + private static final short REORDER_NUMBERS_SPECIAL = 1; + + /** + * Reordering mode: Logical to Visual algorithm grouping numbers with adjacent R + * characters (reversible algorithm). + * + * @see #setReorderingMode + * @stable ICU 3.8 + */ + private static final short REORDER_GROUP_NUMBERS_WITH_R = 2; + + /** + * Reordering mode: 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 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. + * + * @see #OPTION_INSERT_MARKS + * @see #setReorderingMode + * @stable ICU 3.8 + */ + static final short REORDER_RUNS_ONLY = 3; + + /** + * Reordering mode: Visual to Logical algorithm which handles numbers like L + * (same algorithm as selected by setInverse(true). + * + * @see #setInverse + * @see #setReorderingMode + * @stable ICU 3.8 + */ + static final short REORDER_INVERSE_NUMBERS_AS_L = 4; + + /** + * Reordering mode: Visual to Logical algorithm equivalent to the regular + * Logical to Visual algorithm. + * + * @see #setReorderingMode + * @stable ICU 3.8 + */ + static final short REORDER_INVERSE_LIKE_DIRECT = 5; + + /** + * Reordering mode: Inverse Bidi (Visual to Logical) algorithm for the + * REORDER_NUMBERS_SPECIAL Bidi algorithm. + * + * @see #setReorderingMode + * @stable ICU 3.8 + */ + static final short REORDER_INVERSE_FOR_NUMBERS_SPECIAL = 6; + + /* + * Reordering mode values must be ordered so that all the regular logical to + * visual modes come first, and all inverse Bidi modes come last. + */ + private static final short REORDER_LAST_LOGICAL_TO_VISUAL = REORDER_NUMBERS_SPECIAL; + + /** + * Option bit for 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 setPara. + *

+ * + *

+ * This option is significant only with reordering modes which generate a result + * with Logical order, specifically. + *

+ *
    + *
  • REORDER_RUNS_ONLY
  • + *
  • REORDER_INVERSE_NUMBERS_AS_L
  • + *
  • REORDER_INVERSE_LIKE_DIRECT
  • + *
  • REORDER_INVERSE_FOR_NUMBERS_SPECIAL
  • + *
+ * + *

+ * If this option is set in conjunction with reordering mode + * REORDER_INVERSE_NUMBERS_AS_L or with calling + * setInverse(true), it implies option + * INSERT_LRM_FOR_NUMERIC in calls to method + * 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 OPTION_REMOVE_CONTROLS set before + * calling setPara() or option REMOVE_BIDI_CONTROLS in + * writeReordered), the result will be identical to the source text + * in the first transformation. + * + *

+ * This option will be ignored if specified together with option + * OPTION_REMOVE_CONTROLS. It inhibits option + * REMOVE_BIDI_CONTROLS in calls to method + * writeReordered() and it implies option + * INSERT_LRM_FOR_NUMERIC in calls to method + * writeReordered() if the reordering mode is + * REORDER_INVERSE_NUMBERS_AS_L. + *

+ * + * @see #setReorderingMode + * @see #setReorderingOptions + * @see #INSERT_LRM_FOR_NUMERIC + * @see #REMOVE_BIDI_CONTROLS + * @see #OPTION_REMOVE_CONTROLS + * @see #REORDER_RUNS_ONLY + * @see #REORDER_INVERSE_NUMBERS_AS_L + * @see #REORDER_INVERSE_LIKE_DIRECT + * @see #REORDER_INVERSE_FOR_NUMBERS_SPECIAL + * @stable ICU 3.8 + */ + static final int OPTION_INSERT_MARKS = 1; + + /** + * Option bit for setReorderingOptions: remove Bidi control + * characters + * + *

+ * This option must be set or reset before calling setPara. + *

+ * + *

+ * This option nullifies option OPTION_INSERT_MARKS. It inhibits + * option INSERT_LRM_FOR_NUMERIC in calls to method + * writeReordered() and it implies option + * REMOVE_BIDI_CONTROLS in calls to that method. + *

+ * + * @see #setReorderingMode + * @see #setReorderingOptions + * @see #OPTION_INSERT_MARKS + * @see #INSERT_LRM_FOR_NUMERIC + * @see #REMOVE_BIDI_CONTROLS + * @stable ICU 3.8 + */ + static final int OPTION_REMOVE_CONTROLS = 2; + + /** + * Option bit for setReorderingOptions: process the output as part + * of a stream to be continued + * + *

+ * This option must be set or reset before calling 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, setPara() may process less than the + * full source text in order to truncate the text at a meaningful boundary. The + * caller should call getProcessedLength() immediately after + * calling 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 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 + * OPTION_STREAMING.
  • + *
+ * In all cases, this option should be turned off before processing the last + * part of the text. + *

+ * + *

+ * When the OPTION_STREAMING option is used, it is recommended to + * call orderParagraphsLTR(true) before calling + * setPara() so that later paragraphs may be concatenated to + * previous paragraphs on the right. + *

+ * + * @see #setReorderingMode + * @see #setReorderingOptions + * @see #getProcessedLength + * @stable ICU 3.8 + */ + private static final int OPTION_STREAMING = 4; + + /* + * 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 + * UCharacterDirection + */ + /* private */ static final byte L = 0; + private static final byte R = 1; + private static final byte EN = 2; + private static final byte ES = 3; + private static final byte ET = 4; + private static final byte AN = 5; + private static final byte CS = 6; + static final byte B = 7; + private static final byte S = 8; + private static final byte WS = 9; + private static final byte ON = 10; + private static final byte LRE = 11; + private static final byte LRO = 12; + private static final byte AL = 13; + private static final byte RLE = 14; + private static final byte RLO = 15; + private static final byte PDF = 16; + private static final byte NSM = 17; + private static final byte BN = 18; + private static final byte FSI = 19; + private static final byte LRI = 20; + private static final byte RLI = 21; + private static final byte PDI = 22; + private static final byte ENL = PDI + 1; /* EN after W7 */ + private static final byte ENR = ENL + 1; /* EN not subject to W7 */ + + // Number of directional types + private static final int CHAR_DIRECTION_COUNT = 23; + + /** + * Enumerated property Bidi_Paired_Bracket_Type (new in Unicode 6.3). Used in + * Unicode Standard Annex #9: + * Unicode Bidirectional Algorithm. Returns UCharacter.BidiPairedBracketType + * values. + * + * @stable ICU 52 + */ + public static final int BIDI_PAIRED_BRACKET_TYPE = 0x1015; + + /** + * Bidi Paired Bracket Type constants. + * + * @see UProperty#BIDI_PAIRED_BRACKET_TYPE + * @stable ICU 52 + */ + public static interface BidiPairedBracketType { + /** + * Not a paired bracket. + * + * @stable ICU 52 + */ + public static final int NONE = 0; + /** + * Open paired bracket. + * + * @stable ICU 52 + */ + public static final int OPEN = 1; + /** + * Close paired bracket. + * + * @stable ICU 52 + */ + public static final int CLOSE = 2; + /** + * @stable ICU 52 + */ + public static final int COUNT = 3; + } + + /* number of paras entries allocated initially */ + static final int SIMPLE_PARAS_COUNT = 10; + + private static final char CR = '\r'; + private static final char LF = '\n'; + + static final int LRM_BEFORE = 1; + static final int LRM_AFTER = 2; + static final int RLM_BEFORE = 4; + static final int RLM_AFTER = 8; + + /* flags for Opening.flags */ + static final byte FOUND_L = (byte) DirPropFlag(L); + static final byte FOUND_R = (byte) DirPropFlag(R); + + /* + * The following bit is used for the directional isolate status. Stack entries + * corresponding to isolate sequences are greater than ISOLATE. + */ + static final int ISOLATE = 0x0100; + + /* + * reference to parent paragraph object (reference 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 setPara or setLine + */ + BidiBase paraBidi; + + final UBiDiProps bdp; + + /* character array representing the current text */ + char[] text; + + /* length of the current text */ + int originalLength; + + /* + * if the option OPTION_STREAMING is set, this is the length of text actually + * processed by setPara, which may be shorter than the original + * length. Otherwise, it is identical to the original length. + */ + public int length; + + /* + * if option OPTION_REMOVE_CONTROLS is set, and/or Bidi marks are allowed to be + * inserted in one of the reordering modes, the length of the result string may + * be different from the processed length. + */ + int resultLength; + + /* indicators for whether memory may be allocated after construction */ + boolean mayAllocateText; + boolean mayAllocateRuns; + + /* arrays with one value per text-character */ + byte[] dirPropsMemory = new byte[1]; + byte[] levelsMemory = new byte[1]; + byte[] dirProps; + byte[] levels; + + /* are we performing an approximation of the "inverse Bidi" algorithm? */ + boolean isInverse; + + /* are we using the basic algorithm or its variation? */ + int reorderingMode; + + /* bitmask for reordering options */ + int reorderingOptions; + + /* must block separators receive level 0? */ + boolean orderParagraphsLTR; + + /* the paragraph level */ + byte paraLevel; + + /* original paraLevel when contextual */ + /* must be one of DEFAULT_xxx or 0 if not contextual */ + byte defaultParaLevel; + + /* the following is set in setPara, used in processPropertySeq */ + + ImpTabPair impTabPair; /* reference to levels state table pair */ + + /* the overall paragraph or line directionality */ + byte direction; + + /* flags is a bit set for which directional properties are in the text */ + int flags; + + /* lastArabicPos is index to the last AL in the text, -1 if none */ + int lastArabicPos; + + /* characters after trailingWSStart are WS and are */ + /* implicitly at the paraLevel (rule (L1)) - levels may not reflect that */ + int trailingWSStart; + + /* fields for paragraph handling, set in getDirProps() */ + int paraCount; + int[] paras_limit = new int[SIMPLE_PARAS_COUNT]; + byte[] paras_level = new byte[SIMPLE_PARAS_COUNT]; + + /* fields for line reordering */ + int runCount; /* ==-1: runs not set up yet */ + BidiRun[] runsMemory = new BidiRun[0]; + BidiRun[] runs; + + /* for non-mixed text, we only need a tiny array of runs (no allocation) */ + BidiRun[] simpleRuns = { new BidiRun() }; + + /* fields for managing isolate sequences */ + Isolate[] isolates; + + /* 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. + */ + int isolateCount; + + /* mapping of runs in logical order to visual order */ + int[] logicalToVisualRunsMap; + /* flag to indicate that the map has been updated */ + boolean isGoodLogicalToVisualRunsMap; + + /* for inverse Bidi with insertion of directional marks */ + InsertPoints insertPoints = new InsertPoints(); + + /* for option OPTION_REMOVE_CONTROLS */ + int controlCount; + + /* + * Sometimes, bit values are more appropriate to deal with directionality + * properties. Abbreviations in these method names refer to names used in the + * Bidi algorithm. + */ + static int DirPropFlag(byte dir) { + return (1 << dir); + } + + boolean testDirPropFlagAt(int flag, int index) { + return ((DirPropFlag(dirProps[index]) & flag) != 0); + } + + static final int DirPropFlagMultiRuns = DirPropFlag((byte) 31); + + /* to avoid some conditional statements, use tiny constant arrays */ + static final int DirPropFlagLR[] = { DirPropFlag(L), DirPropFlag(R) }; + static final int DirPropFlagE[] = { DirPropFlag(LRE), DirPropFlag(RLE) }; + static final int DirPropFlagO[] = { DirPropFlag(LRO), DirPropFlag(RLO) }; + + static final int DirPropFlagLR(byte level) { + return DirPropFlagLR[level & 1]; + } + + static final int DirPropFlagE(byte level) { + return DirPropFlagE[level & 1]; + } + + static final int DirPropFlagO(byte level) { + return DirPropFlagO[level & 1]; + } + + static final byte DirFromStrong(byte strong) { + return strong == L ? L : R; + } + + static final byte NoOverride(byte level) { + return (byte) (level & ~LEVEL_OVERRIDE); + } + + /* are there any characters that are LTR or RTL? */ + static final int MASK_LTR = DirPropFlag(L) | DirPropFlag(EN) | DirPropFlag(ENL) | DirPropFlag(ENR) | DirPropFlag(AN) + | DirPropFlag(LRE) | DirPropFlag(LRO) | DirPropFlag(LRI); + static final int MASK_RTL = DirPropFlag(R) | DirPropFlag(AL) | DirPropFlag(RLE) | DirPropFlag(RLO) + | DirPropFlag(RLI); + + static final int MASK_R_AL = DirPropFlag(R) | DirPropFlag(AL); + + /* explicit embedding codes */ + private static final int MASK_EXPLICIT = DirPropFlag(LRE) | DirPropFlag(LRO) | DirPropFlag(RLE) | DirPropFlag(RLO) + | DirPropFlag(PDF); + private static final int MASK_BN_EXPLICIT = DirPropFlag(BN) | MASK_EXPLICIT; + + /* explicit isolate codes */ + private static final int MASK_ISO = DirPropFlag(LRI) | DirPropFlag(RLI) | DirPropFlag(FSI) | DirPropFlag(PDI); + + /* paragraph and segment separators */ + private static final int MASK_B_S = DirPropFlag(B) | DirPropFlag(S); + + /* all types that are counted as White Space or Neutral in some steps */ + static final int MASK_WS = MASK_B_S | DirPropFlag(WS) | MASK_BN_EXPLICIT | MASK_ISO; + + /* types that are neutrals or could becomes neutrals in (Wn) */ + private static final int MASK_POSSIBLE_N = DirPropFlag(ON) | DirPropFlag(CS) | DirPropFlag(ES) | DirPropFlag(ET) + | MASK_WS; + + /* + * These types may be changed to "e", the embedding type (L or R) of the run, in + * the Bidi algorithm (N2) + */ + private static final int MASK_EMBEDDING = DirPropFlag(NSM) | MASK_POSSIBLE_N; + + /* + * the dirProp's L and R are defined to 0 and 1 values in + * UCharacterDirection.java + */ + private static byte GetLRFromLevel(byte level) { + return (byte) (level & 1); + } + + private static boolean IsDefaultLevel(byte level) { + return ((level & LEVEL_DEFAULT_LTR) == LEVEL_DEFAULT_LTR); + } + + static boolean IsBidiControlChar(int c) { + /* + * check for range 0x200c to 0x200f (ZWNJ, ZWJ, LRM, RLM) or 0x202a to 0x202e + * (LRE, RLE, PDF, LRO, RLO) + */ + return (((c & 0xfffffffc) == 0x200c) || ((c >= 0x202a) && (c <= 0x202e)) || ((c >= 0x2066) && (c <= 0x2069))); + } + + void verifyValidPara() { + if (!(this == this.paraBidi)) { + throw new IllegalStateException(); + } + } + + void verifyValidParaOrLine() { + BidiBase para = this.paraBidi; + /* verify Para */ + if (this == para) { + return; + } + /* verify Line */ + if ((para == null) || (para != para.paraBidi)) { + throw new IllegalStateException(); + } + } + + void verifyRange(int index, int start, int limit) { + if (index < start || index >= limit) { + throw new IllegalArgumentException("Value " + index + " is out of range " + start + " to " + limit); + } + } + + /** + * Allocate a Bidi object with preallocated memory for internal + * structures. This method provides a Bidi object like the default + * constructor but it also preallocates memory for internal structures according + * to the sizings supplied by the caller. + *

+ * 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 the default constructor. + * + * @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. + * + * @throws IllegalArgumentException if maxLength or maxRunCount is less than 0 + * @stable ICU 3.8 + */ + public BidiBase(int maxLength, int maxRunCount) { + /* check the argument values */ + if (maxLength < 0 || maxRunCount < 0) { + throw new IllegalArgumentException(); + } + + /* + * reset the object, all reference variables null, all flags false, all sizes 0. + * In fact, we don't need to do anything, since class members are initialized as + * zero when an instance is created. + */ + /* + * mayAllocateText = false; mayAllocateRuns = false; orderParagraphsLTR = false; + * paraCount = 0; runCount = 0; trailingWSStart = 0; flags = 0; paraLevel = 0; + * defaultParaLevel = 0; direction = 0; + */ + /* get Bidi properties */ + bdp = UBiDiProps.INSTANCE; + + /* allocate memory for arrays as requested */ + if (maxLength > 0) { + getInitialDirPropsMemory(maxLength); + getInitialLevelsMemory(maxLength); + } else { + mayAllocateText = true; + } + + if (maxRunCount > 0) { + // if maxRunCount == 1, use simpleRuns[] + if (maxRunCount > 1) { + getInitialRunsMemory(maxRunCount); + } + } else { + mayAllocateRuns = true; + } + } + + /* + * We are allowed to allocate memory if object==null or mayAllocate==true for + * each array that we need. + * + * Assume sizeNeeded>0. If object != null, then assume size > 0. + */ + private Object getMemory(String label, Object array, Class arrayClass, boolean mayAllocate, int sizeNeeded) { + int len = Array.getLength(array); + + /* we have at least enough memory and must not allocate */ + if (sizeNeeded == len) { + return array; + } + if (!mayAllocate) { + /* we must not allocate */ + if (sizeNeeded <= len) { + return array; + } + throw new OutOfMemoryError("Failed to allocate memory for " + label); + } + /* we may try to grow or shrink */ + /* + * FOOD FOR THOUGHT: when shrinking it should be possible to avoid the + * allocation altogether and rely on this.length + */ + try { + return Array.newInstance(arrayClass, sizeNeeded); + } catch (Exception e) { + throw new OutOfMemoryError("Failed to allocate memory for " + label); + } + } + + /* helper methods for each allocated array */ + private void getDirPropsMemory(boolean mayAllocate, int len) { + Object array = getMemory("DirProps", dirPropsMemory, Byte.TYPE, mayAllocate, len); + dirPropsMemory = (byte[]) array; + } + + void getDirPropsMemory(int len) { + getDirPropsMemory(mayAllocateText, len); + } + + private void getLevelsMemory(boolean mayAllocate, int len) { + Object array = getMemory("Levels", levelsMemory, Byte.TYPE, mayAllocate, len); + levelsMemory = (byte[]) array; + } + + void getLevelsMemory(int len) { + getLevelsMemory(mayAllocateText, len); + } + + private void getRunsMemory(boolean mayAllocate, int len) { + Object array = getMemory("Runs", runsMemory, BidiRun.class, mayAllocate, len); + runsMemory = (BidiRun[]) array; + } + + void getRunsMemory(int len) { + getRunsMemory(mayAllocateRuns, len); + } + + /* additional methods used by constructor - always allow allocation */ + private void getInitialDirPropsMemory(int len) { + getDirPropsMemory(true, len); + } + + private void getInitialLevelsMemory(int len) { + getLevelsMemory(true, len); + } + + private void getInitialRunsMemory(int len) { + getRunsMemory(true, len); + } + + /** + * Is this Bidi object set to perform the inverse Bidi algorithm? + *

+ * Note: calling this method after setting the reordering mode with + * setReorderingMode will return true if the + * reordering mode was set to REORDER_INVERSE_NUMBERS_AS_L, + * false for all other values. + *

+ * + * @return true if the Bidi object is set to perform + * the inverse Bidi algorithm by handling numbers as L. + * + * @see #setInverse + * @see #setReorderingMode + * @see #REORDER_INVERSE_NUMBERS_AS_L + * @stable ICU 3.8 + */ + public boolean isInverse() { + return isInverse; + } + + /* perform (P2)..(P3) ------------------------------------------------------- */ + + /* + * Check that there are enough entries in the arrays paras_limit and paras_level + */ + private void checkParaCount() { + int[] saveLimits; + byte[] saveLevels; + int count = paraCount; + if (count <= paras_level.length) + return; + int oldLength = paras_level.length; + saveLimits = paras_limit; + saveLevels = paras_level; + try { + paras_limit = new int[count * 2]; + paras_level = new byte[count * 2]; + } catch (Exception e) { + throw new OutOfMemoryError("Failed to allocate memory for paras"); + } + System.arraycopy(saveLimits, 0, paras_limit, 0, oldLength); + System.arraycopy(saveLevels, 0, paras_level, 0, oldLength); + } + + /* + * Get the directional properties for the text, calculate the flags bit-set, and + * determine the paragraph level if necessary (in paras_level[i]). 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 final int NOT_SEEKING_STRONG = 0; /* 0: not contextual paraLevel, not after FSI */ + static final int SEEKING_STRONG_FOR_PARA = 1; /* 1: looking for first strong char in para */ + static final int SEEKING_STRONG_FOR_FSI = 2; /* 2: looking for first strong after FSI */ + static final int LOOKING_FOR_PDI = 3; /* 3: found strong after FSI, looking for PDI */ + + private void getDirProps() { + int i = 0, i0, i1; + flags = 0; /* collect all directionalities in the text */ + int uchar; + byte dirProp; + byte defaultParaLevel = 0; /* initialize to avoid compiler warnings */ + boolean isDefaultLevel = IsDefaultLevel(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 + */ + boolean isDefaultLevelInverse = isDefaultLevel && (reorderingMode == REORDER_INVERSE_LIKE_DIRECT + || reorderingMode == REORDER_INVERSE_FOR_NUMBERS_SPECIAL); + lastArabicPos = -1; + int controlCount = 0; + boolean removeBidiControls = (reorderingOptions & OPTION_REMOVE_CONTROLS) != 0; + + byte state; + byte 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 + */ + int[] isolateStartStack = new int[MAX_EXPLICIT_LEVEL + 1]; + /* + * The following stack contains the last known state before encountering the + * initiator of an isolate sequence + */ + byte[] previousStateStack = new byte[MAX_EXPLICIT_LEVEL + 1]; + int stackLast = -1; + + if ((reorderingOptions & OPTION_STREAMING) != 0) + length = 0; + defaultParaLevel = (byte) (paraLevel & 1); + + if (isDefaultLevel) { + paras_level[0] = defaultParaLevel; + lastStrong = defaultParaLevel; + state = SEEKING_STRONG_FOR_PARA; + } else { + paras_level[0] = paraLevel; + state = NOT_SEEKING_STRONG; + } + /* count paragraphs and determine the paragraph level (P2..P3) */ + /* + * see comment on constant fields: the LEVEL_DEFAULT_XXX values are designed so + * that their low-order bit alone yields the intended default + */ + + for (i = 0; i < originalLength; /* i is incremented in the loop */) { + i0 = i; /* index of first code unit */ + uchar = UTF16.charAt(text, 0, originalLength, i); + i += UTF16.getCharCount(uchar); + i1 = i - 1; /* index of last code unit, gets the directional property */ + + dirProp = (byte) getCustomizedClass(uchar); + flags |= DirPropFlag(dirProp); + dirProps[i1] = dirProp; + if (i1 > i0) { /* set previous code units' properties to BN */ + flags |= DirPropFlag(BN); + do { + dirProps[--i1] = BN; + } while (i1 > i0); + } + if (removeBidiControls && IsBidiControlChar(uchar)) { + controlCount++; + } + if (dirProp == L) { + if (state == SEEKING_STRONG_FOR_PARA) { + paras_level[paraCount - 1] = 0; + state = NOT_SEEKING_STRONG; + } else if (state == SEEKING_STRONG_FOR_FSI) { + if (stackLast <= MAX_EXPLICIT_LEVEL) { + /* no need for next statement, already set by default */ + /* dirProps[isolateStartStack[stackLast]] = LRI; */ + flags |= DirPropFlag(LRI); + } + state = LOOKING_FOR_PDI; + } + lastStrong = L; + continue; + } + if (dirProp == R || dirProp == AL) { + if (state == SEEKING_STRONG_FOR_PARA) { + paras_level[paraCount - 1] = 1; + state = NOT_SEEKING_STRONG; + } else if (state == SEEKING_STRONG_FOR_FSI) { + if (stackLast <= MAX_EXPLICIT_LEVEL) { + dirProps[isolateStartStack[stackLast]] = RLI; + flags |= DirPropFlag(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 <= 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 <= MAX_EXPLICIT_LEVEL) { + /* no need for next statement, already set by default */ + /* dirProps[isolateStartStack[stackLast]] = LRI; */ + flags |= DirPropFlag(LRI); + } + } + if (stackLast >= 0) { + if (stackLast <= MAX_EXPLICIT_LEVEL) + state = previousStateStack[stackLast]; + stackLast--; + } + continue; + } + if (dirProp == B) { + if (i < originalLength && uchar == CR && text[i] == LF) /* do nothing on the CR */ + continue; + paras_limit[paraCount - 1] = i; + if (isDefaultLevelInverse && lastStrong == R) + paras_level[paraCount - 1] = 1; + if ((reorderingOptions & OPTION_STREAMING) != 0) { + /* + * When streaming, we only process whole paragraphs thus some updates are only + * done on paragraph boundaries + */ + length = i; /* i is index to next character */ + this.controlCount = controlCount; + } + if (i < originalLength) { /* B not last char in text */ + paraCount++; + checkParaCount(); /* check that there is enough memory for a new para entry */ + if (isDefaultLevel) { + paras_level[paraCount - 1] = defaultParaLevel; + state = SEEKING_STRONG_FOR_PARA; + lastStrong = defaultParaLevel; + } else { + paras_level[paraCount - 1] = paraLevel; + state = NOT_SEEKING_STRONG; + } + stackLast = -1; + } + continue; + } + } + /* +Ignore still open isolate sequences with overflow */ + if (stackLast > MAX_EXPLICIT_LEVEL) { + stackLast = 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 |= DirPropFlag(LRI); + break; + } + state = previousStateStack[stackLast]; + stackLast--; + } + /* When streaming, ignore text after the last paragraph separator */ + if ((reorderingOptions & OPTION_STREAMING) != 0) { + if (length < originalLength) + paraCount--; + } else { + paras_limit[paraCount - 1] = originalLength; + this.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) { + paras_level[paraCount - 1] = 1; + } + if (isDefaultLevel) { + paraLevel = paras_level[0]; + } + /* + * The following is needed to resolve the text direction for default level + * paragraphs containing no strong character + */ + for (i = 0; i < paraCount; i++) + flags |= DirPropFlagLR(paras_level[i]); + + if (orderParagraphsLTR && (flags & DirPropFlag(B)) != 0) { + flags |= DirPropFlag(L); + } + } + + /* determine the paragraph level at position index */ + byte GetParaLevelAt(int pindex) { + if (defaultParaLevel == 0 || pindex < paras_limit[0]) + return paraLevel; + int i; + for (i = 1; i < paraCount; i++) + if (pindex < paras_limit[i]) + break; + if (i >= paraCount) + i = paraCount - 1; + return paras_level[i]; + } + + /* 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. + */ + + private void bracketInit(BracketData bd) { + bd.isoRunLast = 0; + bd.isoRuns[0] = new IsoRun(); + bd.isoRuns[0].start = 0; + bd.isoRuns[0].limit = 0; + bd.isoRuns[0].level = GetParaLevelAt(0); + bd.isoRuns[0].lastStrong = bd.isoRuns[0].lastBase = bd.isoRuns[0].contextDir = (byte) (GetParaLevelAt(0) & 1); + bd.isoRuns[0].contextPos = 0; + bd.openings = new Opening[SIMPLE_PARAS_COUNT]; + bd.isNumbersSpecial = reorderingMode == REORDER_NUMBERS_SPECIAL + || reorderingMode == REORDER_INVERSE_FOR_NUMBERS_SPECIAL; + } + + /* paragraph boundary */ + private void bracketProcessB(BracketData bd, byte level) { + bd.isoRunLast = 0; + bd.isoRuns[0].limit = 0; + bd.isoRuns[0].level = level; + bd.isoRuns[0].lastStrong = bd.isoRuns[0].lastBase = bd.isoRuns[0].contextDir = (byte) (level & 1); + bd.isoRuns[0].contextPos = 0; + } + + /* LRE, LRO, RLE, RLO, PDF */ + private void bracketProcessBoundary(BracketData bd, int lastCcPos, byte contextLevel, byte embeddingLevel) { + IsoRun pLastIsoRun = bd.isoRuns[bd.isoRunLast]; + if ((DirPropFlag(dirProps[lastCcPos]) & MASK_ISO) != 0) /* after an isolate */ + return; + if (NoOverride(embeddingLevel) > NoOverride(contextLevel)) /* not a PDF */ + contextLevel = embeddingLevel; + pLastIsoRun.limit = pLastIsoRun.start; + pLastIsoRun.level = embeddingLevel; + pLastIsoRun.lastStrong = pLastIsoRun.lastBase = pLastIsoRun.contextDir = (byte) (contextLevel & 1); + pLastIsoRun.contextPos = lastCcPos; + } + + /* LRI or RLI */ + private void bracketProcessLRI_RLI(BracketData bd, byte level) { + IsoRun pLastIsoRun = bd.isoRuns[bd.isoRunLast]; + short lastLimit; + pLastIsoRun.lastBase = ON; + lastLimit = pLastIsoRun.limit; + bd.isoRunLast++; + pLastIsoRun = bd.isoRuns[bd.isoRunLast]; + if (pLastIsoRun == null) + pLastIsoRun = bd.isoRuns[bd.isoRunLast] = new IsoRun(); + pLastIsoRun.start = pLastIsoRun.limit = lastLimit; + pLastIsoRun.level = level; + pLastIsoRun.lastStrong = pLastIsoRun.lastBase = pLastIsoRun.contextDir = (byte) (level & 1); + pLastIsoRun.contextPos = 0; + } + + /* PDI */ + private void bracketProcessPDI(BracketData bd) { + IsoRun pLastIsoRun; + bd.isoRunLast--; + pLastIsoRun = bd.isoRuns[bd.isoRunLast]; + pLastIsoRun.lastBase = ON; + } + + /* newly found opening bracket: create an openings entry */ + private void bracketAddOpening(BracketData bd, char match, int position) { + IsoRun pLastIsoRun = bd.isoRuns[bd.isoRunLast]; + Opening pOpening; + if (pLastIsoRun.limit >= bd.openings.length) { /* no available new entry */ + Opening[] saveOpenings = bd.openings; + int count; + try { + count = bd.openings.length; + bd.openings = new Opening[count * 2]; + } catch (Exception e) { + throw new OutOfMemoryError("Failed to allocate memory for openings"); + } + System.arraycopy(saveOpenings, 0, bd.openings, 0, count); + } + pOpening = bd.openings[pLastIsoRun.limit]; + if (pOpening == null) + pOpening = bd.openings[pLastIsoRun.limit] = new Opening(); + pOpening.position = position; + pOpening.match = match; + pOpening.contextDir = pLastIsoRun.contextDir; + pOpening.contextPos = pLastIsoRun.contextPos; + pOpening.flags = 0; + pLastIsoRun.limit++; + } + + /* + * change N0c1 to N0c2 when a preceding bracket is assigned the embedding level + */ + private void fixN0c(BracketData bd, int openingIndex, int newPropPosition, byte newProp) { + /* This function calls itself recursively */ + IsoRun pLastIsoRun = bd.isoRuns[bd.isoRunLast]; + Opening qOpening; + int k, openingPosition, closingPosition; + for (k = openingIndex + 1; k < pLastIsoRun.limit; k++) { + qOpening = bd.openings[k]; + if (qOpening.match >= 0) /* not an N0c match */ + continue; + if (newPropPosition < qOpening.contextPos) + 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; return L or R if N0b or N0c, ON if N0d */ + private byte bracketProcessClosing(BracketData bd, int openIdx, int position) { + IsoRun pLastIsoRun = bd.isoRuns[bd.isoRunLast]; + Opening pOpening, qOpening; + byte direction; + boolean stable; + byte newProp; + pOpening = bd.openings[openIdx]; + direction = (byte) (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) > 0) + || (direction == 1 && (pOpening.flags & FOUND_R) > 0)) { /* N0b */ + newProp = direction; + } else if ((pOpening.flags & (FOUND_L | FOUND_R)) != 0) { /* N0c */ + /* + * it is stable if there is no preceding text or in conditions too complicated + * and not worth checking + */ + stable = (openIdx == pLastIsoRun.start); + if (direction != pOpening.contextDir) + newProp = pOpening.contextDir; /* N0c1 */ + else + newProp = direction; /* N0c2 */ + } else { + /* forget this and any brackets nested within this pair */ + pLastIsoRun.limit = (short) openIdx; + return ON; /* N0d */ + } + dirProps[pOpening.position] = newProp; + dirProps[position] = newProp; + /* Update nested N0c pairs that may be affected */ + fixN0c(bd, openIdx, pOpening.position, newProp); + if (stable) { + pLastIsoRun.limit = (short) 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 { + int 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; k < pLastIsoRun.limit; 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 */ + private void bracketProcessChar(BracketData bd, int position) { + IsoRun pLastIsoRun = bd.isoRuns[bd.isoRunLast]; + byte dirProp, newProp; + byte level; + dirProp = dirProps[position]; + if (dirProp == ON) { + char c, match; + int 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 = 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 = newProp; + pLastIsoRun.contextPos = position; + level = levels[position]; + if ((level & LEVEL_OVERRIDE) != 0) { /* X4, X5 */ + short flag; + int i; + newProp = (byte) (level & 1); + pLastIsoRun.lastStrong = newProp; + flag = (short) DirPropFlag(newProp); + for (i = pLastIsoRun.start; i < idx; i++) + bd.openings[i].flags |= flag; + /* matching brackets are not overridden by LRO/RLO */ + levels[position] &= ~LEVEL_OVERRIDE; + } + /* matching brackets are not overridden by LRO/RLO */ + levels[bd.openings[idx].position] &= ~LEVEL_OVERRIDE; + return; + } + /* + * 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 != 0) { + match = (char) UCharacter.getBidiPairedBracket(c); /* get the matching char */ + } else { + match = 0; + } + if (match != c && /* has a matching char */ + UCharacter.getIntPropertyValue(c, BIDI_PAIRED_BRACKET_TYPE) == + /* opening bracket */ BidiPairedBracketType.OPEN) { + /* + * special case: process synonyms create an opening entry for each synonym + */ + if (match == 0x232A) { /* RIGHT-POINTING ANGLE BRACKET */ + bracketAddOpening(bd, (char) 0x3009, position); + } else if (match == 0x3009) { /* RIGHT ANGLE BRACKET */ + bracketAddOpening(bd, (char) 0x232A, position); + } + bracketAddOpening(bd, match, position); + } + } + level = levels[position]; + if ((level & LEVEL_OVERRIDE) != 0) { /* X4, X5 */ + newProp = (byte) (level & 1); + if (dirProp != S && dirProp != WS && dirProp != ON) + dirProps[position] = newProp; + pLastIsoRun.lastBase = newProp; + pLastIsoRun.lastStrong = newProp; + pLastIsoRun.contextDir = newProp; + pLastIsoRun.contextPos = position; + } else if (dirProp <= R || dirProp == AL) { + newProp = DirFromStrong(dirProp); + pLastIsoRun.lastBase = dirProp; + pLastIsoRun.lastStrong = dirProp; + pLastIsoRun.contextDir = 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 = L; + pLastIsoRun.contextPos = position; + } else { + newProp = R; /* N0 */ + if (pLastIsoRun.lastStrong == AL) + dirProps[position] = AN; /* W2 */ + else + dirProps[position] = ENR; + pLastIsoRun.contextDir = R; + pLastIsoRun.contextPos = position; + } + } else if (dirProp == AN) { + newProp = R; /* N0 */ + pLastIsoRun.lastBase = AN; + pLastIsoRun.contextDir = 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) { + int i; + short flag = (short) DirPropFlag(DirFromStrong(newProp)); + for (i = pLastIsoRun.start; i < pLastIsoRun.limit; i++) + if (position > bd.openings[i].position) + bd.openings[i].flags |= flag; + } + } + + /* perform (X1)..(X9) ------------------------------------------------------- */ + + /* determine if the text is mixed-directional or single-directional */ + private byte directionFromFlags() { + + /* if the text contains AN and neutrals, then some neutrals may become RTL */ + if (!((flags & MASK_RTL) != 0 || ((flags & DirPropFlag(AN)) != 0 && (flags & MASK_POSSIBLE_N) != 0))) { + return LTR; + } else if ((flags & MASK_LTR) == 0) { + return RTL; + } else { + return 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 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 MAX_EXPLICIT_LEVEL is odd. + * + * Returns the direction + * + */ + private byte resolveExplicitLevels() { + int i = 0; + byte dirProp; + byte level = GetParaLevelAt(0); + byte dirct; + isolateCount = 0; + + /* determine if the text is mixed-directional or single-directional */ + dirct = directionFromFlags(); + + /* we may not need to resolve any explicit levels */ + if (dirct != MIXED) { + /* not mixed directionality: levels don't matter - trailingWSStart will be 0 */ + return dirct; + } + + if (reorderingMode > REORDER_LAST_LOGICAL_TO_VISUAL) { + /* inverse BiDi: mixed, but all characters are at the same embedding level */ + /* set all levels to the paragraph level */ + int paraIndex, start, limit; + for (paraIndex = 0; paraIndex < paraCount; paraIndex++) { + if (paraIndex == 0) + start = 0; + else + start = paras_limit[paraIndex - 1]; + limit = paras_limit[paraIndex]; + level = paras_level[paraIndex]; + for (i = start; i < limit; i++) + levels[i] = level; + } + return dirct; /* no bracket matching for inverse BiDi */ + } + if ((flags & (MASK_EXPLICIT | MASK_ISO)) == 0) { + /* no embeddings, set all levels to the paragraph level */ + /* we still have to perform bracket matching */ + int paraIndex, start, limit; + BracketData bracketData = new BracketData(); + bracketInit(bracketData); + for (paraIndex = 0; paraIndex < paraCount; paraIndex++) { + if (paraIndex == 0) + start = 0; + else + start = paras_limit[paraIndex - 1]; + limit = paras_limit[paraIndex]; + level = paras_level[paraIndex]; + for (i = start; i < limit; i++) { + levels[i] = level; + dirProp = dirProps[i]; + if (dirProp == BN) + continue; + if (dirProp == B) { + if ((i + 1) < length) { + if (text[i] == CR && text[i + 1] == LF) + continue; /* skip CR when followed by LF */ + bracketProcessB(bracketData, level); + } + continue; + } + bracketProcessChar(bracketData, i); + } + } + return dirct; + } + /* continue to perform (Xn) */ + + /* + * (X1) level is set for all codes, embeddingLevel keeps track of the push/pop + * operations + */ + /* + * both variables may carry the LEVEL_OVERRIDE flag to indicate the override + * status + */ + byte embeddingLevel = level, newLevel; + byte previousLevel = level; /* previous level for regular (not CC) characters */ + int lastCcPos = 0; /* index of last effective LRx,RLx, PDx */ + + /* + * The following stack remembers the embedding level and the ISOLATE flag of + * level runs. stackLast points to its current entry. + */ + short[] stack = new short[MAX_EXPLICIT_LEVEL + 2]; /* + * we never push anything >= MAX_EXPLICIT_LEVEL but we need + * one more entry as base + */ + int stackLast = 0; + int overflowIsolateCount = 0; + int overflowEmbeddingCount = 0; + int validIsolateCount = 0; + BracketData bracketData = new BracketData(); + bracketInit(bracketData); + stack[0] = level; /* initialize base entry to para level, no override, no isolate */ + + /* recalculate the flags */ + flags = 0; + + for (i = 0; i < length; i++) { + dirProp = dirProps[i]; + switch (dirProp) { + case LRE: + case RLE: + case LRO: + case RLO: + /* (X2, X3, X4, X5) */ + flags |= DirPropFlag(BN); + levels[i] = previousLevel; + if (dirProp == LRE || dirProp == LRO) { + /* least greater even level */ + newLevel = (byte) ((embeddingLevel + 2) & ~(LEVEL_OVERRIDE | 1)); + } else { + /* least greater odd level */ + newLevel = (byte) ((NoOverride(embeddingLevel) + 1) | 1); + } + if (newLevel <= MAX_EXPLICIT_LEVEL && overflowIsolateCount == 0 && overflowEmbeddingCount == 0) { + lastCcPos = i; + embeddingLevel = newLevel; + if (dirProp == LRO || dirProp == RLO) + embeddingLevel |= LEVEL_OVERRIDE; + stackLast++; + stack[stackLast] = embeddingLevel; + /* + * we don't need to set LEVEL_OVERRIDE off for LRE and RLE since this has + * already been done for newLevel which is the source for embeddingLevel. + */ + } else { + if (overflowIsolateCount == 0) + overflowEmbeddingCount++; + } + break; + case PDF: + /* (X7) */ + flags |= DirPropFlag(BN); + levels[i] = previousLevel; + /* handle all the overflow cases first */ + if (overflowIsolateCount > 0) { + break; + } + if (overflowEmbeddingCount > 0) { + overflowEmbeddingCount--; + break; + } + if (stackLast > 0 && stack[stackLast] < ISOLATE) { /* not an isolate entry */ + lastCcPos = i; + stackLast--; + embeddingLevel = (byte) stack[stackLast]; + } + break; + case LRI: + case RLI: + flags |= DirPropFlag(ON) | DirPropFlagLR(embeddingLevel); + levels[i] = NoOverride(embeddingLevel); + if (NoOverride(embeddingLevel) != NoOverride(previousLevel)) { + bracketProcessBoundary(bracketData, lastCcPos, previousLevel, embeddingLevel); + flags |= DirPropFlagMultiRuns; + } + previousLevel = embeddingLevel; + /* (X5a, X5b) */ + if (dirProp == LRI) + /* least greater even level */ + newLevel = (byte) ((embeddingLevel + 2) & ~(LEVEL_OVERRIDE | 1)); + else + /* least greater odd level */ + newLevel = (byte) ((NoOverride(embeddingLevel) + 1) | 1); + if (newLevel <= MAX_EXPLICIT_LEVEL && overflowIsolateCount == 0 && overflowEmbeddingCount == 0) { + flags |= DirPropFlag(dirProp); + lastCcPos = i; + validIsolateCount++; + if (validIsolateCount > isolateCount) + isolateCount = validIsolateCount; + embeddingLevel = newLevel; + /* + * we can increment stackLast without checking because newLevel will exceed + * UBIDI_MAX_EXPLICIT_LEVEL before stackLast overflows + */ + stackLast++; + stack[stackLast] = (short) (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 (NoOverride(embeddingLevel) != NoOverride(previousLevel)) { + bracketProcessBoundary(bracketData, lastCcPos, previousLevel, embeddingLevel); + flags |= DirPropFlagMultiRuns; + } + /* (X6a) */ + if (overflowIsolateCount > 0) { + overflowIsolateCount--; + /* make it WS so that it is handled by adjustWSLevels() */ + dirProps[i] = WS; + } else if (validIsolateCount > 0) { + flags |= DirPropFlag(PDI); + lastCcPos = i; + overflowEmbeddingCount = 0; + while (stack[stackLast] < ISOLATE) /* pop embedding entries */ + stackLast--; /* until the last isolate entry */ + stackLast--; /* pop also the last isolate entry */ + validIsolateCount--; + bracketProcessPDI(bracketData); + } else + /* make it WS so that it is handled by adjustWSLevels() */ + dirProps[i] = WS; + embeddingLevel = (byte) (stack[stackLast] & ~ISOLATE); + flags |= DirPropFlag(ON) | DirPropFlagLR(embeddingLevel); + previousLevel = embeddingLevel; + levels[i] = NoOverride(embeddingLevel); + break; + case B: + flags |= DirPropFlag(B); + levels[i] = GetParaLevelAt(i); + if ((i + 1) < length) { + if (text[i] == CR && text[i + 1] == LF) + break; /* skip CR when followed by LF */ + overflowEmbeddingCount = overflowIsolateCount = 0; + validIsolateCount = 0; + stackLast = 0; + previousLevel = embeddingLevel = GetParaLevelAt(i + 1); + stack[0] = embeddingLevel; /* initialize base entry to para level, no override, no isolate */ + bracketProcessB(bracketData, embeddingLevel); + } + break; + case BN: + /* BN, LRE, RLE, and PDF are supposed to be removed (X9) */ + /* they will get their levels set correctly in adjustWSLevels() */ + levels[i] = previousLevel; + flags |= DirPropFlag(BN); + break; + default: + /* all other types are normal characters and get the "real" level */ + if (NoOverride(embeddingLevel) != NoOverride(previousLevel)) { + bracketProcessBoundary(bracketData, lastCcPos, previousLevel, embeddingLevel); + flags |= DirPropFlagMultiRuns; + if ((embeddingLevel & LEVEL_OVERRIDE) != 0) + flags |= DirPropFlagO(embeddingLevel); + else + flags |= DirPropFlagE(embeddingLevel); + } + previousLevel = embeddingLevel; + levels[i] = embeddingLevel; + bracketProcessChar(bracketData, i); + /* the dirProp may have been changed in bracketProcessChar() */ + flags |= DirPropFlag(dirProps[i]); + break; + } + } + if ((flags & MASK_EMBEDDING) != 0) { + flags |= DirPropFlagLR(paraLevel); + } + if (orderParagraphsLTR && (flags & DirPropFlag(B)) != 0) { + flags |= DirPropFlag(L); + } + /* again, determine if the text is mixed-directional or single-directional */ + dirct = directionFromFlags(); + + return dirct; + } + + /* + * 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. + */ + private byte checkExplicitLevels() { + byte dirProp; + int i; + int isolateCount = 0; + + this.flags = 0; /* collect all directionalities in the text */ + byte level; + this.isolateCount = 0; + + for (i = 0; i < length; ++i) { + if (levels[i] == 0) { + levels[i] = paraLevel; + } + + // for backward compatibility + if (MAX_EXPLICIT_LEVEL < (levels[i] & 0x7f)) { + if ((levels[i] & LEVEL_OVERRIDE) != 0) { + levels[i] = (byte) (paraLevel | LEVEL_OVERRIDE); + } else { + levels[i] = paraLevel; + } + } + + level = levels[i]; + dirProp = dirProps[i]; + if (dirProp == LRI || dirProp == RLI) { + isolateCount++; + if (isolateCount > this.isolateCount) + this.isolateCount = isolateCount; + } else if (dirProp == PDI) { + isolateCount--; + } else if (dirProp == B) { + isolateCount = 0; + } + if ((level & LEVEL_OVERRIDE) != 0) { + /* keep the override flag in levels[i] but adjust the flags */ + level &= ~LEVEL_OVERRIDE; /* make the range check below simpler */ + flags |= DirPropFlagO(level); + } else { + /* set the flags */ + flags |= DirPropFlagE(level) | DirPropFlag(dirProp); + } + if ((level < GetParaLevelAt(i) && !((0 == level) && (dirProp == B))) || (MAX_EXPLICIT_LEVEL < level)) { + /* level out of bounds */ + throw new IllegalArgumentException("level " + level + " out of bounds at " + i); + } + } + if ((flags & MASK_EMBEDDING) != 0) { + flags |= DirPropFlagLR(paraLevel); + } + /* determine if the text is mixed-directional or single-directional */ + return directionFromFlags(); + } + + /*********************************************************************/ + /* 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 "_(x,y)" where x represents an action */ + /* to perform and y represents the next state. */ + /* */ + /*********************************************************************/ + + /* Definitions and type for properties state tables */ + /*********************************************************************/ + private static final int IMPTABPROPS_COLUMNS = 16; + private static final int IMPTABPROPS_RES = IMPTABPROPS_COLUMNS - 1; + + private static short GetStateProps(short cell) { + return (short) (cell & 0x1f); + } + + private static short GetActionProps(short cell) { + return (short) (cell >> 5); + } + + private static final short 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 }; + private static final short _L = 0; + private static final short _R = 1; + private static final short _EN = 2; + private static final short _AN = 3; + private static final short _ON = 4; + private static final short _S = 5; + private static final short _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. */ + /* */ + /* */ + private static final short impTabProps[][] = { + /* 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, _ON }, + /* 1 L */ { 1, 32 + 2, 32 + 4, 32 + 5, 32 + 7, 32 + 15, 32 + 17, 32 + 7, 32 + 9, 32 + 7, 1, 1, 32 + 3, + 32 + 18, 32 + 21, _L }, + /* 2 R */ { 32 + 1, 2, 32 + 4, 32 + 5, 32 + 7, 32 + 15, 32 + 17, 32 + 7, 32 + 9, 32 + 7, 2, 2, 32 + 3, + 32 + 18, 32 + 21, _R }, + /* 3 AL */ { 32 + 1, 32 + 2, 32 + 6, 32 + 6, 32 + 8, 32 + 16, 32 + 17, 32 + 8, 32 + 8, 32 + 8, 3, 3, 3, + 32 + 18, 32 + 21, _R }, + /* 4 EN */ { 32 + 1, 32 + 2, 4, 32 + 5, 32 + 7, 32 + 15, 32 + 17, 64 + 10, 11, 64 + 10, 4, 4, 32 + 3, 18, + 21, _EN }, + /* 5 AN */ { 32 + 1, 32 + 2, 32 + 4, 5, 32 + 7, 32 + 15, 32 + 17, 32 + 7, 32 + 9, 64 + 12, 5, 5, 32 + 3, + 32 + 18, 32 + 21, _AN }, + /* 6 AL:EN/AN */ { 32 + 1, 32 + 2, 6, 6, 32 + 8, 32 + 16, 32 + 17, 32 + 8, 32 + 8, 64 + 13, 6, 6, 32 + 3, + 18, 21, _AN }, + /* 7 ON */ { 32 + 1, 32 + 2, 32 + 4, 32 + 5, 7, 32 + 15, 32 + 17, 7, 64 + 14, 7, 7, 7, 32 + 3, 32 + 18, + 32 + 21, _ON }, + /* 8 AL:ON */ { 32 + 1, 32 + 2, 32 + 6, 32 + 6, 8, 32 + 16, 32 + 17, 8, 8, 8, 8, 8, 32 + 3, 32 + 18, + 32 + 21, _ON }, + /* 9 ET */ { 32 + 1, 32 + 2, 4, 32 + 5, 7, 32 + 15, 32 + 17, 7, 9, 7, 9, 9, 32 + 3, 18, 21, _ON }, + /* 10 EN+ES/CS */ { 96 + 1, 96 + 2, 4, 96 + 5, 128 + 7, 96 + 15, 96 + 17, 128 + 7, 128 + 14, 128 + 7, 10, + 128 + 7, 96 + 3, 18, 21, _EN }, + /* 11 EN+ET */ { 32 + 1, 32 + 2, 4, 32 + 5, 32 + 7, 32 + 15, 32 + 17, 32 + 7, 11, 32 + 7, 11, 11, 32 + 3, + 18, 21, _EN }, + /* 12 AN+CS */ { 96 + 1, 96 + 2, 96 + 4, 5, 128 + 7, 96 + 15, 96 + 17, 128 + 7, 128 + 14, 128 + 7, 12, + 128 + 7, 96 + 3, 96 + 18, 96 + 21, _AN }, + /* 13 AL:EN/AN+CS */ { 96 + 1, 96 + 2, 6, 6, 128 + 8, 96 + 16, 96 + 17, 128 + 8, 128 + 8, 128 + 8, 13, + 128 + 8, 96 + 3, 18, 21, _AN }, + /* 14 ON+ET */ { 32 + 1, 32 + 2, 128 + 4, 32 + 5, 7, 32 + 15, 32 + 17, 7, 14, 7, 14, 14, 32 + 3, 128 + 18, + 128 + 21, _ON }, + /* 15 S */ { 32 + 1, 32 + 2, 32 + 4, 32 + 5, 32 + 7, 15, 32 + 17, 32 + 7, 32 + 9, 32 + 7, 15, 32 + 7, + 32 + 3, 32 + 18, 32 + 21, _S }, + /* 16 AL:S */ { 32 + 1, 32 + 2, 32 + 6, 32 + 6, 32 + 8, 16, 32 + 17, 32 + 8, 32 + 8, 32 + 8, 16, 32 + 8, + 32 + 3, 32 + 18, 32 + 21, _S }, + /* 17 B */ { 32 + 1, 32 + 2, 32 + 4, 32 + 5, 32 + 7, 32 + 15, 17, 32 + 7, 32 + 9, 32 + 7, 17, 32 + 7, + 32 + 3, 32 + 18, 32 + 21, _B }, + /* 18 ENL */ { 32 + 1, 32 + 2, 18, 32 + 5, 32 + 7, 32 + 15, 32 + 17, 64 + 19, 20, 64 + 19, 18, 18, 32 + 3, + 18, 21, _L }, + /* 19 ENL+ES/CS */ { 96 + 1, 96 + 2, 18, 96 + 5, 128 + 7, 96 + 15, 96 + 17, 128 + 7, 128 + 14, 128 + 7, 19, + 128 + 7, 96 + 3, 18, 21, _L }, + /* 20 ENL+ET */ { 32 + 1, 32 + 2, 18, 32 + 5, 32 + 7, 32 + 15, 32 + 17, 32 + 7, 20, 32 + 7, 20, 20, 32 + 3, + 18, 21, _L }, + /* 21 ENR */ { 32 + 1, 32 + 2, 21, 32 + 5, 32 + 7, 32 + 15, 32 + 17, 64 + 22, 23, 64 + 22, 21, 21, 32 + 3, + 18, 21, _AN }, + /* 22 ENR+ES/CS */ { 96 + 1, 96 + 2, 21, 96 + 5, 128 + 7, 96 + 15, 96 + 17, 128 + 7, 128 + 14, 128 + 7, 22, + 128 + 7, 96 + 3, 18, 21, _AN }, + /* 23 ENR+ET */ { 32 + 1, 32 + 2, 21, 32 + 5, 32 + 7, 32 + 15, 32 + 17, 32 + 7, 23, 32 + 7, 23, 23, 32 + 3, + 18, 21, _AN } }; + + /*********************************************************************/ + /* 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 "_(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 */ + /*********************************************************************/ + private static final int IMPTABLEVELS_COLUMNS = _B + 2; + private static final int IMPTABLEVELS_RES = IMPTABLEVELS_COLUMNS - 1; + + private static short GetState(byte cell) { + return (short) (cell & 0x0f); + } + + private static short GetAction(byte cell) { + return (short) (cell >> 4); + } + + private static class ImpTabPair { + byte[][][] imptab; + short[][] impact; + + ImpTabPair(byte[][] table1, byte[][] table2, short[] act1, short[] act2) { + imptab = new byte[][][] { table1, table2 }; + impact = new short[][] { act1, act2 }; + } + } + + /*********************************************************************/ + /* */ + /* 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(). */ + /* */ + + private static final byte 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, 0x14, 0x14, 0, 1 }, + /* 2 : AN */ { 0, 1, 0, 2, 0x15, 0x15, 0, 2 }, /* 3 : R+EN/AN */ { 0, 1, 3, 3, 0x14, 0x14, 0, 2 }, + /* 4 : R+ON */ { 0, 0x21, 0x33, 0x33, 4, 4, 0, 0 }, + /* 5 : AN+ON */ { 0, 0x21, 0, 0x32, 5, 5, 0, 0 } }; + + private static final byte 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, 0x14, 0x14, 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 */ { 0x21, 0, 0x21, 3, 4, 4, 0, 0 }, /* 5 : L+AN+ON */ { 1, 0, 1, 3, 5, 5, 0, 0 } }; + + private static final short[] impAct0 = { 0, 1, 2, 3, 4 }; + + private static final ImpTabPair impTab_DEFAULT = new ImpTabPair(impTabL_DEFAULT, impTabR_DEFAULT, impAct0, impAct0); + + private static final byte 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, 0x11, 0x11, 0, 0, 0, 0 }, /* 1 : L+EN/AN */ { 0, 0x42, 1, 1, 0, 0, 0, 0 }, + /* 2 : R */ { 0, 2, 4, 4, 0x13, 0x13, 0, 1 }, /* 3 : R+ON */ { 0, 0x22, 0x34, 0x34, 3, 3, 0, 0 }, + /* 4 : R+EN/AN */ { 0, 2, 4, 4, 0x13, 0x13, 0, 2 } }; + private static final ImpTabPair impTab_NUMBERS_SPECIAL = new ImpTabPair(impTabL_NUMBERS_SPECIAL, impTabR_DEFAULT, + impAct0, impAct0); + + private static final byte 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, 0x11, 0x11, 0, 0, 0, 0 }, /* 1 EN/AN */ { 0x20, 3, 1, 1, 2, 0x20, 0x20, 2 }, + /* 2 EN/AN+ON */ { 0x20, 3, 1, 1, 2, 0x20, 0x20, 1 }, /* 3 R */ { 0, 3, 5, 5, 0x14, 0, 0, 1 }, + /* 4 R+ON */ { 0x20, 3, 5, 5, 4, 0x20, 0x20, 1 }, /* 5 R+EN/AN */ { 0, 3, 5, 5, 0x14, 0, 0, 2 } }; + private static final byte 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, 0x14, 0x14, 0x13, 0, 0, 1 }, /* 3 L+ON */ { 0x22, 0, 4, 4, 3, 0, 0, 0 }, + /* 4 L+EN/AN */ { 0x22, 0, 4, 4, 3, 0, 0, 1 } }; + private static final ImpTabPair impTab_GROUP_NUMBERS_WITH_R = new ImpTabPair(impTabL_GROUP_NUMBERS_WITH_R, + impTabR_GROUP_NUMBERS_WITH_R, impAct0, impAct0); + + private static final byte 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, 0x14, 0x14, 0, 1 }, + /* 2 : AN */ { 0, 1, 0, 0, 0x15, 0x15, 0, 2 }, /* 3 : R+EN/AN */ { 0, 1, 0, 0, 0x14, 0x14, 0, 2 }, + /* 4 : R+ON */ { 0x20, 1, 0x20, 0x20, 4, 4, 0x20, 1 }, + /* 5 : AN+ON */ { 0x20, 1, 0x20, 0x20, 5, 5, 0x20, 1 } }; + private static final byte 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, 0x14, 0x14, 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 */ { 0x21, 0, 0x21, 0x21, 4, 4, 0, 0 }, /* 5 : L+AN+ON */ { 1, 0, 1, 1, 5, 5, 0, 0 } }; + private static final ImpTabPair impTab_INVERSE_NUMBERS_AS_L = new ImpTabPair(impTabL_INVERSE_NUMBERS_AS_L, + impTabR_INVERSE_NUMBERS_AS_L, impAct0, impAct0); + + private static final byte 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, 0x13, 0x13, 0, 1 }, + /* 2 : EN/AN */ { 1, 0, 2, 2, 0, 0, 0, 1 }, /* 3 : L+ON */ { 0x21, 0x30, 6, 4, 3, 3, 0x30, 0 }, + /* 4 : L+ON+AN */ { 0x21, 0x30, 6, 4, 5, 5, 0x30, 3 }, + /* 5 : L+AN+ON */ { 0x21, 0x30, 6, 4, 5, 5, 0x30, 2 }, + /* 6 : L+ON+EN */ { 0x21, 0x30, 6, 4, 3, 3, 0x30, 1 } }; + private static final short[] impAct1 = { 0, 1, 13, 14 }; + private static final ImpTabPair impTab_INVERSE_LIKE_DIRECT = new ImpTabPair(impTabL_DEFAULT, + impTabR_INVERSE_LIKE_DIRECT, impAct0, impAct1); + + private static final byte 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, 0x63, 0, 1, 0, 0, 0, 0 }, /* 1 : L+AN */ { 0, 0x63, 0, 1, 0x12, 0x30, 0, 4 }, + /* 2 : L+AN+ON */ { 0x20, 0x63, 0x20, 1, 2, 0x30, 0x20, 3 }, + /* 3 : R */ { 0, 0x63, 0x55, 0x56, 0x14, 0x30, 0, 3 }, + /* 4 : R+ON */ { 0x30, 0x43, 0x55, 0x56, 4, 0x30, 0x30, 3 }, + /* 5 : R+EN */ { 0x30, 0x43, 5, 0x56, 0x14, 0x30, 0x30, 4 }, + /* 6 : R+AN */ { 0x30, 0x43, 0x55, 6, 0x14, 0x30, 0x30, 4 } }; + private static final byte 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 */ { 0x13, 0, 1, 1, 0, 0, 0, 0 }, /* 1 : R+EN/AN */ { 0x23, 0, 1, 1, 2, 0x40, 0, 1 }, + /* 2 : R+EN/AN+ON */ { 0x23, 0, 1, 1, 2, 0x40, 0, 0 }, /* 3 : L */ { 3, 0, 3, 0x36, 0x14, 0x40, 0, 1 }, + /* 4 : L+ON */ { 0x53, 0x40, 5, 0x36, 4, 0x40, 0x40, 0 }, + /* 5 : L+ON+EN */ { 0x53, 0x40, 5, 0x36, 4, 0x40, 0x40, 1 }, + /* 6 : L+AN */ { 0x53, 0x40, 6, 6, 4, 0x40, 0x40, 3 } }; + private static final short[] impAct2 = { 0, 1, 2, 5, 6, 7, 8 }; + private static final short[] impAct3 = { 0, 1, 9, 10, 11, 12 }; + private static final ImpTabPair impTab_INVERSE_LIKE_DIRECT_WITH_MARKS = new ImpTabPair( + impTabL_INVERSE_LIKE_DIRECT_WITH_MARKS, impTabR_INVERSE_LIKE_DIRECT_WITH_MARKS, impAct2, impAct3); + + private static final ImpTabPair impTab_INVERSE_FOR_NUMBERS_SPECIAL = new ImpTabPair(impTabL_NUMBERS_SPECIAL, + impTabR_INVERSE_LIKE_DIRECT, impAct0, impAct1); + + private static final byte 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, 0x62, 1, 1, 0, 0, 0, 0 }, /* 1 : L+EN/AN */ { 0, 0x62, 1, 1, 0, 0x30, 0, 4 }, + /* 2 : R */ { 0, 0x62, 0x54, 0x54, 0x13, 0x30, 0, 3 }, + /* 3 : R+ON */ { 0x30, 0x42, 0x54, 0x54, 3, 0x30, 0x30, 3 }, + /* 4 : R+EN/AN */ { 0x30, 0x42, 4, 4, 0x13, 0x30, 0x30, 4 } }; + private static final ImpTabPair impTab_INVERSE_FOR_NUMBERS_SPECIAL_WITH_MARKS = new ImpTabPair( + impTabL_INVERSE_FOR_NUMBERS_SPECIAL_WITH_MARKS, impTabR_INVERSE_LIKE_DIRECT_WITH_MARKS, impAct2, impAct3); + + private static class LevState { + byte[][] impTab; /* level table pointer */ + short[] impAct; /* action map array */ + int startON; /* start of ON sequence */ + int startL2EN; /* start of level 2 sequence */ + int lastStrongRTL; /* index of last found R or AL */ + int runStart; /* start position of the run */ + short state; /* current state */ + byte runLevel; /* run level before implicit solving */ + } + + /*------------------------------------------------------------------------*/ + + static final int FIRSTALLOC = 10; + + /* + * param pos: position where to insert param flag: one of LRM_BEFORE, LRM_AFTER, + * RLM_BEFORE, RLM_AFTER + */ + private void addPoint(int pos, int flag) { + Point point = new Point(); + + int len = insertPoints.points.length; + if (len == 0) { + insertPoints.points = new Point[FIRSTALLOC]; + len = FIRSTALLOC; + } + if (insertPoints.size >= len) { /* no room for new point */ + Point[] savePoints = insertPoints.points; + insertPoints.points = new Point[len * 2]; + System.arraycopy(savePoints, 0, insertPoints.points, 0, len); + } + point.pos = pos; + point.flag = flag; + insertPoints.points[insertPoints.size] = point; + insertPoints.size++; + } + + private void setLevelsOutsideIsolates(int start, int limit, byte level) { + byte dirProp; + int isolateCount = 0, k; + for (k = start; k < limit; k++) { + dirProp = dirProps[k]; + if (dirProp == PDI) + isolateCount--; + if (isolateCount == 0) { + levels[k] = level; + } + if (dirProp == LRI || dirProp == RLI) + isolateCount++; + } + } + + /* perform rules (Wn), (Nn), and (In) on a run of the text ------------------ */ + + /* + * This implementation of the (Wn) rules applies all rules in one pass. In order + * to do so, it needs a look-ahead of typically 1 character (except for W5: + * sequences of ET) and keeps track of changes in a rule Wp that affect a later + * Wq (p= 0) { + addPoint(levState.startL2EN, LRM_BEFORE); + } + levState.startL2EN = -1; /* not within previous if since could also be -2 */ + /* check if we had any relevant EN/AN after R/AL */ + if ((insertPoints.points.length == 0) || (insertPoints.size <= insertPoints.confirmed)) { + /* nothing, just clean up */ + levState.lastStrongRTL = -1; + /* check if we have a pending conditional segment */ + level = impTab[oldStateSeq][IMPTABLEVELS_RES]; + if ((level & 1) != 0 && levState.startON > 0) { /* after ON */ + start = levState.startON; /* reset to basic run level */ + } + if (_prop == _S) { /* add LRM before S */ + addPoint(start0, LRM_BEFORE); + insertPoints.confirmed = insertPoints.size; + } + break; + } + /* reset previous RTL cont to level for LTR text */ + for (k = levState.lastStrongRTL + 1; k < start0; k++) { + /* reset odd level, leave runLevel+2 as is */ + levels[k] = (byte) ((levels[k] - 2) & ~1); + } + /* mark insert points as confirmed */ + insertPoints.confirmed = insertPoints.size; + levState.lastStrongRTL = -1; + if (_prop == _S) { /* add LRM before S */ + addPoint(start0, LRM_BEFORE); + insertPoints.confirmed = insertPoints.size; + } + break; + + case 6: /* R/AL after possible relevant EN/AN */ + /* just clean up */ + if (insertPoints.points.length > 0) + /* remove all non confirmed insert points */ + insertPoints.size = insertPoints.confirmed; + levState.startON = -1; + levState.startL2EN = -1; + levState.lastStrongRTL = limit - 1; + break; + + case 7: /* EN/AN after R/AL + possible cont */ + /* check for real AN */ + + if ((_prop == _AN) && (dirProps[start0] == AN) + && (reorderingMode != REORDER_INVERSE_FOR_NUMBERS_SPECIAL)) { + /* real AN */ + if (levState.startL2EN == -1) { /* if no relevant EN already found */ + /* just note the rightmost digit as a strong RTL */ + levState.lastStrongRTL = limit - 1; + break; + } + if (levState.startL2EN >= 0) { /* after EN, no AN */ + addPoint(levState.startL2EN, LRM_BEFORE); + levState.startL2EN = -2; + } + /* note AN */ + addPoint(start0, LRM_BEFORE); + break; + } + /* if first EN/AN after R/AL */ + if (levState.startL2EN == -1) { + levState.startL2EN = start0; + } + break; + + case 8: /* note location of latest R/AL */ + levState.lastStrongRTL = limit - 1; + levState.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) == 0); k--) { + } + if (k >= 0) { + addPoint(k, RLM_BEFORE); /* add RLM before */ + insertPoints.confirmed = insertPoints.size; /* confirm it */ + } + levState.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(start0, LRM_BEFORE); /* add LRM before */ + addPoint(start0, LRM_AFTER); /* add LRM after */ + break; + + case 11: /* R after L+ON/EN/AN */ + /* false alert, infirm LRMs around previous AN */ + insertPoints.size = insertPoints.confirmed; + if (_prop == _S) { /* add RLM before S */ + addPoint(start0, RLM_BEFORE); + insertPoints.confirmed = insertPoints.size; + } + break; + + case 12: /* L after L+ON/AN */ + level = (byte) (levState.runLevel + addLevel); + for (k = levState.startON; k < start0; k++) { + if (levels[k] < level) { + levels[k] = level; + } + } + insertPoints.confirmed = insertPoints.size; /* confirm inserts */ + levState.startON = start0; + break; + + case 13: /* L after L+ON+EN/AN/ON */ + level = levState.runLevel; + for (k = start0 - 1; k >= levState.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] = (byte) (level + 1); + } + break; + + case 14: /* R after L+ON+EN/AN/ON */ + level = (byte) (levState.runLevel + 1); + for (k = start0 - 1; k >= levState.startON; k--) { + if (levels[k] > level) { + levels[k] -= 2; + } + } + break; + + default: /* we should never get here */ + throw new IllegalStateException("Internal ICU error in processPropertySeq"); + } + } + if ((addLevel) != 0 || (start < start0)) { + level = (byte) (levState.runLevel + addLevel); + if (start >= levState.runStart) { + for (k = start; k < limit; k++) { + levels[k] = level; + } + } else { + setLevelsOutsideIsolates(start, limit, level); + } + } + } + + private void resolveImplicitLevels(int start, int limit, short sor, short eor) { + byte dirProp; + LevState levState = new LevState(); + int i, start1, start2; + short oldStateImp, stateImp, actionImp; + short gprop, resProp, cell; + boolean inverseRTL; + short nextStrongProp = R; + int 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 = ((start < lastArabicPos) && ((GetParaLevelAt(start) & 1) > 0) + && (reorderingMode == REORDER_INVERSE_LIKE_DIRECT + || reorderingMode == REORDER_INVERSE_FOR_NUMBERS_SPECIAL)); + /* initialize for property and levels state table */ + 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 = levels[start]; + levState.impTab = impTabPair.imptab[levState.runLevel & 1]; + levState.impAct = impTabPair.impact[levState.runLevel & 1]; + + /* + * 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) { + levState.startON = isolates[isolateCount].startON; + start1 = isolates[isolateCount].start1; + stateImp = isolates[isolateCount].stateImp; + levState.state = isolates[isolateCount].state; + isolateCount--; + } else { + levState.startON = -1; + start1 = start; + if (dirProps[start] == NSM) + stateImp = (short) (1 + sor); + else + stateImp = 0; + levState.state = 0; + processPropertySeq(levState, sor, start, start); + } + start2 = start; /* to make the Java compiler happy */ + + for (i = start; i <= limit; i++) { + if (i >= limit) { + int k; + for (k = limit - 1; k > start && (DirPropFlag(dirProps[k]) & MASK_BN_EXPLICIT) != 0; k--) + ; + dirProp = dirProps[k]; + if (dirProp == LRI || dirProp == RLI) + break; /* no forced closing for sequence ending with LRI/RLI */ + gprop = eor; + } else { + byte prop, prop1; + prop = dirProps[i]; + if (prop == B) + 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) */ + int j; + nextStrongProp = R; /* set default */ + nextStrongPos = limit; + for (j = i + 1; j < limit; j++) { + prop1 = dirProps[j]; + if (prop1 == L || prop1 == R || prop1 == AL) { + nextStrongProp = prop1; + nextStrongPos = j; + break; + } + } + } + if (nextStrongProp == AL) { + prop = AN; + } + } + } + gprop = groupProp[prop]; + } + oldStateImp = stateImp; + cell = impTabProps[oldStateImp][gprop]; + stateImp = GetStateProps(cell); /* isolate the new state */ + actionImp = GetActionProps(cell); /* isolate the action */ + if ((i == limit) && (actionImp == 0)) { + /* there is an unprocessed sequence if its property == eor */ + actionImp = 1; /* process the last sequence */ + } + if (actionImp != 0) { + resProp = impTabProps[oldStateImp][IMPTABPROPS_RES]; + switch (actionImp) { + case 1: /* process current seq1, init new seq1 */ + processPropertySeq(levState, resProp, start1, i); + start1 = i; + break; + case 2: /* init new seq2 */ + start2 = i; + break; + case 3: /* process seq1, process seq2, init new seq1 */ + processPropertySeq(levState, resProp, start1, start2); + processPropertySeq(levState, _ON, start2, i); + start1 = i; + break; + case 4: /* process seq1, set seq1=seq2, init new seq2 */ + processPropertySeq(levState, resProp, start1, start2); + start1 = start2; + start2 = i; + break; + default: /* we should never get here */ + throw new IllegalStateException("Internal ICU error in resolveImplicitLevels"); + } + } + } + + /* look for the last char not a BN or LRE/RLE/LRO/RLO/PDF */ + for (i = limit - 1; i > start && (DirPropFlag(dirProps[i]) & MASK_BN_EXPLICIT) != 0; i--) + ; + dirProp = dirProps[i]; + if ((dirProp == LRI || dirProp == RLI) && limit < length) { + isolateCount++; + if (isolates[isolateCount] == null) + isolates[isolateCount] = new Isolate(); + isolates[isolateCount].stateImp = stateImp; + isolates[isolateCount].state = levState.state; + isolates[isolateCount].start1 = start1; + isolates[isolateCount].startON = levState.startON; + } else + processPropertySeq(levState, eor, limit, limit); + } + + /* perform (L1) and (X9) ---------------------------------------------------- */ + + /* + * Reset the embedding levels for some non-graphic characters (L1). This method + * also sets appropriate levels for BN, and explicit embedding types that are + * supposed to have been removed from the paragraph in (X9). + */ + private void adjustWSLevels() { + int i; + + if ((flags & MASK_WS) != 0) { + int flag; + i = trailingWSStart; + while (i > 0) { + /* reset a sequence of WS/BN before eop and B/S to the paragraph paraLevel */ + while (i > 0 && ((flag = DirPropFlag(dirProps[--i])) & MASK_WS) != 0) { + if (orderParagraphsLTR && (flag & DirPropFlag(B)) != 0) { + levels[i] = 0; + } else { + levels[i] = GetParaLevelAt(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 = DirPropFlag(dirProps[--i]); + if ((flag & MASK_BN_EXPLICIT) != 0) { + levels[i] = levels[i + 1]; + } else if (orderParagraphsLTR && (flag & DirPropFlag(B)) != 0) { + levels[i] = 0; + break; + } else if ((flag & MASK_B_S) != 0) { + levels[i] = GetParaLevelAt(i); + break; + } + } + } + } + } + + private void setParaSuccess() { + paraBidi = this; /* mark successful setPara */ + } + + private int Bidi_Min(int x, int y) { + return x < y ? x : y; + } + + private int Bidi_Abs(int x) { + return x >= 0 ? x : -x; + } + + void setParaRunsOnly(char[] parmText, byte parmParaLevel) { + int[] visualMap; + String visualText; + int saveLength, saveTrailingWSStart; + byte[] saveLevels; + byte saveDirection; + int i, j, visualStart, logicalStart, oldRunCount, runLength, addedRuns, insertRemove, start, limit, step, + indexOddBit, logicalPos, index, index1; + int saveOptions; + + reorderingMode = REORDER_DEFAULT; + int parmLength = parmText.length; + if (parmLength == 0) { + setPara(parmText, parmParaLevel, null); + reorderingMode = REORDER_RUNS_ONLY; + return; + } + /* obtain memory for mapping table and visual text */ + saveOptions = reorderingOptions; + if ((saveOptions & OPTION_INSERT_MARKS) > 0) { + reorderingOptions &= ~OPTION_INSERT_MARKS; + reorderingOptions |= OPTION_REMOVE_CONTROLS; + } + parmParaLevel &= 1; /* accept only 0 or 1 */ + setPara(parmText, parmParaLevel, null); + /* + * we cannot access directly levels since it is not yet set if direction is not + * MIXED + */ + saveLevels = new byte[this.length]; + System.arraycopy(getLevels(), 0, saveLevels, 0, this.length); + saveTrailingWSStart = trailingWSStart; + + /* + * 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 setPara (but must make + * provision for possible removal of Bidi controls. Alternatively, only use the + * dirProps array via customized classifier callback. + */ + visualText = writeReordered(DO_MIRRORING); + visualMap = getVisualMap(); + this.reorderingOptions = saveOptions; + saveLength = this.length; + saveDirection = this.direction; + + this.reorderingMode = REORDER_INVERSE_LIKE_DIRECT; + parmParaLevel ^= 1; + setPara(visualText, parmParaLevel, null); + BidiLine.getRuns(this); + /* check if some runs must be split, count how many splits */ + addedRuns = 0; + oldRunCount = this.runCount; + visualStart = 0; + for (i = 0; i < oldRunCount; i++, visualStart += runLength) { + runLength = runs[i].limit - visualStart; + if (runLength < 2) { + continue; + } + logicalStart = runs[i].start; + for (j = logicalStart + 1; j < logicalStart + runLength; j++) { + index = visualMap[j]; + index1 = visualMap[j - 1]; + if ((Bidi_Abs(index - index1) != 1) || (saveLevels[index] != saveLevels[index1])) { + addedRuns++; + } + } + } + if (addedRuns > 0) { + getRunsMemory(oldRunCount + addedRuns); + if (runCount == 1) { + /* because we switch from UBiDi.simpleRuns to UBiDi.runs */ + runsMemory[0] = runs[0]; + } else { + System.arraycopy(runs, 0, runsMemory, 0, runCount); + } + runs = runsMemory; + runCount += addedRuns; + for (i = oldRunCount; i < runCount; i++) { + if (runs[i] == null) { + runs[i] = new BidiRun(0, 0, (byte) 0); + } + } + } + /* split runs which are not consecutive in source text */ + int newI; + for (i = oldRunCount - 1; i >= 0; i--) { + newI = i + addedRuns; + runLength = i == 0 ? runs[0].limit : runs[i].limit - runs[i - 1].limit; + logicalStart = runs[i].start; + indexOddBit = runs[i].level & 1; + if (runLength < 2) { + if (addedRuns > 0) { + runs[newI].copyFrom(runs[i]); + } + logicalPos = visualMap[logicalStart]; + runs[newI].start = logicalPos; + runs[newI].level = (byte) (saveLevels[logicalPos] ^ indexOddBit); + continue; + } + if (indexOddBit > 0) { + start = logicalStart; + limit = logicalStart + runLength - 1; + step = 1; + } else { + start = logicalStart + runLength - 1; + limit = logicalStart; + step = -1; + } + for (j = start; j != limit; j += step) { + index = visualMap[j]; + index1 = visualMap[j + step]; + if ((Bidi_Abs(index - index1) != 1) || (saveLevels[index] != saveLevels[index1])) { + logicalPos = Bidi_Min(visualMap[start], index); + runs[newI].start = logicalPos; + runs[newI].level = (byte) (saveLevels[logicalPos] ^ indexOddBit); + runs[newI].limit = runs[i].limit; + runs[i].limit -= Bidi_Abs(j - start) + 1; + insertRemove = runs[i].insertRemove & (LRM_AFTER | RLM_AFTER); + runs[newI].insertRemove = insertRemove; + runs[i].insertRemove &= ~insertRemove; + start = j + step; + addedRuns--; + newI--; + } + } + if (addedRuns > 0) { + runs[newI].copyFrom(runs[i]); + } + logicalPos = Bidi_Min(visualMap[start], visualMap[limit]); + runs[newI].start = logicalPos; + runs[newI].level = (byte) (saveLevels[logicalPos] ^ indexOddBit); + } + + cleanup1: + /* restore initial paraLevel */ + this.paraLevel ^= 1; + cleanup2: + /* restore real text */ + this.text = parmText; + this.length = saveLength; + this.originalLength = parmLength; + this.direction = saveDirection; + this.levels = saveLevels; + this.trailingWSStart = saveTrailingWSStart; + if (runCount > 1) { + this.direction = MIXED; + } + cleanup3: this.reorderingMode = REORDER_RUNS_ONLY; + } + + /** + * Perform the Unicode Bidi algorithm. It is defined in the + * Unicode Standard Annex #9: + * Unicode Bidirectional Algorithm, version 13, also described in The + * Unicode Standard, Version 4.0 . + *

+ * + * This method 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 method 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. + * + * Although the text is passed here as a String, it is stored + * internally as an array of characters. Therefore the documentation will refer + * to indexes of the characters in the text. + * + * @param text contains the text that the Bidi algorithm will be + * performed on. This text can be retrieved with + * getText() or + * getTextAsString.
+ * + * @param paraLevel specifies the default level for the text; it is + * typically 0 (LTR) or 1 (RTL). If the method shall + * determine the paragraph level from the text, then + * paraLevel can be set to either + * LEVEL_DEFAULT_LTR or + * LEVEL_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 + * 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 LEVEL_OVERRIDE bit set.
+ *
+ * Except for that bit, it must be + * paraLevel<=embeddingLevels[]<=MAX_EXPLICIT_LEVEL, + * with one exception: a level of zero may be specified + * for a paragraph separator even if + * paraLevel>0 when multiple paragraphs + * are submitted in the same call to + * setPara().
+ *
+ * Caution: A reference to this array, + * not a copy of the levels, will be stored in the + * Bidi object; the + * embeddingLevels should not be modified to + * avoid unexpected results on subsequent Bidi + * operations. However, the setPara() and + * setLine() methods may modify some or all + * of the levels.
+ *
+ * Note: the + * embeddingLevels array must have one entry + * for each character in text. + * + * @throws IllegalArgumentException if the values in embeddingLevels are not + * within the allowed range + * + * @see #LEVEL_DEFAULT_LTR + * @see #LEVEL_DEFAULT_RTL + * @see #LEVEL_OVERRIDE + * @see #MAX_EXPLICIT_LEVEL + * @stable ICU 3.8 + */ + void setPara(String text, byte paraLevel, byte[] embeddingLevels) { + if (text == null) { + setPara(new char[0], paraLevel, embeddingLevels); + } else { + setPara(text.toCharArray(), paraLevel, embeddingLevels); + } + } + + /** + * Perform the Unicode Bidi algorithm. It is defined in the + * Unicode Standard Annex #9: + * Unicode Bidirectional Algorithm, version 13, also described in The + * Unicode Standard, Version 4.0 . + *

+ * + * This method 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 method 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. + * + * The text is stored internally as an array of characters. Therefore the + * documentation will refer to indexes of the characters in the text. + * + * @param chars contains the text that the Bidi algorithm will be + * performed on. This text can be retrieved with + * getText() or + * getTextAsString.
+ * + * @param paraLevel specifies the default level for the text; it is + * typically 0 (LTR) or 1 (RTL). If the method shall + * determine the paragraph level from the text, then + * paraLevel can be set to either + * LEVEL_DEFAULT_LTR or + * LEVEL_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 + * 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 LEVEL_OVERRIDE bit set.
+ *
+ * Except for that bit, it must be + * paraLevel<=embeddingLevels[]<=MAX_EXPLICIT_LEVEL, + * with one exception: a level of zero may be specified + * for a paragraph separator even if + * paraLevel>0 when multiple paragraphs + * are submitted in the same call to + * setPara().
+ *
+ * Caution: A reference to this array, + * not a copy of the levels, will be stored in the + * Bidi object; the + * embeddingLevels should not be modified to + * avoid unexpected results on subsequent Bidi + * operations. However, the setPara() and + * setLine() methods may modify some or all + * of the levels.
+ *
+ * Note: the + * embeddingLevels array must have one entry + * for each character in text. + * + * @throws IllegalArgumentException if the values in embeddingLevels are not + * within the allowed range + * + * @see #LEVEL_DEFAULT_LTR + * @see #LEVEL_DEFAULT_RTL + * @see #LEVEL_OVERRIDE + * @see #MAX_EXPLICIT_LEVEL + * @stable ICU 3.8 + */ + void setPara(char[] chars, byte paraLevel, byte[] embeddingLevels) { + /* check the argument values */ + if (paraLevel < LEVEL_DEFAULT_LTR) { + verifyRange(paraLevel, 0, MAX_EXPLICIT_LEVEL + 1); + } + if (chars == null) { + chars = new char[0]; + } + + /* special treatment for RUNS_ONLY mode */ + if (reorderingMode == REORDER_RUNS_ONLY) { + setParaRunsOnly(chars, paraLevel); + return; + } + + /* initialize the Bidi object */ + this.paraBidi = null; /* mark unfinished setPara */ + this.text = chars; + this.length = this.originalLength = this.resultLength = text.length; + this.paraLevel = paraLevel; + this.direction = (byte) (paraLevel & 1); + this.paraCount = 1; + + /* + * Allocate zero-length arrays instead of setting to null here; then checks for + * null in various places can be eliminated. + */ + dirProps = new byte[0]; + levels = new byte[0]; + runs = new BidiRun[0]; + isGoodLogicalToVisualRunsMap = false; + insertPoints.size = 0; /* clean up from last call */ + insertPoints.confirmed = 0; /* clean up from last call */ + + /* + * Save the original paraLevel if contextual; otherwise, set to 0. + */ + defaultParaLevel = IsDefaultLevel(paraLevel) ? paraLevel : 0; + + if (length == 0) { + /* + * For an empty paragraph, create a Bidi object with the paraLevel and the flags + * and the direction set but without allocating zero-length arrays. There is + * nothing more to do. + */ + if (IsDefaultLevel(paraLevel)) { + this.paraLevel &= 1; + defaultParaLevel = 0; + } + flags = DirPropFlagLR(paraLevel); + runCount = 0; + paraCount = 0; + setParaSuccess(); + return; + } + + runCount = -1; + + /* + * Get the directional properties, the flags bit-set, and determine the + * paragraph level if necessary. + */ + getDirPropsMemory(length); + dirProps = dirPropsMemory; + getDirProps(); + /* the processed length may have changed if OPTION_STREAMING is set */ + 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 */ + getLevelsMemory(length); + levels = levelsMemory; + direction = resolveExplicitLevels(); + } else { + /* + * set BN for all explicit codes, check that all levels are 0 or + * paraLevel..MAX_EXPLICIT_LEVEL + */ + levels = embeddingLevels; + direction = checkExplicitLevels(); + } + + /* allocate isolate memory */ + if (isolateCount > 0) { + if (isolates == null || isolates.length < isolateCount) + isolates = new Isolate[isolateCount + 3]; /* keep some reserve */ + } + isolateCount = -1; /* current isolates stack entry == none */ + + /* + * The steps after (X9) in the Bidi algorithm are performed only if the + * paragraph text has mixed directionality! + */ + switch (direction) { + case LTR: + /* all levels are implicitly at paraLevel (important for getLevels()) */ + trailingWSStart = 0; + break; + case RTL: + /* all levels are implicitly at paraLevel (important for getLevels()) */ + trailingWSStart = 0; + break; + default: + /* + * Choose the right implicit state table + */ + switch (reorderingMode) { + case REORDER_DEFAULT: + this.impTabPair = impTab_DEFAULT; + break; + case REORDER_NUMBERS_SPECIAL: + this.impTabPair = impTab_NUMBERS_SPECIAL; + break; + case REORDER_GROUP_NUMBERS_WITH_R: + this.impTabPair = impTab_GROUP_NUMBERS_WITH_R; + break; + case REORDER_RUNS_ONLY: + /* we should never get here */ + throw new InternalError("Internal ICU error in setPara"); + /* break; */ + case REORDER_INVERSE_NUMBERS_AS_L: + this.impTabPair = impTab_INVERSE_NUMBERS_AS_L; + break; + case REORDER_INVERSE_LIKE_DIRECT: + if ((reorderingOptions & OPTION_INSERT_MARKS) != 0) { + this.impTabPair = impTab_INVERSE_LIKE_DIRECT_WITH_MARKS; + } else { + this.impTabPair = impTab_INVERSE_LIKE_DIRECT; + } + break; + case REORDER_INVERSE_FOR_NUMBERS_SPECIAL: + if ((reorderingOptions & OPTION_INSERT_MARKS) != 0) { + this.impTabPair = impTab_INVERSE_FOR_NUMBERS_SPECIAL_WITH_MARKS; + } else { + this.impTabPair = impTab_INVERSE_FOR_NUMBERS_SPECIAL; + } + break; + } + /* + * 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 && paraCount <= 1 && (flags & DirPropFlagMultiRuns) == 0) { + resolveImplicitLevels(0, length, GetLRFromLevel(GetParaLevelAt(0)), + GetLRFromLevel(GetParaLevelAt(length - 1))); + } else { + /* sor, eor: start and end types of same-level-run */ + int start, limit = 0; + byte level, nextLevel; + short sor, eor; + + /* + * determine the first sor and set eor to it because of the loop body (sor=eor + * there) + */ + level = GetParaLevelAt(0); + nextLevel = levels[0]; + if (level < nextLevel) { + eor = GetLRFromLevel(nextLevel); + } else { + eor = GetLRFromLevel(level); + } + + do { + /* determine start and limit of the run (end points just behind the run) */ + + /* the values for this run's start are the same as for the previous run's end */ + start = limit; + level = nextLevel; + if ((start > 0) && (dirProps[start - 1] == B)) { + /* except if this is a new paragraph, then set sor = para level */ + sor = GetLRFromLevel(GetParaLevelAt(start)); + } else { + sor = eor; + } + + /* search for the limit of this run */ + while ((++limit < length) + && ((levels[limit] == level) || ((DirPropFlag(dirProps[limit]) & MASK_BN_EXPLICIT) != 0))) { + } + + /* get the correct level of the next run */ + if (limit < length) { + nextLevel = levels[limit]; + } else { + nextLevel = GetParaLevelAt(length - 1); + } + + /* determine eor from max(level, nextLevel); sor is last run's eor */ + if (NoOverride(level) < NoOverride(nextLevel)) { + eor = GetLRFromLevel(nextLevel); + } else { + eor = GetLRFromLevel(level); + } + + /* + * if the run consists of overridden directional types, then there are no + * implicit types to be resolved + */ + if ((level & LEVEL_OVERRIDE) == 0) { + resolveImplicitLevels(start, limit, sor, eor); + } else { + /* remove the LEVEL_OVERRIDE flags */ + do { + levels[start++] &= ~LEVEL_OVERRIDE; + } while (start < limit); + } + } while (limit < length); + } + + /* reset the embedding levels for some non-graphic characters (L1), (X9) */ + adjustWSLevels(); + + break; + } + + /* + * add RLM for inverse Bidi with contextual orientation resolving to RTL which + * would not round-trip otherwise + */ + if ((defaultParaLevel > 0) && ((reorderingOptions & OPTION_INSERT_MARKS) != 0) + && ((reorderingMode == REORDER_INVERSE_LIKE_DIRECT) + || (reorderingMode == REORDER_INVERSE_FOR_NUMBERS_SPECIAL))) { + int start, last; + byte level; + byte dirProp; + for (int i = 0; i < paraCount; i++) { + last = paras_limit[i] - 1; + level = paras_level[i]; + if (level == 0) + continue; /* LTR paragraph */ + start = i == 0 ? 0 : paras_limit[i - 1]; + for (int j = last; j >= start; j--) { + dirProp = dirProps[j]; + if (dirProp == L) { + if (j < last) { + while (dirProps[last] == B) { + last--; + } + } + addPoint(last, RLM_BEFORE); + break; + } + if ((DirPropFlag(dirProp) & MASK_R_AL) != 0) { + break; + } + } + } + } + + if ((reorderingOptions & OPTION_REMOVE_CONTROLS) != 0) { + resultLength -= controlCount; + } else { + resultLength += insertPoints.size; + } + setParaSuccess(); + } + + /** + * Perform the Unicode Bidi algorithm on a given paragraph, as defined in the + * Unicode Standard Annex #9: + * Unicode Bidirectional Algorithm, version 13, also described in The + * Unicode Standard, Version 4.0 . + *

+ * + * This method takes a paragraph of text and computes the + * left-right-directionality of each character. The text should not contain any + * Unicode block separators. + *

+ * + * The RUN_DIRECTION attribute in the text, if present, determines the base + * direction (left-to-right or right-to-left). If not present, the base + * direction is computed using the Unicode Bidirectional Algorithm, defaulting + * to left-to-right if there are no strong directional characters in the text. + * This attribute, if present, must be applied to all the text in the paragraph. + *

+ * + * The BIDI_EMBEDDING attribute in the text, if present, represents embedding + * level information. Negative values from -1 to -62 indicate overrides at the + * absolute value of the level. Positive values from 1 to 62 indicate + * embeddings. Where values are zero or not defined, the base embedding level as + * determined by the base direction is assumed. + *

+ * + * The NUMERIC_SHAPING attribute in the text, if present, converts European + * digits to other decimal digits before running the bidi algorithm. This + * attribute, if present, must be applied to all the text in the paragraph. + * + * If the entire text is all of the same directionality, then the method 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. + *

+ * + * @param paragraph a paragraph of text with optional character and paragraph + * attribute information + * @stable ICU 3.8 + */ + public void setPara(AttributedCharacterIterator paragraph) { + byte paraLvl; + char ch = paragraph.first(); + Boolean runDirection = (Boolean) paragraph.getAttribute(TextAttribute.RUN_DIRECTION); + Object shaper = paragraph.getAttribute(TextAttribute.NUMERIC_SHAPING); + + if (runDirection == null) { + paraLvl = LEVEL_DEFAULT_LTR; + } else { + paraLvl = (runDirection.equals(TextAttribute.RUN_DIRECTION_LTR)) ? LTR : RTL; + } + + byte[] lvls = null; + int len = paragraph.getEndIndex() - paragraph.getBeginIndex(); + byte[] embeddingLevels = new byte[len]; + char[] txt = new char[len]; + int i = 0; + while (ch != AttributedCharacterIterator.DONE) { + txt[i] = ch; + Integer embedding = (Integer) paragraph.getAttribute(TextAttribute.BIDI_EMBEDDING); + if (embedding != null) { + byte level = embedding.byteValue(); + if (level == 0) { + /* no-op */ + } else if (level < 0) { + lvls = embeddingLevels; + embeddingLevels[i] = (byte) ((0 - level) | LEVEL_OVERRIDE); + } else { + lvls = embeddingLevels; + embeddingLevels[i] = level; + } + } + ch = paragraph.next(); + ++i; + } + + if (shaper != null) { + ((NumericShaper) shaper).shape(txt, 0, len); + } + setPara(txt, paraLvl, lvls); + } + + /** + * Specify whether block separators must be allocated level zero, so that + * successive paragraphs will progress from left to right. This method must be + * called before 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 ordarParaLTR specifies whether paragraph separators (B) must receive + * level 0, so that successive paragraphs progress from left + * to right. + * + * @see #setPara + * @stable ICU 3.8 + */ + public void orderParagraphsLTR(boolean ordarParaLTR) { + orderParagraphsLTR = ordarParaLTR; + } + + /** + * Get the directionality of the text. + * + * @return a value of LTR, RTL or MIXED + * that indicates if the entire text represented by this object is + * unidirectional, and which direction, or if it is mixed-directional. + * + * @throws IllegalStateException if this call is not preceded by a successful + * call to setPara or + * setLine + * + * @see #LTR + * @see #RTL + * @see #MIXED + * @stable ICU 3.8 + */ + public byte getDirection() { + verifyValidParaOrLine(); + return direction; + } + + /** + * Get the length of the text. + * + * @return The length of the text that the Bidi object was created + * for. + * + * @throws IllegalStateException if this call is not preceded by a successful + * call to setPara or + * setLine + * @stable ICU 3.8 + */ + public int getLength() { + verifyValidParaOrLine(); + return originalLength; + } + + /* paragraphs API methods ------------------------------------------------- */ + + /** + * Get the paragraph level of the text. + * + * @return The paragraph level. If there are multiple paragraphs, their level + * may vary if the required paraLevel is LEVEL_DEFAULT_LTR or + * LEVEL_DEFAULT_RTL. In that case, the level of the first paragraph is + * returned. + * + * @throws IllegalStateException if this call is not preceded by a successful + * call to setPara or + * setLine + * + * @see #LEVEL_DEFAULT_LTR + * @see #LEVEL_DEFAULT_RTL + * @see #getParagraph + * @see #getParagraphByIndex + * @stable ICU 3.8 + */ + public byte getParaLevel() { + verifyValidParaOrLine(); + return paraLevel; + } + + /** + * Retrieves the Bidi class for a given code point. + *

+ * If a BidiClassifier is defined and returns a value other than + * CLASS_DEFAULT, that value is used; otherwise the default class + * determination mechanism is invoked. + *

+ * + * @param c The code point to get a Bidi class for. + * + * @return The Bidi class for the character c that is in effect for + * this Bidi instance. + * + * @stable ICU 3.8 + */ + public int getCustomizedClass(int c) { + int dir; + + dir = bdp.getClass(c); + if (dir >= CHAR_DIRECTION_COUNT) + dir = ON; + return dir; + } + + /** + * setLine() returns a Bidi object 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 Bidi 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 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 Bidi object that represents a + * line. + *

+ * + * Important: the line Bidi object may reference + * data within the global text Bidi object. You should not alter + * the content of the global text object until you are finished using the line + * object. + * + * @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). + * + * @return a Bidi object that will now represent a line of the + * text. + * + * @throws IllegalStateException if this call is not preceded by a successful + * call to setPara + * @throws IllegalArgumentException if start and limit are not in the range + * 0<=start<limit<=getProcessedLength(), + * or if the specified line crosses a paragraph + * boundary + * + * @see #setPara + * @see #getProcessedLength + * @stable ICU 3.8 + */ + public Bidi setLine(Bidi bidi, BidiBase bidiBase, Bidi newBidi, BidiBase newBidiBase, int start, int limit) { + verifyValidPara(); + verifyRange(start, 0, limit); + verifyRange(limit, 0, length + 1); + + return BidiLine.setLine(this, newBidi, newBidiBase, start, limit); + } + + /** + * Get the level for one character. + * + * @param charIndex the index of a character. + * + * @return The level for the character at charIndex. + * + * @throws IllegalStateException if this call is not preceded by a successful + * call to setPara or + * setLine + * @throws IllegalArgumentException if charIndex is not in the range + * 0<=charIndex<getProcessedLength() + * + * @see #getProcessedLength + * @stable ICU 3.8 + */ + public byte getLevelAt(int charIndex) { + // for backward compatibility + if (charIndex < 0 || charIndex >= length) { + return (byte) getBaseLevel(); + } + + verifyValidParaOrLine(); + verifyRange(charIndex, 0, length); + return BidiLine.getLevelAt(this, charIndex); + } + + /** + * Get an array of levels for each character. + *

+ * + * Note that this method may allocate memory under some circumstances, unlike + * getLevelAt(). + * + * @return The levels array for the text, or null if an error + * occurs. + * + * @throws IllegalStateException if this call is not preceded by a successful + * call to setPara or + * setLine + * @stable ICU 3.8 + */ + byte[] getLevels() { + verifyValidParaOrLine(); + if (length <= 0) { + return new byte[0]; + } + return BidiLine.getLevels(this); + } + + /** + * Get the number of runs. This method may invoke the actual reordering on the + * Bidi object, after setPara() may have resolved only + * the levels of the text. Therefore, countRuns() may have to + * allocate memory, and may throw an exception if it fails to do so. + * + * @return The number of runs. + * + * @throws IllegalStateException if this call is not preceded by a successful + * call to setPara or + * setLine + * @stable ICU 3.8 + */ + public int countRuns() { + verifyValidParaOrLine(); + BidiLine.getRuns(this); + return runCount; + } + + /** + * + * Get a BidiRun object according to its index. BidiRun methods may + * be used to retrieve the run's logical start, length and level, which can be + * even for an LTR run or odd for an RTL run. 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. + *

+ * countRuns() is normally called before the runs are retrieved. + * + *

+ * Example: + * + *

+	 * Bidi bidi = new Bidi();
+	 * String text = "abc 123 DEFG xyz";
+	 * bidi.setPara(text, Bidi.RTL, null);
+	 * int i, count = bidi.countRuns(), logicalStart, visualIndex = 0, length;
+	 * BidiRun run;
+	 * for (i = 0; i < count; ++i) {
+	 * 	run = bidi.getVisualRun(i);
+	 * 	logicalStart = run.getStart();
+	 * 	length = run.getLength();
+	 * 	if (Bidi.LTR == run.getEmbeddingLevel()) {
+	 * 		do { // LTR
+	 * 			show_char(text.charAt(logicalStart++), visualIndex++);
+	 * 		} while (--length > 0);
+	 * 	} else {
+	 * 		logicalStart += length; // logicalLimit
+	 * 		do { // RTL
+	 * 			show_char(text.charAt(--logicalStart), visualIndex++);
+	 * 		} while (--length > 0);
+	 * 	}
+	 * }
+	 * 
+ *

+ * 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 {@link #writeReordered}, optionally with the + * {@link #KEEP_BASE_COMBINING} option, can be considered in order + * to avoid these issues. + * + * @param runIndex is the number of the run in visual order, in the range + * [0..countRuns()-1]. + * + * @return a BidiRun object containing the details of the run. The + * directionality of the run is LTR==0 or + * RTL==1, never MIXED. + * + * @throws IllegalStateException if this call is not preceded by a successful + * call to setPara or + * setLine + * @throws IllegalArgumentException if runIndex is not in the range + * 0<=runIndex<countRuns() + * + * @see #countRuns() + * @see com.ibm.icu.text.BidiRun + * @see com.ibm.icu.text.BidiRun#getStart() + * @see com.ibm.icu.text.BidiRun#getLength() + * @see com.ibm.icu.text.BidiRun#getEmbeddingLevel() + * @stable ICU 3.8 + */ + BidiRun getVisualRun(int runIndex) { + verifyValidParaOrLine(); + BidiLine.getRuns(this); + verifyRange(runIndex, 0, runCount); + return BidiLine.getVisualRun(this, runIndex); + } + + /** + * Get a visual-to-logical index map (array) for the characters in the + * Bidi (paragraph or line) object. + *

+ * Some values in the map may be MAP_NOWHERE if the corresponding + * text characters are Bidi marks inserted in the visual output by the option + * OPTION_INSERT_MARKS. + *

+ * When the visual output is altered by using options of + * writeReordered() such as INSERT_LRM_FOR_NUMERIC, + * KEEP_BASE_COMBINING, OUTPUT_REVERSE, + * REMOVE_BIDI_CONTROLS, the logical positions returned may not be + * correct. It is advised to use, when possible, reordering options such as + * {@link #OPTION_INSERT_MARKS} and {@link #OPTION_REMOVE_CONTROLS}. + * + * @return an array of getResultLength() indexes which will reflect + * the reordering of the characters.
+ *
+ * The index map will result in + * indexMap[visualIndex]==logicalIndex, where + * indexMap represents the returned array. + * + * @throws IllegalStateException if this call is not preceded by a successful + * call to setPara or + * setLine + * + * @see #getLogicalMap + * @see #getLogicalIndex + * @see #getResultLength + * @see #MAP_NOWHERE + * @see #OPTION_INSERT_MARKS + * @see #writeReordered + * @stable ICU 3.8 + */ + private int[] getVisualMap() { + /* countRuns() checks successful call to setPara/setLine */ + countRuns(); + if (resultLength <= 0) { + return new int[0]; + } + return BidiLine.getVisualMap(this); + } + + /** + * This is a convenience method that does not use a Bidi 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 getVisualMap() on a + * Bidi object. + * + * @param levels is an array of levels that have been determined by the + * application. + * + * @return an array of levels.length indexes which will reflect the + * reordering of the characters. + *

+ * The index map will result in + * indexMap[visualIndex]==logicalIndex, where + * indexMap represents the returned array. + * + * @stable ICU 3.8 + */ + private static int[] reorderVisual(byte[] levels) { + return BidiLine.reorderVisual(levels); + } + + /** + * 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, the base direction + * is right-to-left. + * + * @stable ICU 3.8 + */ + public static final int DIRECTION_DEFAULT_RIGHT_TO_LEFT = LEVEL_DEFAULT_RTL; + + /** + * Create Bidi from the given text, embedding, and direction information. The + * embeddings array may be null. If present, the values represent embedding + * level information. Negative values from -1 to -61 indicate overrides at the + * absolute value of the level. Positive values from 1 to 61 indicate + * embeddings. Where values are zero, the base embedding level as determined by + * the base direction is assumed. + *

+ * + * Note: this constructor calls setPara() internally. + * + * @param text an array containing the paragraph of text to process. + * @param textStart the index into the text array of the start of the + * paragraph. + * @param embeddings an array containing embedding values for each + * character in the paragraph. This can be null, in which + * case it is assumed that there is no external embedding + * information. + * @param embStart the index into the embedding array of the start of the + * paragraph. + * @param paragraphLength the length of the paragraph in the text and embeddings + * arrays. + * @param flags a collection of flags that control the algorithm. The + * algorithm understands the flags + * DIRECTION_LEFT_TO_RIGHT, DIRECTION_RIGHT_TO_LEFT, + * DIRECTION_DEFAULT_LEFT_TO_RIGHT, and + * DIRECTION_DEFAULT_RIGHT_TO_LEFT. Other values are + * reserved. + * + * @throws IllegalArgumentException if the values in embeddings are not within + * the allowed range + * + * @see #DIRECTION_LEFT_TO_RIGHT + * @see #DIRECTION_RIGHT_TO_LEFT + * @see #DIRECTION_DEFAULT_LEFT_TO_RIGHT + * @see #DIRECTION_DEFAULT_RIGHT_TO_LEFT + * @stable ICU 3.8 + */ + public BidiBase(char[] text, int textStart, byte[] embeddings, int embStart, int paragraphLength, int flags) { + this(0, 0); + byte paraLvl; + switch (flags) { + case Bidi.DIRECTION_LEFT_TO_RIGHT: + default: + paraLvl = LTR; + break; + case Bidi.DIRECTION_RIGHT_TO_LEFT: + paraLvl = RTL; + break; + case Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT: + paraLvl = LEVEL_DEFAULT_LTR; + break; + case Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT: + paraLvl = LEVEL_DEFAULT_RTL; + break; + } + byte[] paraEmbeddings; + if (embeddings == null) { + paraEmbeddings = null; + } else { + paraEmbeddings = new byte[paragraphLength]; + byte lev; + for (int i = 0; i < paragraphLength; i++) { + lev = embeddings[i + embStart]; + if (lev < 0) { + lev = (byte) ((-lev) | LEVEL_OVERRIDE); + } else if (lev == 0) { + lev = paraLvl; + if (paraLvl > MAX_EXPLICIT_LEVEL) { + lev &= 1; + } + } + paraEmbeddings[i] = lev; + } + } + + char[] paraText = new char[paragraphLength]; + System.arraycopy(text, textStart, paraText, 0, paragraphLength); + setPara(paraText, paraLvl, paraEmbeddings); + } + + /** + * Return true if the line is not left-to-right or right-to-left. This means it + * either has mixed runs of left-to-right and right-to-left text, or the base + * direction differs from the direction of the only run of text. + * + * @return true if the line is not left-to-right or right-to-left. + * + * @throws IllegalStateException if this call is not preceded by a successful + * call to setPara + * @stable ICU 3.8 + */ + public boolean isMixed() { + return (!isLeftToRight() && !isRightToLeft()); + } + + /** + * Return true if the line is all left-to-right text and the base direction is + * left-to-right. + * + * @return true if the line is all left-to-right text and the base direction is + * left-to-right. + * + * @throws IllegalStateException if this call is not preceded by a successful + * call to setPara + * @stable ICU 3.8 + */ + public boolean isLeftToRight() { + return (getDirection() == LTR && (paraLevel & 1) == 0); + } + + /** + * Return true if the line is all right-to-left text, and the base direction is + * right-to-left + * + * @return true if the line is all right-to-left text, and the base direction is + * right-to-left + * + * @throws IllegalStateException if this call is not preceded by a successful + * call to setPara + * @stable ICU 3.8 + */ + public boolean isRightToLeft() { + return (getDirection() == RTL && (paraLevel & 1) == 1); + } + + /** + * Return true if the base direction is left-to-right + * + * @return true if the base direction is left-to-right + * + * @throws IllegalStateException if this call is not preceded by a successful + * call to setPara or + * setLine + * + * @stable ICU 3.8 + */ + public boolean baseIsLeftToRight() { + return (getParaLevel() == LTR); + } + + /** + * Return the base level (0 if left-to-right, 1 if right-to-left). + * + * @return the base level + * + * @throws IllegalStateException if this call is not preceded by a successful + * call to setPara or + * setLine + * + * @stable ICU 3.8 + */ + public int getBaseLevel() { + return getParaLevel(); + } + + /** + * Compute the logical to visual run mapping + */ + void getLogicalToVisualRunsMap() { + if (isGoodLogicalToVisualRunsMap) { + return; + } + int count = countRuns(); + if ((logicalToVisualRunsMap == null) || (logicalToVisualRunsMap.length < count)) { + logicalToVisualRunsMap = new int[count]; + } + int i; + long[] keys = new long[count]; + for (i = 0; i < count; i++) { + keys[i] = ((long) (runs[i].start) << 32) + i; + } + Arrays.sort(keys); + for (i = 0; i < count; i++) { + logicalToVisualRunsMap[i] = (int) (keys[i] & 0x00000000FFFFFFFF); + } + isGoodLogicalToVisualRunsMap = true; + } + + /** + * Return the level of the nth logical run in this line. + * + * @param run the index of the run, between 0 and countRuns()-1 + * + * @return the level of the run + * + * @throws IllegalStateException if this call is not preceded by a successful + * call to setPara or + * setLine + * @throws IllegalArgumentException if run is not in the range + * 0<=run<countRuns() + * @stable ICU 3.8 + */ + public int getRunLevel(int run) { + verifyValidParaOrLine(); + BidiLine.getRuns(this); + + // for backward compatibility + if (run < 0 || run >= runCount) { + return getParaLevel(); + } + + getLogicalToVisualRunsMap(); + return runs[logicalToVisualRunsMap[run]].level; + } + + /** + * Return the index of the character at the start of the nth logical run in this + * line, as an offset from the start of the line. + * + * @param run the index of the run, between 0 and countRuns() + * + * @return the start of the run + * + * @throws IllegalStateException if this call is not preceded by a successful + * call to setPara or + * setLine + * @throws IllegalArgumentException if run is not in the range + * 0<=run<countRuns() + * @stable ICU 3.8 + */ + public int getRunStart(int run) { + verifyValidParaOrLine(); + BidiLine.getRuns(this); + + // for backward compatibility + if (runCount == 1) { + return 0; + } else if (run == runCount) { + return length; + } + + getLogicalToVisualRunsMap(); + return runs[logicalToVisualRunsMap[run]].start; + } + + /** + * Return the index of the character past the end of the nth logical run in this + * line, as an offset from the start of the line. For example, this will return + * the length of the line for the last run on the line. + * + * @param run the index of the run, between 0 and countRuns() + * + * @return the limit of the run + * + * @throws IllegalStateException if this call is not preceded by a successful + * call to setPara or + * setLine + * @throws IllegalArgumentException if run is not in the range + * 0<=run<countRuns() + * @stable ICU 3.8 + */ + public int getRunLimit(int run) { + verifyValidParaOrLine(); + BidiLine.getRuns(this); + + // for backward compatibility + if (runCount == 1) { + return length; + } + + getLogicalToVisualRunsMap(); + int idx = logicalToVisualRunsMap[run]; + int len = idx == 0 ? runs[idx].limit : runs[idx].limit - runs[idx - 1].limit; + return runs[idx].start + len; + } + + /** + * Return true if the specified text requires bidi analysis. If this returns + * false, the text will display left-to-right. Clients can then avoid + * constructing a Bidi object. Text in the Arabic Presentation Forms area of + * Unicode is presumed to already be shaped and ordered for display, and so will + * not cause this method to return true. + * + * @param text the text containing the characters to test + * @param start the start of the range of characters to test + * @param limit the limit of the range of characters to test + * + * @return true if the range of characters requires bidi analysis + * + * @stable ICU 3.8 + */ + public static boolean requiresBidi(char[] text, int start, int limit) { + final int RTLMask = (1 << R | 1 << AL | 1 << RLE | 1 << RLO | 1 << AN); + + if (0 > start || start > limit || limit > text.length) { + throw new IllegalArgumentException("Value start " + start + " is out of range 0 to " + limit + ", or limit " + + limit + " is beyond the text length " + text.length); + } + + for (int i = start; i < limit; ++i) { + if (Character.isHighSurrogate(text[i]) && i < (limit - 1) && Character.isLowSurrogate(text[i + 1])) { + if (((1 << UCharacter.getDirection(Character.codePointAt(text, i))) & RTLMask) != 0) { + return true; + } + } else if (((1 << UCharacter.getDirection(text[i])) & RTLMask) != 0) { + return true; + } + } + + return false; + } + + /** + * Reorder the objects in the array into visual order based on their levels. + * This is a utility method to use when you have a collection of objects + * representing runs of text in logical order, each run containing text at a + * single level. The elements at index from + * objectStart up to objectStart + count in the + * objects array will be reordered into visual order assuming each run of text + * has the level indicated by the corresponding element in the levels array (at + * index - objectStart + levelStart). + * + * @param levels an array representing the bidi level of each object + * @param levelStart the start position in the levels array + * @param objects the array of objects to be reordered into visual order + * @param objectStart the start position in the objects array + * @param count the number of objects to reorder + * @stable ICU 3.8 + */ + public static void reorderVisually(byte[] levels, int levelStart, Object[] objects, int objectStart, int count) { + // for backward compatibility + if (0 > levelStart || levels.length <= levelStart) { + throw new IllegalArgumentException( + "Value levelStart " + levelStart + " is out of range 0 to " + (levels.length - 1)); + } + if (0 > objectStart || objects.length <= objectStart) { + throw new IllegalArgumentException( + "Value objectStart " + objectStart + " is out of range 0 to " + (objects.length - 1)); + } + if (0 > count || objects.length < (objectStart + count)) { + throw new IllegalArgumentException("Value count " + count + " is less than zero, or objectStart + count" + + " is beyond objects length " + objects.length); + } + + byte[] reorderLevels = new byte[count]; + System.arraycopy(levels, levelStart, reorderLevels, 0, count); + int[] indexMap = reorderVisual(reorderLevels); + Object[] temp = new Object[count]; + System.arraycopy(objects, objectStart, temp, 0, count); + for (int i = 0; i < count; ++i) { + objects[objectStart + i] = temp[indexMap[i]]; + } + } + + /** + * Take a Bidi object containing the reordering information for a + * piece of text (one or more paragraphs) set by setPara() or for a + * line of text set by setLine() and return a string containing the + * reordered text. + * + *

+ * The text may have been aliased (only a reference was stored without copying + * the contents), thus it must not have been modified since the + * setPara() call. + *

+ * + * This method 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 returned string. 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 descriptions of the return value and the + * options parameter, and of the option bit flags. + * + * @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: DO_MIRRORING, + * INSERT_LRM_FOR_NUMERIC, + * KEEP_BASE_COMBINING, OUTPUT_REVERSE, + * REMOVE_BIDI_CONTROLS, STREAMING + * + * @return The reordered text. If the INSERT_LRM_FOR_NUMERIC option + * is set, then the length of the returned string could be as large as + * getLength()+2*countRuns().
+ * If the REMOVE_BIDI_CONTROLS option is set, then the + * length of the returned string may be less than + * getLength().
+ * If none of these options is set, then the length of the returned + * string will be exactly getProcessedLength(). + * + * @throws IllegalStateException if this call is not preceded by a successful + * call to setPara or + * setLine + * + * @see #DO_MIRRORING + * @see #INSERT_LRM_FOR_NUMERIC + * @see #KEEP_BASE_COMBINING + * @see #OUTPUT_REVERSE + * @see #REMOVE_BIDI_CONTROLS + * @see #OPTION_STREAMING + * @see #getProcessedLength + * @stable ICU 3.8 + */ + public String writeReordered(int options) { + verifyValidParaOrLine(); + if (length == 0) { + /* nothing to do */ + return ""; + } + return BidiWriter.writeReordered(this, options); + } + + /** + * Display the bidi internal state, used in debugging. + */ + public String toString() { + StringBuilder buf = new StringBuilder(getClass().getName()); + + buf.append("[dir: "); + buf.append(direction); + buf.append(" baselevel: "); + buf.append(paraLevel); + buf.append(" length: "); + buf.append(length); + buf.append(" runs: "); + if (levels == null) { + buf.append("none"); + } else { + buf.append('['); + buf.append(levels[0]); + for (int i = 1; i < levels.length; i++) { + buf.append(' '); + buf.append(levels[i]); + } + buf.append(']'); + } + buf.append(" text: [0x"); + buf.append(Integer.toHexString(text[0])); + for (int i = 1; i < text.length; i++) { + buf.append(" 0x"); + buf.append(Integer.toHexString(text[i])); + } + buf.append("]]"); + + return buf.toString(); + } + +} diff --git a/src/main/java/jdk_internal/icu/text/BidiLine.java b/src/main/java/jdk_internal/icu/text/BidiLine.java index 56620549..f220ba0f 100755 --- a/src/main/java/jdk_internal/icu/text/BidiLine.java +++ b/src/main/java/jdk_internal/icu/text/BidiLine.java @@ -1,821 +1,821 @@ -/* - * Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* -******************************************************************************* -* Copyright (C) 2001-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -*/ -/* Written by Simon Montagu, Matitiahu Allouche - * (ported from C code written by Markus W. Scherer) - */ - -package jdk_internal.icu.text; - -import java.util.Arrays; - -import jdk_internal.bidi.Bidi; - -final class BidiLine { - - /* - * 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 3.0 Bidi algorithm as defined in Unicode Standard Annex #9: Unicode - * Bidirectional Algorithm, version 13, also described in The Unicode - * Standard, Version 4.0.1 . - * - * This means that there is a Bidi 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 getLogical/VisualIndex() methods 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 methods 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 Bidi object to use the same levels array as its paragraph - * parent object. - * - * When a Bidi 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 method is called only from setLine(), so paraLevel is set correctly for - * the line even when contextual multiple paragraphs. - */ - - static void setTrailingWSStart(BidiBase bidiBase) { - byte[] dirProps = bidiBase.dirProps; - byte[] levels = bidiBase.levels; - int start = bidiBase.length; - byte paraLevel = bidiBase.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 getLevels when - * orderParagraphsLTR==TRUE - */ - if (dirProps[start - 1] == BidiBase.B) { - bidiBase.trailingWSStart = start; /* currently == bidiBase.length */ - return; - } - /* go backwards across all WS, BN, explicit codes */ - while (start > 0 && (BidiBase.DirPropFlag(dirProps[start - 1]) & BidiBase.MASK_WS) != 0) { - --start; - } - - /* if the WS run can be merged with the previous run then do so here */ - while (start > 0 && levels[start - 1] == paraLevel) { - --start; - } - - bidiBase.trailingWSStart = start; - } - - static Bidi setLine(BidiBase paraBidi, Bidi newBidi, BidiBase lineBidi, int start, int limit) { - int length; - - /* set the values in lineBidi from its paraBidi parent */ - /* class members are already initialized to 0 */ - // lineBidi.paraBidi = null; /* mark unfinished setLine */ - // lineBidi.flags = 0; - // lineBidi.controlCount = 0; - - length = lineBidi.length = lineBidi.originalLength = lineBidi.resultLength = limit - start; - - lineBidi.text = new char[length]; - System.arraycopy(paraBidi.text, start, lineBidi.text, 0, length); - lineBidi.paraLevel = paraBidi.GetParaLevelAt(start); - lineBidi.paraCount = paraBidi.paraCount; - lineBidi.runs = new BidiRun[0]; - lineBidi.reorderingMode = paraBidi.reorderingMode; - lineBidi.reorderingOptions = paraBidi.reorderingOptions; - if (paraBidi.controlCount > 0) { - int j; - for (j = start; j < limit; j++) { - if (BidiBase.IsBidiControlChar(paraBidi.text[j])) { - lineBidi.controlCount++; - } - } - lineBidi.resultLength -= lineBidi.controlCount; - } - /* copy proper subset of DirProps */ - lineBidi.getDirPropsMemory(length); - lineBidi.dirProps = lineBidi.dirPropsMemory; - System.arraycopy(paraBidi.dirProps, start, lineBidi.dirProps, 0, length); - /* copy proper subset of Levels */ - lineBidi.getLevelsMemory(length); - lineBidi.levels = lineBidi.levelsMemory; - System.arraycopy(paraBidi.levels, start, lineBidi.levels, 0, length); - lineBidi.runCount = -1; - - if (paraBidi.direction != BidiBase.MIXED) { - /* the parent is already trivial */ - lineBidi.direction = paraBidi.direction; - - /* - * The parent's levels are all either implicitly or explicitly ==paraLevel; do - * the same here. - */ - if (paraBidi.trailingWSStart <= start) { - lineBidi.trailingWSStart = 0; - } else if (paraBidi.trailingWSStart < limit) { - lineBidi.trailingWSStart = paraBidi.trailingWSStart - start; - } else { - lineBidi.trailingWSStart = length; - } - } else { - byte[] levels = lineBidi.levels; - int i, trailingWSStart; - byte level; - - setTrailingWSStart(lineBidi); - trailingWSStart = lineBidi.trailingWSStart; - - /* recalculate lineBidiBase.direction */ - if (trailingWSStart == 0) { - /* all levels are at paraLevel */ - lineBidi.direction = (byte) (lineBidi.paraLevel & 1); - } else { - /* get the level of the first character */ - level = (byte) (levels[0] & 1); - - /* - * if there is anything of a different level, then the line is mixed - */ - if (trailingWSStart < length && (lineBidi.paraLevel & 1) != level) { - /* - * the trailing WS is at paraLevel, which differs from levels[0] - */ - lineBidi.direction = BidiBase.MIXED; - } else { - /* - * see if levels[1..trailingWSStart-1] have the same direction as levels[0] and - * paraLevel - */ - for (i = 1;; i++) { - if (i == trailingWSStart) { - /* the direction values match those in level */ - lineBidi.direction = level; - break; - } else if ((levels[i] & 1) != level) { - lineBidi.direction = BidiBase.MIXED; - break; - } - } - } - } - - switch (lineBidi.direction) { - case Bidi.DIRECTION_LEFT_TO_RIGHT: - /* make sure paraLevel is even */ - lineBidi.paraLevel = (byte) ((lineBidi.paraLevel + 1) & ~1); - - /* - * all levels are implicitly at paraLevel (important for getLevels()) - */ - lineBidi.trailingWSStart = 0; - break; - case Bidi.DIRECTION_RIGHT_TO_LEFT: - /* make sure paraLevel is odd */ - lineBidi.paraLevel |= 1; - - /* - * all levels are implicitly at paraLevel (important for getLevels()) - */ - lineBidi.trailingWSStart = 0; - break; - default: - break; - } - } - - lineBidi.paraBidi = paraBidi; /* mark successful setLine */ - - return newBidi; - } - - static byte getLevelAt(BidiBase bidiBase, int charIndex) { - /* return paraLevel if in the trailing WS run, otherwise the real level */ - if (bidiBase.direction != BidiBase.MIXED || charIndex >= bidiBase.trailingWSStart) { - return bidiBase.GetParaLevelAt(charIndex); - } else { - return bidiBase.levels[charIndex]; - } - } - - static byte[] getLevels(BidiBase bidiBase) { - int start = bidiBase.trailingWSStart; - int length = bidiBase.length; - - if (start != length) { - /* the current levels array does not reflect the WS run */ - /* - * 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 Bidi object for a line, and we need to create a new levels - * array. - */ - /* - * bidiBase.paraLevel is ok even if contextual multiple paragraphs, since - * bidiBase is a line object - */ - Arrays.fill(bidiBase.levels, start, length, bidiBase.paraLevel); - - /* this new levels array is set for the line and reflects the WS run */ - bidiBase.trailingWSStart = length; - } - if (length < bidiBase.levels.length) { - byte[] levels = new byte[length]; - System.arraycopy(bidiBase.levels, 0, levels, 0, length); - return levels; - } - return bidiBase.levels; - } - - static BidiRun getVisualRun(BidiBase bidiBase, int runIndex) { - int start = bidiBase.runs[runIndex].start; - int limit; - byte level = bidiBase.runs[runIndex].level; - - if (runIndex > 0) { - limit = start + bidiBase.runs[runIndex].limit - bidiBase.runs[runIndex - 1].limit; - } else { - limit = start + bidiBase.runs[0].limit; - } - return new BidiRun(start, limit, level); - } - - /* in trivial cases there is only one trivial run; called by getRuns() */ - private static void getSingleRun(BidiBase bidiBase, byte level) { - /* simple, single-run case */ - bidiBase.runs = bidiBase.simpleRuns; - bidiBase.runCount = 1; - - /* fill and reorder the single run */ - bidiBase.runs[0] = new BidiRun(0, bidiBase.length, level); - } - - /* 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. - */ - private static void reorderLine(BidiBase bidiBase, byte minLevel, byte maxLevel) { - - /* nothing to do? */ - if (maxLevel <= (minLevel | 1)) { - return; - } - - BidiRun[] runs; - BidiRun tempRun; - byte[] levels; - int firstRun, endRun, limitRun, runCount; - - /* - * 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 = bidiBase.runs; - levels = bidiBase.levels; - runCount = bidiBase.runCount; - - /* - * do not include the WS run at paraLevel<=old minLevel except in the simple - * loop - */ - if (bidiBase.trailingWSStart < bidiBase.length) { - --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 && levels[runs[firstRun].start] < maxLevel) { - ++firstRun; - } - if (firstRun >= runCount) { - break; /* no more such runs */ - } - - /* look for the limit run of such a sequence (the run behind it) */ - for (limitRun = firstRun; ++limitRun < runCount && levels[runs[limitRun].start] >= maxLevel;) { - } - - /* Swap the entire sequence of runs from firstRun to limitRun-1. */ - endRun = limitRun - 1; - while (firstRun < endRun) { - tempRun = runs[firstRun]; - runs[firstRun] = runs[endRun]; - runs[endRun] = tempRun; - ++firstRun; - --endRun; - } - - if (limitRun == runCount) { - break; /* no more such runs */ - } else { - firstRun = limitRun + 1; - } - } - } - - /* now do maxLevel==old minLevel (==odd!), see above */ - if ((minLevel & 1) == 0) { - firstRun = 0; - - /* include the trailing WS run in this complete reordering */ - if (bidiBase.trailingWSStart == bidiBase.length) { - --runCount; - } - - /* Swap the entire sequence of all runs. (endRun==runCount) */ - while (firstRun < runCount) { - tempRun = runs[firstRun]; - runs[firstRun] = runs[runCount]; - runs[runCount] = tempRun; - ++firstRun; - --runCount; - } - } - } - - /* compute the runs array --------------------------------------------------- */ - - static int getRunFromLogicalIndex(BidiBase bidiBase, int logicalIndex) { - BidiRun[] runs = bidiBase.runs; - int runCount = bidiBase.runCount, visualStart = 0, i, length, logicalStart; - - for (i = 0; i < runCount; i++) { - length = runs[i].limit - visualStart; - logicalStart = runs[i].start; - if ((logicalIndex >= logicalStart) && (logicalIndex < (logicalStart + length))) { - return i; - } - visualStart += length; - } - /* we should never get here */ - throw new IllegalStateException("Internal ICU error in getRunFromLogicalIndex"); - } - - /* - * Compute the runs array from the levels array. After 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 OPTION_INSERT_MARKS is set, insertRemove will contain the sum - * of appropriate LRM/RLM_BEFORE/AFTER flags. If option OPTION_REMOVE_CONTROLS - * is set, insertRemove will contain the negative number of BiDi control - * characters within this run. - */ - static void getRuns(BidiBase bidiBase) { - /* - * This method returns immediately if the runs are already set. This includes - * the case of length==0 (handled in setPara).. - */ - if (bidiBase.runCount >= 0) { - return; - } - if (bidiBase.direction != BidiBase.MIXED) { - /* simple, single-run case - this covers length==0 */ - /* bidiBase.paraLevel is ok even for contextual multiple paragraphs */ - getSingleRun(bidiBase, bidiBase.paraLevel); - } else /* BidiBase.MIXED, length>0 */ { - /* mixed directionality */ - int length = bidiBase.length, limit; - byte[] levels = bidiBase.levels; - int i, runCount; - byte level = -1; /* 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 Bidi 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 = bidiBase.trailingWSStart; - /* count the runs, there is at least one non-WS run, and limit>0 */ - runCount = 0; - for (i = 0; i < limit; ++i) { - /* increment runCount at the start of each run */ - if (levels[i] != level) { - ++runCount; - level = levels[i]; - } - } - - /* - * We don't need to see if the last run can be merged with a trailing WS run - * because setTrailingWSStart() would have done that. - */ - if (runCount == 1 && limit == length) { - /* There is only one non-WS run and no trailing WS-run. */ - getSingleRun(bidiBase, levels[0]); - } else /* runCount>1 || limit 1 */ - bidiBase.getRunsMemory(runCount); - runs = bidiBase.runsMemory; - - /* 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 (level < minLevel) { - minLevel = level; - } - if (level > maxLevel) { - maxLevel = level; - } - - /* look for the run limit */ - while (++i < limit && levels[i] == level) { - } - - /* i is another run limit */ - runs[runIndex] = new BidiRun(start, i - start, level); - ++runIndex; - } while (i < limit); - - if (limit < length) { - /* there is a separate WS run */ - runs[runIndex] = new BidiRun(limit, length - limit, bidiBase.paraLevel); - /* - * For the trailing WS run, bidiBase.paraLevel is ok even if contextual multiple - * paragraphs. - */ - if (bidiBase.paraLevel < minLevel) { - minLevel = bidiBase.paraLevel; - } - } - - /* set the object fields */ - bidiBase.runs = runs; - bidiBase.runCount = runCount; - - reorderLine(bidiBase, 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; i < runCount; ++i) { - runs[i].level = levels[runs[i].start]; - limit = (runs[i].limit += limit); - } - - /* Set the embedding level for the trailing WS run. */ - /* For a RTL paragraph, it will be the *first* run in visual order. */ - /* - * For the trailing WS run, bidiBase.paraLevel is ok even if contextual multiple - * paragraphs. - */ - if (runIndex < runCount) { - int trailingRun = ((bidiBase.paraLevel & 1) != 0) ? 0 : runIndex; - runs[trailingRun].level = bidiBase.paraLevel; - } - } - } - - /* handle insert LRM/RLM BEFORE/AFTER run */ - if (bidiBase.insertPoints.size > 0) { - BidiBase.Point point; - int runIndex, ip; - for (ip = 0; ip < bidiBase.insertPoints.size; ip++) { - point = bidiBase.insertPoints.points[ip]; - runIndex = getRunFromLogicalIndex(bidiBase, point.pos); - bidiBase.runs[runIndex].insertRemove |= point.flag; - } - } - - /* handle remove BiDi control characters */ - if (bidiBase.controlCount > 0) { - int runIndex, ic; - char c; - for (ic = 0; ic < bidiBase.length; ic++) { - c = bidiBase.text[ic]; - if (BidiBase.IsBidiControlChar(c)) { - runIndex = getRunFromLogicalIndex(bidiBase, ic); - bidiBase.runs[runIndex].insertRemove--; - } - } - } - } - - static int[] prepareReorder(byte[] levels, byte[] pMinLevel, byte[] pMaxLevel) { - int start; - byte level, minLevel, maxLevel; - - if (levels == null || levels.length <= 0) { - return null; - } - - /* determine minLevel and maxLevel */ - minLevel = BidiBase.MAX_EXPLICIT_LEVEL + 1; - maxLevel = 0; - for (start = levels.length; start > 0;) { - level = levels[--start]; - if (level < 0 || level > (BidiBase.MAX_EXPLICIT_LEVEL + 1)) { - return null; - } - if (level < minLevel) { - minLevel = level; - } - if (level > maxLevel) { - maxLevel = level; - } - } - pMinLevel[0] = minLevel; - pMaxLevel[0] = maxLevel; - - /* initialize the index map */ - int[] indexMap = new int[levels.length]; - for (start = levels.length; start > 0;) { - --start; - indexMap[start] = start; - } - - return indexMap; - } - - static int[] reorderVisual(byte[] levels) { - byte[] aMinLevel = new byte[1]; - byte[] aMaxLevel = new byte[1]; - int start, end, limit, temp; - byte minLevel, maxLevel; - - int[] indexMap = prepareReorder(levels, aMinLevel, aMaxLevel); - if (indexMap == null) { - return null; - } - - minLevel = aMinLevel[0]; - maxLevel = aMaxLevel[0]; - - /* nothing to do? */ - if (minLevel == maxLevel && (minLevel & 1) == 0) { - return indexMap; - } - - /* 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 < levels.length && levels[start] < maxLevel) { - ++start; - } - if (start >= levels.length) { - break; /* no more such runs */ - } - - /* look for the limit of such a sequence (the index behind it) */ - for (limit = start; ++limit < levels.length && levels[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 < end) { - temp = indexMap[start]; - indexMap[start] = indexMap[end]; - indexMap[end] = temp; - - ++start; - --end; - } - - if (limit == levels.length) { - break; /* no more such sequences */ - } else { - start = limit + 1; - } - } - } while (--maxLevel >= minLevel); - - return indexMap; - } - - static int[] getVisualMap(BidiBase bidiBase) { - /* fill a visual-to-logical index map using the runs[] */ - BidiRun[] runs = bidiBase.runs; - int logicalStart, visualStart, visualLimit; - int allocLength = bidiBase.length > bidiBase.resultLength ? bidiBase.length : bidiBase.resultLength; - int[] indexMap = new int[allocLength]; - - visualStart = 0; - int idx = 0; - for (int j = 0; j < bidiBase.runCount; ++j) { - logicalStart = runs[j].start; - visualLimit = runs[j].limit; - if (runs[j].isEvenRun()) { - do { /* LTR */ - indexMap[idx++] = logicalStart++; - } while (++visualStart < visualLimit); - } else { - logicalStart += visualLimit - visualStart; /* logicalLimit */ - do { /* RTL */ - indexMap[idx++] = --logicalStart; - } while (++visualStart < visualLimit); - } - /* visualStart==visualLimit; */ - } - - if (bidiBase.insertPoints.size > 0) { - int markFound = 0, runCount = bidiBase.runCount; - int insertRemove, i, j, k; - runs = bidiBase.runs; - /* count all inserted marks */ - for (i = 0; i < runCount; i++) { - insertRemove = runs[i].insertRemove; - if ((insertRemove & (BidiBase.LRM_BEFORE | BidiBase.RLM_BEFORE)) > 0) { - markFound++; - } - if ((insertRemove & (BidiBase.LRM_AFTER | BidiBase.RLM_AFTER)) > 0) { - markFound++; - } - } - /* move back indexes by number of preceding marks */ - k = bidiBase.resultLength; - for (i = runCount - 1; i >= 0 && markFound > 0; i--) { - insertRemove = runs[i].insertRemove; - if ((insertRemove & (BidiBase.LRM_AFTER | BidiBase.RLM_AFTER)) > 0) { - indexMap[--k] = BidiBase.MAP_NOWHERE; - markFound--; - } - visualStart = i > 0 ? runs[i - 1].limit : 0; - for (j = runs[i].limit - 1; j >= visualStart && markFound > 0; j--) { - indexMap[--k] = indexMap[j]; - } - if ((insertRemove & (BidiBase.LRM_BEFORE | BidiBase.RLM_BEFORE)) > 0) { - indexMap[--k] = BidiBase.MAP_NOWHERE; - markFound--; - } - } - } else if (bidiBase.controlCount > 0) { - int runCount = bidiBase.runCount, logicalEnd; - int insertRemove, length, i, j, k, m; - char uchar; - boolean evenRun; - runs = bidiBase.runs; - visualStart = 0; - /* move forward indexes by number of preceding controls */ - k = 0; - for (i = 0; i < runCount; i++, visualStart += length) { - length = runs[i].limit - visualStart; - insertRemove = runs[i].insertRemove; - /* if no control found yet, nothing to do in this run */ - if ((insertRemove == 0) && (k == visualStart)) { - k += length; - continue; - } - /* if no control in this run */ - if (insertRemove == 0) { - visualLimit = runs[i].limit; - for (j = visualStart; j < visualLimit; j++) { - indexMap[k++] = indexMap[j]; - } - continue; - } - logicalStart = runs[i].start; - evenRun = runs[i].isEvenRun(); - logicalEnd = logicalStart + length - 1; - for (j = 0; j < length; j++) { - m = evenRun ? logicalStart + j : logicalEnd - j; - uchar = bidiBase.text[m]; - if (!BidiBase.IsBidiControlChar(uchar)) { - indexMap[k++] = m; - } - } - } - } - if (allocLength == bidiBase.resultLength) { - return indexMap; - } - int[] newMap = new int[bidiBase.resultLength]; - System.arraycopy(indexMap, 0, newMap, 0, bidiBase.resultLength); - return newMap; - } - -} +/* + * Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* +******************************************************************************* +* Copyright (C) 2001-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +*/ +/* Written by Simon Montagu, Matitiahu Allouche + * (ported from C code written by Markus W. Scherer) + */ + +package jdk_internal.icu.text; + +import java.util.Arrays; + +import jdk_internal.bidi.Bidi; + +final class BidiLine { + + /* + * 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 3.0 Bidi algorithm as defined in Unicode Standard Annex #9: Unicode + * Bidirectional Algorithm, version 13, also described in The Unicode + * Standard, Version 4.0.1 . + * + * This means that there is a Bidi 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 getLogical/VisualIndex() methods 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 methods 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 Bidi object to use the same levels array as its paragraph + * parent object. + * + * When a Bidi 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 method is called only from setLine(), so paraLevel is set correctly for + * the line even when contextual multiple paragraphs. + */ + + static void setTrailingWSStart(BidiBase bidiBase) { + byte[] dirProps = bidiBase.dirProps; + byte[] levels = bidiBase.levels; + int start = bidiBase.length; + byte paraLevel = bidiBase.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 getLevels when + * orderParagraphsLTR==TRUE + */ + if (dirProps[start - 1] == BidiBase.B) { + bidiBase.trailingWSStart = start; /* currently == bidiBase.length */ + return; + } + /* go backwards across all WS, BN, explicit codes */ + while (start > 0 && (BidiBase.DirPropFlag(dirProps[start - 1]) & BidiBase.MASK_WS) != 0) { + --start; + } + + /* if the WS run can be merged with the previous run then do so here */ + while (start > 0 && levels[start - 1] == paraLevel) { + --start; + } + + bidiBase.trailingWSStart = start; + } + + static Bidi setLine(BidiBase paraBidi, Bidi newBidi, BidiBase lineBidi, int start, int limit) { + int length; + + /* set the values in lineBidi from its paraBidi parent */ + /* class members are already initialized to 0 */ + // lineBidi.paraBidi = null; /* mark unfinished setLine */ + // lineBidi.flags = 0; + // lineBidi.controlCount = 0; + + length = lineBidi.length = lineBidi.originalLength = lineBidi.resultLength = limit - start; + + lineBidi.text = new char[length]; + System.arraycopy(paraBidi.text, start, lineBidi.text, 0, length); + lineBidi.paraLevel = paraBidi.GetParaLevelAt(start); + lineBidi.paraCount = paraBidi.paraCount; + lineBidi.runs = new BidiRun[0]; + lineBidi.reorderingMode = paraBidi.reorderingMode; + lineBidi.reorderingOptions = paraBidi.reorderingOptions; + if (paraBidi.controlCount > 0) { + int j; + for (j = start; j < limit; j++) { + if (BidiBase.IsBidiControlChar(paraBidi.text[j])) { + lineBidi.controlCount++; + } + } + lineBidi.resultLength -= lineBidi.controlCount; + } + /* copy proper subset of DirProps */ + lineBidi.getDirPropsMemory(length); + lineBidi.dirProps = lineBidi.dirPropsMemory; + System.arraycopy(paraBidi.dirProps, start, lineBidi.dirProps, 0, length); + /* copy proper subset of Levels */ + lineBidi.getLevelsMemory(length); + lineBidi.levels = lineBidi.levelsMemory; + System.arraycopy(paraBidi.levels, start, lineBidi.levels, 0, length); + lineBidi.runCount = -1; + + if (paraBidi.direction != BidiBase.MIXED) { + /* the parent is already trivial */ + lineBidi.direction = paraBidi.direction; + + /* + * The parent's levels are all either implicitly or explicitly ==paraLevel; do + * the same here. + */ + if (paraBidi.trailingWSStart <= start) { + lineBidi.trailingWSStart = 0; + } else if (paraBidi.trailingWSStart < limit) { + lineBidi.trailingWSStart = paraBidi.trailingWSStart - start; + } else { + lineBidi.trailingWSStart = length; + } + } else { + byte[] levels = lineBidi.levels; + int i, trailingWSStart; + byte level; + + setTrailingWSStart(lineBidi); + trailingWSStart = lineBidi.trailingWSStart; + + /* recalculate lineBidiBase.direction */ + if (trailingWSStart == 0) { + /* all levels are at paraLevel */ + lineBidi.direction = (byte) (lineBidi.paraLevel & 1); + } else { + /* get the level of the first character */ + level = (byte) (levels[0] & 1); + + /* + * if there is anything of a different level, then the line is mixed + */ + if (trailingWSStart < length && (lineBidi.paraLevel & 1) != level) { + /* + * the trailing WS is at paraLevel, which differs from levels[0] + */ + lineBidi.direction = BidiBase.MIXED; + } else { + /* + * see if levels[1..trailingWSStart-1] have the same direction as levels[0] and + * paraLevel + */ + for (i = 1;; i++) { + if (i == trailingWSStart) { + /* the direction values match those in level */ + lineBidi.direction = level; + break; + } else if ((levels[i] & 1) != level) { + lineBidi.direction = BidiBase.MIXED; + break; + } + } + } + } + + switch (lineBidi.direction) { + case Bidi.DIRECTION_LEFT_TO_RIGHT: + /* make sure paraLevel is even */ + lineBidi.paraLevel = (byte) ((lineBidi.paraLevel + 1) & ~1); + + /* + * all levels are implicitly at paraLevel (important for getLevels()) + */ + lineBidi.trailingWSStart = 0; + break; + case Bidi.DIRECTION_RIGHT_TO_LEFT: + /* make sure paraLevel is odd */ + lineBidi.paraLevel |= 1; + + /* + * all levels are implicitly at paraLevel (important for getLevels()) + */ + lineBidi.trailingWSStart = 0; + break; + default: + break; + } + } + + lineBidi.paraBidi = paraBidi; /* mark successful setLine */ + + return newBidi; + } + + static byte getLevelAt(BidiBase bidiBase, int charIndex) { + /* return paraLevel if in the trailing WS run, otherwise the real level */ + if (bidiBase.direction != BidiBase.MIXED || charIndex >= bidiBase.trailingWSStart) { + return bidiBase.GetParaLevelAt(charIndex); + } else { + return bidiBase.levels[charIndex]; + } + } + + static byte[] getLevels(BidiBase bidiBase) { + int start = bidiBase.trailingWSStart; + int length = bidiBase.length; + + if (start != length) { + /* the current levels array does not reflect the WS run */ + /* + * 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 Bidi object for a line, and we need to create a new levels + * array. + */ + /* + * bidiBase.paraLevel is ok even if contextual multiple paragraphs, since + * bidiBase is a line object + */ + Arrays.fill(bidiBase.levels, start, length, bidiBase.paraLevel); + + /* this new levels array is set for the line and reflects the WS run */ + bidiBase.trailingWSStart = length; + } + if (length < bidiBase.levels.length) { + byte[] levels = new byte[length]; + System.arraycopy(bidiBase.levels, 0, levels, 0, length); + return levels; + } + return bidiBase.levels; + } + + static BidiRun getVisualRun(BidiBase bidiBase, int runIndex) { + int start = bidiBase.runs[runIndex].start; + int limit; + byte level = bidiBase.runs[runIndex].level; + + if (runIndex > 0) { + limit = start + bidiBase.runs[runIndex].limit - bidiBase.runs[runIndex - 1].limit; + } else { + limit = start + bidiBase.runs[0].limit; + } + return new BidiRun(start, limit, level); + } + + /* in trivial cases there is only one trivial run; called by getRuns() */ + private static void getSingleRun(BidiBase bidiBase, byte level) { + /* simple, single-run case */ + bidiBase.runs = bidiBase.simpleRuns; + bidiBase.runCount = 1; + + /* fill and reorder the single run */ + bidiBase.runs[0] = new BidiRun(0, bidiBase.length, level); + } + + /* 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. + */ + private static void reorderLine(BidiBase bidiBase, byte minLevel, byte maxLevel) { + + /* nothing to do? */ + if (maxLevel <= (minLevel | 1)) { + return; + } + + BidiRun[] runs; + BidiRun tempRun; + byte[] levels; + int firstRun, endRun, limitRun, runCount; + + /* + * 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 = bidiBase.runs; + levels = bidiBase.levels; + runCount = bidiBase.runCount; + + /* + * do not include the WS run at paraLevel<=old minLevel except in the simple + * loop + */ + if (bidiBase.trailingWSStart < bidiBase.length) { + --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 && levels[runs[firstRun].start] < maxLevel) { + ++firstRun; + } + if (firstRun >= runCount) { + break; /* no more such runs */ + } + + /* look for the limit run of such a sequence (the run behind it) */ + for (limitRun = firstRun; ++limitRun < runCount && levels[runs[limitRun].start] >= maxLevel;) { + } + + /* Swap the entire sequence of runs from firstRun to limitRun-1. */ + endRun = limitRun - 1; + while (firstRun < endRun) { + tempRun = runs[firstRun]; + runs[firstRun] = runs[endRun]; + runs[endRun] = tempRun; + ++firstRun; + --endRun; + } + + if (limitRun == runCount) { + break; /* no more such runs */ + } else { + firstRun = limitRun + 1; + } + } + } + + /* now do maxLevel==old minLevel (==odd!), see above */ + if ((minLevel & 1) == 0) { + firstRun = 0; + + /* include the trailing WS run in this complete reordering */ + if (bidiBase.trailingWSStart == bidiBase.length) { + --runCount; + } + + /* Swap the entire sequence of all runs. (endRun==runCount) */ + while (firstRun < runCount) { + tempRun = runs[firstRun]; + runs[firstRun] = runs[runCount]; + runs[runCount] = tempRun; + ++firstRun; + --runCount; + } + } + } + + /* compute the runs array --------------------------------------------------- */ + + static int getRunFromLogicalIndex(BidiBase bidiBase, int logicalIndex) { + BidiRun[] runs = bidiBase.runs; + int runCount = bidiBase.runCount, visualStart = 0, i, length, logicalStart; + + for (i = 0; i < runCount; i++) { + length = runs[i].limit - visualStart; + logicalStart = runs[i].start; + if ((logicalIndex >= logicalStart) && (logicalIndex < (logicalStart + length))) { + return i; + } + visualStart += length; + } + /* we should never get here */ + throw new IllegalStateException("Internal ICU error in getRunFromLogicalIndex"); + } + + /* + * Compute the runs array from the levels array. After 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 OPTION_INSERT_MARKS is set, insertRemove will contain the sum + * of appropriate LRM/RLM_BEFORE/AFTER flags. If option OPTION_REMOVE_CONTROLS + * is set, insertRemove will contain the negative number of BiDi control + * characters within this run. + */ + static void getRuns(BidiBase bidiBase) { + /* + * This method returns immediately if the runs are already set. This includes + * the case of length==0 (handled in setPara).. + */ + if (bidiBase.runCount >= 0) { + return; + } + if (bidiBase.direction != BidiBase.MIXED) { + /* simple, single-run case - this covers length==0 */ + /* bidiBase.paraLevel is ok even for contextual multiple paragraphs */ + getSingleRun(bidiBase, bidiBase.paraLevel); + } else /* BidiBase.MIXED, length>0 */ { + /* mixed directionality */ + int length = bidiBase.length, limit; + byte[] levels = bidiBase.levels; + int i, runCount; + byte level = -1; /* 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 Bidi 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 = bidiBase.trailingWSStart; + /* count the runs, there is at least one non-WS run, and limit>0 */ + runCount = 0; + for (i = 0; i < limit; ++i) { + /* increment runCount at the start of each run */ + if (levels[i] != level) { + ++runCount; + level = levels[i]; + } + } + + /* + * We don't need to see if the last run can be merged with a trailing WS run + * because setTrailingWSStart() would have done that. + */ + if (runCount == 1 && limit == length) { + /* There is only one non-WS run and no trailing WS-run. */ + getSingleRun(bidiBase, levels[0]); + } else /* runCount>1 || limit 1 */ + bidiBase.getRunsMemory(runCount); + runs = bidiBase.runsMemory; + + /* 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 (level < minLevel) { + minLevel = level; + } + if (level > maxLevel) { + maxLevel = level; + } + + /* look for the run limit */ + while (++i < limit && levels[i] == level) { + } + + /* i is another run limit */ + runs[runIndex] = new BidiRun(start, i - start, level); + ++runIndex; + } while (i < limit); + + if (limit < length) { + /* there is a separate WS run */ + runs[runIndex] = new BidiRun(limit, length - limit, bidiBase.paraLevel); + /* + * For the trailing WS run, bidiBase.paraLevel is ok even if contextual multiple + * paragraphs. + */ + if (bidiBase.paraLevel < minLevel) { + minLevel = bidiBase.paraLevel; + } + } + + /* set the object fields */ + bidiBase.runs = runs; + bidiBase.runCount = runCount; + + reorderLine(bidiBase, 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; i < runCount; ++i) { + runs[i].level = levels[runs[i].start]; + limit = (runs[i].limit += limit); + } + + /* Set the embedding level for the trailing WS run. */ + /* For a RTL paragraph, it will be the *first* run in visual order. */ + /* + * For the trailing WS run, bidiBase.paraLevel is ok even if contextual multiple + * paragraphs. + */ + if (runIndex < runCount) { + int trailingRun = ((bidiBase.paraLevel & 1) != 0) ? 0 : runIndex; + runs[trailingRun].level = bidiBase.paraLevel; + } + } + } + + /* handle insert LRM/RLM BEFORE/AFTER run */ + if (bidiBase.insertPoints.size > 0) { + BidiBase.Point point; + int runIndex, ip; + for (ip = 0; ip < bidiBase.insertPoints.size; ip++) { + point = bidiBase.insertPoints.points[ip]; + runIndex = getRunFromLogicalIndex(bidiBase, point.pos); + bidiBase.runs[runIndex].insertRemove |= point.flag; + } + } + + /* handle remove BiDi control characters */ + if (bidiBase.controlCount > 0) { + int runIndex, ic; + char c; + for (ic = 0; ic < bidiBase.length; ic++) { + c = bidiBase.text[ic]; + if (BidiBase.IsBidiControlChar(c)) { + runIndex = getRunFromLogicalIndex(bidiBase, ic); + bidiBase.runs[runIndex].insertRemove--; + } + } + } + } + + static int[] prepareReorder(byte[] levels, byte[] pMinLevel, byte[] pMaxLevel) { + int start; + byte level, minLevel, maxLevel; + + if (levels == null || levels.length <= 0) { + return null; + } + + /* determine minLevel and maxLevel */ + minLevel = BidiBase.MAX_EXPLICIT_LEVEL + 1; + maxLevel = 0; + for (start = levels.length; start > 0;) { + level = levels[--start]; + if (level < 0 || level > (BidiBase.MAX_EXPLICIT_LEVEL + 1)) { + return null; + } + if (level < minLevel) { + minLevel = level; + } + if (level > maxLevel) { + maxLevel = level; + } + } + pMinLevel[0] = minLevel; + pMaxLevel[0] = maxLevel; + + /* initialize the index map */ + int[] indexMap = new int[levels.length]; + for (start = levels.length; start > 0;) { + --start; + indexMap[start] = start; + } + + return indexMap; + } + + static int[] reorderVisual(byte[] levels) { + byte[] aMinLevel = new byte[1]; + byte[] aMaxLevel = new byte[1]; + int start, end, limit, temp; + byte minLevel, maxLevel; + + int[] indexMap = prepareReorder(levels, aMinLevel, aMaxLevel); + if (indexMap == null) { + return null; + } + + minLevel = aMinLevel[0]; + maxLevel = aMaxLevel[0]; + + /* nothing to do? */ + if (minLevel == maxLevel && (minLevel & 1) == 0) { + return indexMap; + } + + /* 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 < levels.length && levels[start] < maxLevel) { + ++start; + } + if (start >= levels.length) { + break; /* no more such runs */ + } + + /* look for the limit of such a sequence (the index behind it) */ + for (limit = start; ++limit < levels.length && levels[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 < end) { + temp = indexMap[start]; + indexMap[start] = indexMap[end]; + indexMap[end] = temp; + + ++start; + --end; + } + + if (limit == levels.length) { + break; /* no more such sequences */ + } else { + start = limit + 1; + } + } + } while (--maxLevel >= minLevel); + + return indexMap; + } + + static int[] getVisualMap(BidiBase bidiBase) { + /* fill a visual-to-logical index map using the runs[] */ + BidiRun[] runs = bidiBase.runs; + int logicalStart, visualStart, visualLimit; + int allocLength = bidiBase.length > bidiBase.resultLength ? bidiBase.length : bidiBase.resultLength; + int[] indexMap = new int[allocLength]; + + visualStart = 0; + int idx = 0; + for (int j = 0; j < bidiBase.runCount; ++j) { + logicalStart = runs[j].start; + visualLimit = runs[j].limit; + if (runs[j].isEvenRun()) { + do { /* LTR */ + indexMap[idx++] = logicalStart++; + } while (++visualStart < visualLimit); + } else { + logicalStart += visualLimit - visualStart; /* logicalLimit */ + do { /* RTL */ + indexMap[idx++] = --logicalStart; + } while (++visualStart < visualLimit); + } + /* visualStart==visualLimit; */ + } + + if (bidiBase.insertPoints.size > 0) { + int markFound = 0, runCount = bidiBase.runCount; + int insertRemove, i, j, k; + runs = bidiBase.runs; + /* count all inserted marks */ + for (i = 0; i < runCount; i++) { + insertRemove = runs[i].insertRemove; + if ((insertRemove & (BidiBase.LRM_BEFORE | BidiBase.RLM_BEFORE)) > 0) { + markFound++; + } + if ((insertRemove & (BidiBase.LRM_AFTER | BidiBase.RLM_AFTER)) > 0) { + markFound++; + } + } + /* move back indexes by number of preceding marks */ + k = bidiBase.resultLength; + for (i = runCount - 1; i >= 0 && markFound > 0; i--) { + insertRemove = runs[i].insertRemove; + if ((insertRemove & (BidiBase.LRM_AFTER | BidiBase.RLM_AFTER)) > 0) { + indexMap[--k] = BidiBase.MAP_NOWHERE; + markFound--; + } + visualStart = i > 0 ? runs[i - 1].limit : 0; + for (j = runs[i].limit - 1; j >= visualStart && markFound > 0; j--) { + indexMap[--k] = indexMap[j]; + } + if ((insertRemove & (BidiBase.LRM_BEFORE | BidiBase.RLM_BEFORE)) > 0) { + indexMap[--k] = BidiBase.MAP_NOWHERE; + markFound--; + } + } + } else if (bidiBase.controlCount > 0) { + int runCount = bidiBase.runCount, logicalEnd; + int insertRemove, length, i, j, k, m; + char uchar; + boolean evenRun; + runs = bidiBase.runs; + visualStart = 0; + /* move forward indexes by number of preceding controls */ + k = 0; + for (i = 0; i < runCount; i++, visualStart += length) { + length = runs[i].limit - visualStart; + insertRemove = runs[i].insertRemove; + /* if no control found yet, nothing to do in this run */ + if ((insertRemove == 0) && (k == visualStart)) { + k += length; + continue; + } + /* if no control in this run */ + if (insertRemove == 0) { + visualLimit = runs[i].limit; + for (j = visualStart; j < visualLimit; j++) { + indexMap[k++] = indexMap[j]; + } + continue; + } + logicalStart = runs[i].start; + evenRun = runs[i].isEvenRun(); + logicalEnd = logicalStart + length - 1; + for (j = 0; j < length; j++) { + m = evenRun ? logicalStart + j : logicalEnd - j; + uchar = bidiBase.text[m]; + if (!BidiBase.IsBidiControlChar(uchar)) { + indexMap[k++] = m; + } + } + } + } + if (allocLength == bidiBase.resultLength) { + return indexMap; + } + int[] newMap = new int[bidiBase.resultLength]; + System.arraycopy(indexMap, 0, newMap, 0, bidiBase.resultLength); + return newMap; + } + +} diff --git a/src/main/java/jdk_internal/icu/text/BidiRun.java b/src/main/java/jdk_internal/icu/text/BidiRun.java index 333af89e..87465256 100755 --- a/src/main/java/jdk_internal/icu/text/BidiRun.java +++ b/src/main/java/jdk_internal/icu/text/BidiRun.java @@ -1,123 +1,123 @@ -/* - * Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -/* - ******************************************************************************* - * (C) Copyright IBM Corp. and others, 1996-2009 - All Rights Reserved * - * * - * The original version of this source code and documentation is copyrighted * - * and owned by IBM, These materials are provided under terms of a License * - * Agreement between IBM and Sun. This technology is protected by multiple * - * US and International patents. This notice and attribution to IBM may not * - * to removed. * - ******************************************************************************* - */ -/* Written by Simon Montagu, Matitiahu Allouche - * (ported from C code written by Markus W. Scherer) - */ - -package jdk_internal.icu.text; - -/** - * A BidiRun represents a sequence of characters at the same embedding level. - * The Bidi algorithm decomposes a piece of text into sequences of characters at - * the same embedding level, each such sequence is called a "run". - * - *

- * A BidiRun represents such a run by storing its essential properties, but does - * not duplicate the characters which form the run. - * - *

- * The "limit" of the run is the position just after the last - * character, i.e., one more than that position. - * - *

- * This class has no public constructor, and its members cannot be modified by - * users. - * - * @see com.ibm.icu.text.Bidi - */ -class BidiRun { - - int start; /* first logical position of the run */ - int limit; /* last visual position of the run +1 */ - int insertRemove; /* - * if >0, flags for inserting LRM/RLM before/after run, if <0, count of bidi - * controls within run - */ - byte level; - - /* - * Default constructor - * - * Note that members start and limit of a run instance have different meanings - * depending whether the run is part of the runs array of a Bidi object, or if - * it is a reference returned by getVisualRun() or getLogicalRun(). For a member - * of the runs array of a Bidi object, - start is the first logical position of - * the run in the source text. - limit is one after the last visual position of - * the run. For a reference returned by getLogicalRun() or getVisualRun(), - - * start is the first logical position of the run in the source text. - limit is - * one after the last logical position of the run. - */ - BidiRun() { - this(0, 0, (byte) 0); - } - - /* - * Constructor - */ - BidiRun(int start, int limit, byte embeddingLevel) { - this.start = start; - this.limit = limit; - this.level = embeddingLevel; - } - - /* - * Copy the content of a BidiRun instance - */ - void copyFrom(BidiRun run) { - this.start = run.start; - this.limit = run.limit; - this.level = run.level; - this.insertRemove = run.insertRemove; - } - - /** - * Get level of run - */ - byte getEmbeddingLevel() { - return level; - } - - /** - * Check if run level is even - * - * @return true if the embedding level of this run is even, i.e. it is a - * left-to-right run. - */ - boolean isEvenRun() { - return (level & 1) == 0; - } - -} +/* + * Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + ******************************************************************************* + * (C) Copyright IBM Corp. and others, 1996-2009 - All Rights Reserved * + * * + * The original version of this source code and documentation is copyrighted * + * and owned by IBM, These materials are provided under terms of a License * + * Agreement between IBM and Sun. This technology is protected by multiple * + * US and International patents. This notice and attribution to IBM may not * + * to removed. * + ******************************************************************************* + */ +/* Written by Simon Montagu, Matitiahu Allouche + * (ported from C code written by Markus W. Scherer) + */ + +package jdk_internal.icu.text; + +/** + * A BidiRun represents a sequence of characters at the same embedding level. + * The Bidi algorithm decomposes a piece of text into sequences of characters at + * the same embedding level, each such sequence is called a "run". + * + *

+ * A BidiRun represents such a run by storing its essential properties, but does + * not duplicate the characters which form the run. + * + *

+ * The "limit" of the run is the position just after the last + * character, i.e., one more than that position. + * + *

+ * This class has no public constructor, and its members cannot be modified by + * users. + * + * @see com.ibm.icu.text.Bidi + */ +class BidiRun { + + int start; /* first logical position of the run */ + int limit; /* last visual position of the run +1 */ + int insertRemove; /* + * if >0, flags for inserting LRM/RLM before/after run, if <0, count of bidi + * controls within run + */ + byte level; + + /* + * Default constructor + * + * Note that members start and limit of a run instance have different meanings + * depending whether the run is part of the runs array of a Bidi object, or if + * it is a reference returned by getVisualRun() or getLogicalRun(). For a member + * of the runs array of a Bidi object, - start is the first logical position of + * the run in the source text. - limit is one after the last visual position of + * the run. For a reference returned by getLogicalRun() or getVisualRun(), - + * start is the first logical position of the run in the source text. - limit is + * one after the last logical position of the run. + */ + BidiRun() { + this(0, 0, (byte) 0); + } + + /* + * Constructor + */ + BidiRun(int start, int limit, byte embeddingLevel) { + this.start = start; + this.limit = limit; + this.level = embeddingLevel; + } + + /* + * Copy the content of a BidiRun instance + */ + void copyFrom(BidiRun run) { + this.start = run.start; + this.limit = run.limit; + this.level = run.level; + this.insertRemove = run.insertRemove; + } + + /** + * Get level of run + */ + byte getEmbeddingLevel() { + return level; + } + + /** + * Check if run level is even + * + * @return true if the embedding level of this run is even, i.e. it is a + * left-to-right run. + */ + boolean isEvenRun() { + return (level & 1) == 0; + } + +} diff --git a/src/main/java/jdk_internal/icu/text/BidiWriter.java b/src/main/java/jdk_internal/icu/text/BidiWriter.java index 60339207..86081702 100755 --- a/src/main/java/jdk_internal/icu/text/BidiWriter.java +++ b/src/main/java/jdk_internal/icu/text/BidiWriter.java @@ -1,425 +1,425 @@ -/* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* -******************************************************************************* -* Copyright (C) 2001-2010, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -*/ -/* Written by Simon Montagu, Matitiahu Allouche - * (ported from C code written by Markus W. Scherer) - */ - -package jdk_internal.icu.text; - -import jdk_internal.icu.lang.UCharacter; - -final class BidiWriter { - - /** Bidi control code points */ - static final char LRM_CHAR = 0x200e; - static final char RLM_CHAR = 0x200f; - static final int MASK_R_AL = (1 << UCharacter.RIGHT_TO_LEFT | 1 << UCharacter.RIGHT_TO_LEFT_ARABIC); - - private static boolean IsCombining(int type) { - return ((1 << type & (1 << UCharacter.NON_SPACING_MARK | 1 << UCharacter.COMBINING_SPACING_MARK - | 1 << UCharacter.ENCLOSING_MARK)) != 0); - } - - /* - * When we have OUTPUT_REVERSE set on writeReordered(), then we semantically - * write RTL runs in reverse and later reverse them again. Instead, we actually - * write them in forward order to begin with. However, if the RTL run was to be - * mirrored, we need to mirror here now since the implicit second reversal must - * not do it. It looks strange to do mirroring in LTR output, but it is only - * because we are writing RTL output in reverse. - */ - private static String doWriteForward(String src, int options) { - /* optimize for several combinations of options */ - switch (options & (BidiBase.REMOVE_BIDI_CONTROLS | BidiBase.DO_MIRRORING)) { - case 0: { - /* simply return the LTR run */ - return src; - } - case BidiBase.DO_MIRRORING: { - StringBuffer dest = new StringBuffer(src.length()); - - /* do mirroring */ - int i = 0; - int c; - - do { - c = UTF16.charAt(src, i); - i += UTF16.getCharCount(c); - UTF16.append(dest, UCharacter.getMirror(c)); - } while (i < src.length()); - return dest.toString(); - } - case BidiBase.REMOVE_BIDI_CONTROLS: { - StringBuilder dest = new StringBuilder(src.length()); - - /* copy the LTR run and remove any Bidi control characters */ - int i = 0; - char c; - do { - c = src.charAt(i++); - if (!BidiBase.IsBidiControlChar(c)) { - dest.append(c); - } - } while (i < src.length()); - return dest.toString(); - } - default: { - StringBuffer dest = new StringBuffer(src.length()); - - /* remove Bidi control characters and do mirroring */ - int i = 0; - int c; - do { - c = UTF16.charAt(src, i); - i += UTF16.getCharCount(c); - if (!BidiBase.IsBidiControlChar(c)) { - UTF16.append(dest, UCharacter.getMirror(c)); - } - } while (i < src.length()); - return dest.toString(); - } - } /* end of switch */ - } - - private static String doWriteForward(char[] text, int start, int limit, int options) { - return doWriteForward(new String(text, start, limit - start), options); - } - - static String writeReverse(String src, int options) { - /* - * 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. - */ - StringBuffer dest = new StringBuffer(src.length()); - - /* optimize for several combinations of options */ - switch (options & (BidiBase.REMOVE_BIDI_CONTROLS | BidiBase.DO_MIRRORING | BidiBase.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. - * - * XXX: or dest = UTF16.reverse(new StringBuffer(src)); - */ - - int srcLength = src.length(); - - /* preserve character integrity */ - do { - /* - * i is always after the last code unit known to need to be kept in this segment - */ - int i = srcLength; - - /* collect code units for one base character */ - srcLength -= UTF16.getCharCount(UTF16.charAt(src, srcLength - 1)); - - /* copy this base character */ - dest.append(src.substring(srcLength, i)); - } while (srcLength > 0); - break; - - case BidiBase.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. - */ - srcLength = src.length(); - - /* preserve character integrity */ - do { - /* - * i is always after the last code unit known to need to be kept in this segment - */ - int c; - int i = srcLength; - - /* - * collect code units and modifier letters for one base character - */ - do { - c = UTF16.charAt(src, srcLength - 1); - srcLength -= UTF16.getCharCount(c); - } while (srcLength > 0 && IsCombining(UCharacter.getType(c))); - - /* copy this "user character" */ - dest.append(src.substring(srcLength, i)); - } while (srcLength > 0); - 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. - */ - srcLength = src.length(); - - /* preserve character integrity */ - do { - /* - * i is always after the last code unit known to need to be kept in this segment - */ - int i = srcLength; - - /* collect code units for one base character */ - int c = UTF16.charAt(src, srcLength - 1); - srcLength -= UTF16.getCharCount(c); - if ((options & BidiBase.KEEP_BASE_COMBINING) != 0) { - /* collect modifier letters for this base character */ - while (srcLength > 0 && IsCombining(UCharacter.getType(c))) { - c = UTF16.charAt(src, srcLength - 1); - srcLength -= UTF16.getCharCount(c); - } - } - - if ((options & BidiBase.REMOVE_BIDI_CONTROLS) != 0 && BidiBase.IsBidiControlChar(c)) { - /* do not copy this Bidi control character */ - continue; - } - - /* copy this "user character" */ - int j = srcLength; - if ((options & BidiBase.DO_MIRRORING) != 0) { - /* mirror only the base character */ - c = UCharacter.getMirror(c); - UTF16.append(dest, c); - j += UTF16.getCharCount(c); - } - dest.append(src.substring(j, i)); - } while (srcLength > 0); - break; - } /* end of switch */ - - return dest.toString(); - } - - static String doWriteReverse(char[] text, int start, int limit, int options) { - return writeReverse(new String(text, start, limit - start), options); - } - - static String writeReordered(BidiBase bidi, int options) { - int run, runCount; - StringBuilder dest; - char[] text = bidi.text; - runCount = bidi.countRuns(); - - /* - * Option "insert marks" implies BidiBase.INSERT_LRM_FOR_NUMERIC if the - * reordering mode (checked below) is appropriate. - */ - if ((bidi.reorderingOptions & BidiBase.OPTION_INSERT_MARKS) != 0) { - options |= BidiBase.INSERT_LRM_FOR_NUMERIC; - options &= ~BidiBase.REMOVE_BIDI_CONTROLS; - } - /* - * Option "remove controls" implies BidiBase.REMOVE_BIDI_CONTROLS and cancels - * BidiBase.INSERT_LRM_FOR_NUMERIC. - */ - if ((bidi.reorderingOptions & BidiBase.OPTION_REMOVE_CONTROLS) != 0) { - options |= BidiBase.REMOVE_BIDI_CONTROLS; - options &= ~BidiBase.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 ((bidi.reorderingMode != BidiBase.REORDER_INVERSE_NUMBERS_AS_L) - && (bidi.reorderingMode != BidiBase.REORDER_INVERSE_LIKE_DIRECT) - && (bidi.reorderingMode != BidiBase.REORDER_INVERSE_FOR_NUMBERS_SPECIAL) - && (bidi.reorderingMode != BidiBase.REORDER_RUNS_ONLY)) { - options &= ~BidiBase.INSERT_LRM_FOR_NUMERIC; - } - dest = new StringBuilder((options & BidiBase.INSERT_LRM_FOR_NUMERIC) != 0 ? bidi.length * 2 : bidi.length); - /* - * 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). - */ - if ((options & BidiBase.OUTPUT_REVERSE) == 0) { - /* forward output */ - if ((options & BidiBase.INSERT_LRM_FOR_NUMERIC) == 0) { - /* do not insert Bidi controls */ - for (run = 0; run < runCount; ++run) { - BidiRun bidiRun = bidi.getVisualRun(run); - if (bidiRun.isEvenRun()) { - dest.append( - doWriteForward(text, bidiRun.start, bidiRun.limit, options & ~BidiBase.DO_MIRRORING)); - } else { - dest.append(doWriteReverse(text, bidiRun.start, bidiRun.limit, options)); - } - } - } else { - /* insert Bidi controls for "inverse Bidi" */ - byte[] dirProps = bidi.dirProps; - char uc; - int markFlag; - - for (run = 0; run < runCount; ++run) { - BidiRun bidiRun = bidi.getVisualRun(run); - markFlag = 0; - /* check if something relevant in insertPoints */ - markFlag = bidi.runs[run].insertRemove; - if (markFlag < 0) { /* bidi controls count */ - markFlag = 0; - } - if (bidiRun.isEvenRun()) { - if (bidi.isInverse() && dirProps[bidiRun.start] != BidiBase.L) { - markFlag |= BidiBase.LRM_BEFORE; - } - if ((markFlag & BidiBase.LRM_BEFORE) != 0) { - uc = LRM_CHAR; - } else if ((markFlag & BidiBase.RLM_BEFORE) != 0) { - uc = RLM_CHAR; - } else { - uc = 0; - } - if (uc != 0) { - dest.append(uc); - } - dest.append( - doWriteForward(text, bidiRun.start, bidiRun.limit, options & ~BidiBase.DO_MIRRORING)); - - if (bidi.isInverse() && dirProps[bidiRun.limit - 1] != BidiBase.L) { - markFlag |= BidiBase.LRM_AFTER; - } - if ((markFlag & BidiBase.LRM_AFTER) != 0) { - uc = LRM_CHAR; - } else if ((markFlag & BidiBase.RLM_AFTER) != 0) { - uc = RLM_CHAR; - } else { - uc = 0; - } - if (uc != 0) { - dest.append(uc); - } - } else { /* RTL run */ - if (bidi.isInverse() && !bidi.testDirPropFlagAt(MASK_R_AL, bidiRun.limit - 1)) { - markFlag |= BidiBase.RLM_BEFORE; - } - if ((markFlag & BidiBase.LRM_BEFORE) != 0) { - uc = LRM_CHAR; - } else if ((markFlag & BidiBase.RLM_BEFORE) != 0) { - uc = RLM_CHAR; - } else { - uc = 0; - } - if (uc != 0) { - dest.append(uc); - } - dest.append(doWriteReverse(text, bidiRun.start, bidiRun.limit, options)); - - if (bidi.isInverse() && (MASK_R_AL & BidiBase.DirPropFlag(dirProps[bidiRun.start])) == 0) { - markFlag |= BidiBase.RLM_AFTER; - } - if ((markFlag & BidiBase.LRM_AFTER) != 0) { - uc = LRM_CHAR; - } else if ((markFlag & BidiBase.RLM_AFTER) != 0) { - uc = RLM_CHAR; - } else { - uc = 0; - } - if (uc != 0) { - dest.append(uc); - } - } - } - } - } else { - /* reverse output */ - if ((options & BidiBase.INSERT_LRM_FOR_NUMERIC) == 0) { - /* do not insert Bidi controls */ - for (run = runCount; --run >= 0;) { - BidiRun bidiRun = bidi.getVisualRun(run); - if (bidiRun.isEvenRun()) { - dest.append( - doWriteReverse(text, bidiRun.start, bidiRun.limit, options & ~BidiBase.DO_MIRRORING)); - } else { - dest.append(doWriteForward(text, bidiRun.start, bidiRun.limit, options)); - } - } - } else { - /* insert Bidi controls for "inverse Bidi" */ - - byte[] dirProps = bidi.dirProps; - - for (run = runCount; --run >= 0;) { - /* reverse output */ - BidiRun bidiRun = bidi.getVisualRun(run); - if (bidiRun.isEvenRun()) { - if (dirProps[bidiRun.limit - 1] != BidiBase.L) { - dest.append(LRM_CHAR); - } - - dest.append( - doWriteReverse(text, bidiRun.start, bidiRun.limit, options & ~BidiBase.DO_MIRRORING)); - - if (dirProps[bidiRun.start] != BidiBase.L) { - dest.append(LRM_CHAR); - } - } else { - if ((MASK_R_AL & BidiBase.DirPropFlag(dirProps[bidiRun.start])) == 0) { - dest.append(RLM_CHAR); - } - - dest.append(doWriteForward(text, bidiRun.start, bidiRun.limit, options)); - - if ((MASK_R_AL & BidiBase.DirPropFlag(dirProps[bidiRun.limit - 1])) == 0) { - dest.append(RLM_CHAR); - } - } - } - } - } - - return dest.toString(); - } -} +/* + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* +******************************************************************************* +* Copyright (C) 2001-2010, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +*/ +/* Written by Simon Montagu, Matitiahu Allouche + * (ported from C code written by Markus W. Scherer) + */ + +package jdk_internal.icu.text; + +import jdk_internal.icu.lang.UCharacter; + +final class BidiWriter { + + /** Bidi control code points */ + static final char LRM_CHAR = 0x200e; + static final char RLM_CHAR = 0x200f; + static final int MASK_R_AL = (1 << UCharacter.RIGHT_TO_LEFT | 1 << UCharacter.RIGHT_TO_LEFT_ARABIC); + + private static boolean IsCombining(int type) { + return ((1 << type & (1 << UCharacter.NON_SPACING_MARK | 1 << UCharacter.COMBINING_SPACING_MARK + | 1 << UCharacter.ENCLOSING_MARK)) != 0); + } + + /* + * When we have OUTPUT_REVERSE set on writeReordered(), then we semantically + * write RTL runs in reverse and later reverse them again. Instead, we actually + * write them in forward order to begin with. However, if the RTL run was to be + * mirrored, we need to mirror here now since the implicit second reversal must + * not do it. It looks strange to do mirroring in LTR output, but it is only + * because we are writing RTL output in reverse. + */ + private static String doWriteForward(String src, int options) { + /* optimize for several combinations of options */ + switch (options & (BidiBase.REMOVE_BIDI_CONTROLS | BidiBase.DO_MIRRORING)) { + case 0: { + /* simply return the LTR run */ + return src; + } + case BidiBase.DO_MIRRORING: { + StringBuffer dest = new StringBuffer(src.length()); + + /* do mirroring */ + int i = 0; + int c; + + do { + c = UTF16.charAt(src, i); + i += UTF16.getCharCount(c); + UTF16.append(dest, UCharacter.getMirror(c)); + } while (i < src.length()); + return dest.toString(); + } + case BidiBase.REMOVE_BIDI_CONTROLS: { + StringBuilder dest = new StringBuilder(src.length()); + + /* copy the LTR run and remove any Bidi control characters */ + int i = 0; + char c; + do { + c = src.charAt(i++); + if (!BidiBase.IsBidiControlChar(c)) { + dest.append(c); + } + } while (i < src.length()); + return dest.toString(); + } + default: { + StringBuffer dest = new StringBuffer(src.length()); + + /* remove Bidi control characters and do mirroring */ + int i = 0; + int c; + do { + c = UTF16.charAt(src, i); + i += UTF16.getCharCount(c); + if (!BidiBase.IsBidiControlChar(c)) { + UTF16.append(dest, UCharacter.getMirror(c)); + } + } while (i < src.length()); + return dest.toString(); + } + } /* end of switch */ + } + + private static String doWriteForward(char[] text, int start, int limit, int options) { + return doWriteForward(new String(text, start, limit - start), options); + } + + static String writeReverse(String src, int options) { + /* + * 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. + */ + StringBuffer dest = new StringBuffer(src.length()); + + /* optimize for several combinations of options */ + switch (options & (BidiBase.REMOVE_BIDI_CONTROLS | BidiBase.DO_MIRRORING | BidiBase.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. + * + * XXX: or dest = UTF16.reverse(new StringBuffer(src)); + */ + + int srcLength = src.length(); + + /* preserve character integrity */ + do { + /* + * i is always after the last code unit known to need to be kept in this segment + */ + int i = srcLength; + + /* collect code units for one base character */ + srcLength -= UTF16.getCharCount(UTF16.charAt(src, srcLength - 1)); + + /* copy this base character */ + dest.append(src.substring(srcLength, i)); + } while (srcLength > 0); + break; + + case BidiBase.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. + */ + srcLength = src.length(); + + /* preserve character integrity */ + do { + /* + * i is always after the last code unit known to need to be kept in this segment + */ + int c; + int i = srcLength; + + /* + * collect code units and modifier letters for one base character + */ + do { + c = UTF16.charAt(src, srcLength - 1); + srcLength -= UTF16.getCharCount(c); + } while (srcLength > 0 && IsCombining(UCharacter.getType(c))); + + /* copy this "user character" */ + dest.append(src.substring(srcLength, i)); + } while (srcLength > 0); + 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. + */ + srcLength = src.length(); + + /* preserve character integrity */ + do { + /* + * i is always after the last code unit known to need to be kept in this segment + */ + int i = srcLength; + + /* collect code units for one base character */ + int c = UTF16.charAt(src, srcLength - 1); + srcLength -= UTF16.getCharCount(c); + if ((options & BidiBase.KEEP_BASE_COMBINING) != 0) { + /* collect modifier letters for this base character */ + while (srcLength > 0 && IsCombining(UCharacter.getType(c))) { + c = UTF16.charAt(src, srcLength - 1); + srcLength -= UTF16.getCharCount(c); + } + } + + if ((options & BidiBase.REMOVE_BIDI_CONTROLS) != 0 && BidiBase.IsBidiControlChar(c)) { + /* do not copy this Bidi control character */ + continue; + } + + /* copy this "user character" */ + int j = srcLength; + if ((options & BidiBase.DO_MIRRORING) != 0) { + /* mirror only the base character */ + c = UCharacter.getMirror(c); + UTF16.append(dest, c); + j += UTF16.getCharCount(c); + } + dest.append(src.substring(j, i)); + } while (srcLength > 0); + break; + } /* end of switch */ + + return dest.toString(); + } + + static String doWriteReverse(char[] text, int start, int limit, int options) { + return writeReverse(new String(text, start, limit - start), options); + } + + static String writeReordered(BidiBase bidi, int options) { + int run, runCount; + StringBuilder dest; + char[] text = bidi.text; + runCount = bidi.countRuns(); + + /* + * Option "insert marks" implies BidiBase.INSERT_LRM_FOR_NUMERIC if the + * reordering mode (checked below) is appropriate. + */ + if ((bidi.reorderingOptions & BidiBase.OPTION_INSERT_MARKS) != 0) { + options |= BidiBase.INSERT_LRM_FOR_NUMERIC; + options &= ~BidiBase.REMOVE_BIDI_CONTROLS; + } + /* + * Option "remove controls" implies BidiBase.REMOVE_BIDI_CONTROLS and cancels + * BidiBase.INSERT_LRM_FOR_NUMERIC. + */ + if ((bidi.reorderingOptions & BidiBase.OPTION_REMOVE_CONTROLS) != 0) { + options |= BidiBase.REMOVE_BIDI_CONTROLS; + options &= ~BidiBase.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 ((bidi.reorderingMode != BidiBase.REORDER_INVERSE_NUMBERS_AS_L) + && (bidi.reorderingMode != BidiBase.REORDER_INVERSE_LIKE_DIRECT) + && (bidi.reorderingMode != BidiBase.REORDER_INVERSE_FOR_NUMBERS_SPECIAL) + && (bidi.reorderingMode != BidiBase.REORDER_RUNS_ONLY)) { + options &= ~BidiBase.INSERT_LRM_FOR_NUMERIC; + } + dest = new StringBuilder((options & BidiBase.INSERT_LRM_FOR_NUMERIC) != 0 ? bidi.length * 2 : bidi.length); + /* + * 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). + */ + if ((options & BidiBase.OUTPUT_REVERSE) == 0) { + /* forward output */ + if ((options & BidiBase.INSERT_LRM_FOR_NUMERIC) == 0) { + /* do not insert Bidi controls */ + for (run = 0; run < runCount; ++run) { + BidiRun bidiRun = bidi.getVisualRun(run); + if (bidiRun.isEvenRun()) { + dest.append( + doWriteForward(text, bidiRun.start, bidiRun.limit, options & ~BidiBase.DO_MIRRORING)); + } else { + dest.append(doWriteReverse(text, bidiRun.start, bidiRun.limit, options)); + } + } + } else { + /* insert Bidi controls for "inverse Bidi" */ + byte[] dirProps = bidi.dirProps; + char uc; + int markFlag; + + for (run = 0; run < runCount; ++run) { + BidiRun bidiRun = bidi.getVisualRun(run); + markFlag = 0; + /* check if something relevant in insertPoints */ + markFlag = bidi.runs[run].insertRemove; + if (markFlag < 0) { /* bidi controls count */ + markFlag = 0; + } + if (bidiRun.isEvenRun()) { + if (bidi.isInverse() && dirProps[bidiRun.start] != BidiBase.L) { + markFlag |= BidiBase.LRM_BEFORE; + } + if ((markFlag & BidiBase.LRM_BEFORE) != 0) { + uc = LRM_CHAR; + } else if ((markFlag & BidiBase.RLM_BEFORE) != 0) { + uc = RLM_CHAR; + } else { + uc = 0; + } + if (uc != 0) { + dest.append(uc); + } + dest.append( + doWriteForward(text, bidiRun.start, bidiRun.limit, options & ~BidiBase.DO_MIRRORING)); + + if (bidi.isInverse() && dirProps[bidiRun.limit - 1] != BidiBase.L) { + markFlag |= BidiBase.LRM_AFTER; + } + if ((markFlag & BidiBase.LRM_AFTER) != 0) { + uc = LRM_CHAR; + } else if ((markFlag & BidiBase.RLM_AFTER) != 0) { + uc = RLM_CHAR; + } else { + uc = 0; + } + if (uc != 0) { + dest.append(uc); + } + } else { /* RTL run */ + if (bidi.isInverse() && !bidi.testDirPropFlagAt(MASK_R_AL, bidiRun.limit - 1)) { + markFlag |= BidiBase.RLM_BEFORE; + } + if ((markFlag & BidiBase.LRM_BEFORE) != 0) { + uc = LRM_CHAR; + } else if ((markFlag & BidiBase.RLM_BEFORE) != 0) { + uc = RLM_CHAR; + } else { + uc = 0; + } + if (uc != 0) { + dest.append(uc); + } + dest.append(doWriteReverse(text, bidiRun.start, bidiRun.limit, options)); + + if (bidi.isInverse() && (MASK_R_AL & BidiBase.DirPropFlag(dirProps[bidiRun.start])) == 0) { + markFlag |= BidiBase.RLM_AFTER; + } + if ((markFlag & BidiBase.LRM_AFTER) != 0) { + uc = LRM_CHAR; + } else if ((markFlag & BidiBase.RLM_AFTER) != 0) { + uc = RLM_CHAR; + } else { + uc = 0; + } + if (uc != 0) { + dest.append(uc); + } + } + } + } + } else { + /* reverse output */ + if ((options & BidiBase.INSERT_LRM_FOR_NUMERIC) == 0) { + /* do not insert Bidi controls */ + for (run = runCount; --run >= 0;) { + BidiRun bidiRun = bidi.getVisualRun(run); + if (bidiRun.isEvenRun()) { + dest.append( + doWriteReverse(text, bidiRun.start, bidiRun.limit, options & ~BidiBase.DO_MIRRORING)); + } else { + dest.append(doWriteForward(text, bidiRun.start, bidiRun.limit, options)); + } + } + } else { + /* insert Bidi controls for "inverse Bidi" */ + + byte[] dirProps = bidi.dirProps; + + for (run = runCount; --run >= 0;) { + /* reverse output */ + BidiRun bidiRun = bidi.getVisualRun(run); + if (bidiRun.isEvenRun()) { + if (dirProps[bidiRun.limit - 1] != BidiBase.L) { + dest.append(LRM_CHAR); + } + + dest.append( + doWriteReverse(text, bidiRun.start, bidiRun.limit, options & ~BidiBase.DO_MIRRORING)); + + if (dirProps[bidiRun.start] != BidiBase.L) { + dest.append(LRM_CHAR); + } + } else { + if ((MASK_R_AL & BidiBase.DirPropFlag(dirProps[bidiRun.start])) == 0) { + dest.append(RLM_CHAR); + } + + dest.append(doWriteForward(text, bidiRun.start, bidiRun.limit, options)); + + if ((MASK_R_AL & BidiBase.DirPropFlag(dirProps[bidiRun.limit - 1])) == 0) { + dest.append(RLM_CHAR); + } + } + } + } + } + + return dest.toString(); + } +} diff --git a/src/main/java/jdk_internal/icu/text/FilteredNormalizer2.java b/src/main/java/jdk_internal/icu/text/FilteredNormalizer2.java index f3915e57..b7031fb4 100755 --- a/src/main/java/jdk_internal/icu/text/FilteredNormalizer2.java +++ b/src/main/java/jdk_internal/icu/text/FilteredNormalizer2.java @@ -1,271 +1,271 @@ -/* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* -******************************************************************************* -* Copyright (C) 2009-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -*/ -package jdk_internal.icu.text; - -import java.io.IOException; - -/** - * 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(..., - * UnicodeSet.SpanCondition.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. - * - * @stable ICU 4.4 - * @author Markus W. Scherer - */ -class FilteredNormalizer2 extends Normalizer2 { - - /** - * 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 - */ - public FilteredNormalizer2(Normalizer2 n2, UnicodeSet filterSet) { - norm2 = n2; - set = filterSet; - } - - /** - * {@inheritDoc} - * - * @stable ICU 4.4 - */ - @Override - public StringBuilder normalize(CharSequence src, StringBuilder dest) { - if (dest == src) { - throw new IllegalArgumentException(); - } - dest.setLength(0); - normalize(src, dest, UnicodeSet.SpanCondition.SIMPLE); - return dest; - } - - /** - * {@inheritDoc} - * - * @stable ICU 4.6 - */ - @Override - public Appendable normalize(CharSequence src, Appendable dest) { - if (dest == src) { - throw new IllegalArgumentException(); - } - return normalize(src, dest, UnicodeSet.SpanCondition.SIMPLE); - } - - /** - * {@inheritDoc} - * - * @stable ICU 4.4 - */ - @Override - public StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second) { - return normalizeSecondAndAppend(first, second, true); - } - - /** - * {@inheritDoc} - * - * @stable ICU 4.4 - */ - @Override - public StringBuilder append(StringBuilder first, CharSequence second) { - return normalizeSecondAndAppend(first, second, false); - } - - /** - * {@inheritDoc} - * - * @stable ICU 4.6 - */ - @Override - public String getDecomposition(int c) { - return set.contains(c) ? norm2.getDecomposition(c) : null; - } - - /** - * {@inheritDoc} - * - * @stable ICU 49 - */ - @Override - public int getCombiningClass(int c) { - return set.contains(c) ? norm2.getCombiningClass(c) : 0; - } - - /** - * {@inheritDoc} - * - * @stable ICU 4.4 - */ - @Override - public boolean isNormalized(CharSequence s) { - UnicodeSet.SpanCondition spanCondition = UnicodeSet.SpanCondition.SIMPLE; - for (int prevSpanLimit = 0; prevSpanLimit < s.length();) { - int spanLimit = set.span(s, prevSpanLimit, spanCondition); - if (spanCondition == UnicodeSet.SpanCondition.NOT_CONTAINED) { - spanCondition = UnicodeSet.SpanCondition.SIMPLE; - } else { - if (!norm2.isNormalized(s.subSequence(prevSpanLimit, spanLimit))) { - return false; - } - spanCondition = UnicodeSet.SpanCondition.NOT_CONTAINED; - } - prevSpanLimit = spanLimit; - } - return true; - } - - /** - * {@inheritDoc} - * - * @stable ICU 4.4 - */ - @Override - public int spanQuickCheckYes(CharSequence s) { - UnicodeSet.SpanCondition spanCondition = UnicodeSet.SpanCondition.SIMPLE; - for (int prevSpanLimit = 0; prevSpanLimit < s.length();) { - int spanLimit = set.span(s, prevSpanLimit, spanCondition); - if (spanCondition == UnicodeSet.SpanCondition.NOT_CONTAINED) { - spanCondition = UnicodeSet.SpanCondition.SIMPLE; - } else { - int yesLimit = prevSpanLimit + norm2.spanQuickCheckYes(s.subSequence(prevSpanLimit, spanLimit)); - if (yesLimit < spanLimit) { - return yesLimit; - } - spanCondition = UnicodeSet.SpanCondition.NOT_CONTAINED; - } - prevSpanLimit = spanLimit; - } - return s.length(); - } - - /** - * {@inheritDoc} - * - * @stable ICU 4.4 - */ - @Override - public boolean hasBoundaryBefore(int c) { - return !set.contains(c) || norm2.hasBoundaryBefore(c); - } - - // 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, - // UnicodeSet.SpanCondition.SIMPLE should be passed in for the start of src - // and UnicodeSet.SpanCondition.NOT_CONTAINED should be passed in if we continue - // after - // an in-filter prefix. - private Appendable normalize(CharSequence src, Appendable dest, UnicodeSet.SpanCondition spanCondition) { - // Don't throw away destination buffer between iterations. - StringBuilder tempDest = new StringBuilder(); - try { - for (int prevSpanLimit = 0; prevSpanLimit < src.length();) { - int spanLimit = set.span(src, prevSpanLimit, spanCondition); - int spanLength = spanLimit - prevSpanLimit; - if (spanCondition == UnicodeSet.SpanCondition.NOT_CONTAINED) { - if (spanLength != 0) { - dest.append(src, prevSpanLimit, spanLimit); - } - spanCondition = UnicodeSet.SpanCondition.SIMPLE; - } else { - if (spanLength != 0) { - // Not norm2.normalizeSecondAndAppend() because we do not want - // to modify the non-filter part of dest. - dest.append(norm2.normalize(src.subSequence(prevSpanLimit, spanLimit), tempDest)); - } - spanCondition = UnicodeSet.SpanCondition.NOT_CONTAINED; - } - prevSpanLimit = spanLimit; - } - } catch (IOException e) { - throw new InternalError(e.toString(), e); - } - return dest; - } - - private StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second, boolean doNormalize) { - if (first == second) { - throw new IllegalArgumentException(); - } - if (first.length() == 0) { - if (doNormalize) { - return normalize(second, first); - } else { - return first.append(second); - } - } - // merge the in-filter suffix of the first string with the in-filter prefix of - // the second - int prefixLimit = set.span(second, 0, UnicodeSet.SpanCondition.SIMPLE); - if (prefixLimit != 0) { - CharSequence prefix = second.subSequence(0, prefixLimit); - int suffixStart = set.spanBack(first, 0x7fffffff, UnicodeSet.SpanCondition.SIMPLE); - if (suffixStart == 0) { - if (doNormalize) { - norm2.normalizeSecondAndAppend(first, prefix); - } else { - norm2.append(first, prefix); - } - } else { - StringBuilder middle = new StringBuilder(first.subSequence(suffixStart, first.length())); - if (doNormalize) { - norm2.normalizeSecondAndAppend(middle, prefix); - } else { - norm2.append(middle, prefix); - } - first.delete(suffixStart, 0x7fffffff).append(middle); - } - } - if (prefixLimit < second.length()) { - CharSequence rest = second.subSequence(prefixLimit, second.length()); - if (doNormalize) { - normalize(rest, first, UnicodeSet.SpanCondition.NOT_CONTAINED); - } else { - first.append(rest); - } - } - return first; - } - - private Normalizer2 norm2; - private UnicodeSet set; -}; +/* + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* +******************************************************************************* +* Copyright (C) 2009-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +*/ +package jdk_internal.icu.text; + +import java.io.IOException; + +/** + * 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(..., + * UnicodeSet.SpanCondition.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. + * + * @stable ICU 4.4 + * @author Markus W. Scherer + */ +class FilteredNormalizer2 extends Normalizer2 { + + /** + * 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 + */ + public FilteredNormalizer2(Normalizer2 n2, UnicodeSet filterSet) { + norm2 = n2; + set = filterSet; + } + + /** + * {@inheritDoc} + * + * @stable ICU 4.4 + */ + @Override + public StringBuilder normalize(CharSequence src, StringBuilder dest) { + if (dest == src) { + throw new IllegalArgumentException(); + } + dest.setLength(0); + normalize(src, dest, UnicodeSet.SpanCondition.SIMPLE); + return dest; + } + + /** + * {@inheritDoc} + * + * @stable ICU 4.6 + */ + @Override + public Appendable normalize(CharSequence src, Appendable dest) { + if (dest == src) { + throw new IllegalArgumentException(); + } + return normalize(src, dest, UnicodeSet.SpanCondition.SIMPLE); + } + + /** + * {@inheritDoc} + * + * @stable ICU 4.4 + */ + @Override + public StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second) { + return normalizeSecondAndAppend(first, second, true); + } + + /** + * {@inheritDoc} + * + * @stable ICU 4.4 + */ + @Override + public StringBuilder append(StringBuilder first, CharSequence second) { + return normalizeSecondAndAppend(first, second, false); + } + + /** + * {@inheritDoc} + * + * @stable ICU 4.6 + */ + @Override + public String getDecomposition(int c) { + return set.contains(c) ? norm2.getDecomposition(c) : null; + } + + /** + * {@inheritDoc} + * + * @stable ICU 49 + */ + @Override + public int getCombiningClass(int c) { + return set.contains(c) ? norm2.getCombiningClass(c) : 0; + } + + /** + * {@inheritDoc} + * + * @stable ICU 4.4 + */ + @Override + public boolean isNormalized(CharSequence s) { + UnicodeSet.SpanCondition spanCondition = UnicodeSet.SpanCondition.SIMPLE; + for (int prevSpanLimit = 0; prevSpanLimit < s.length();) { + int spanLimit = set.span(s, prevSpanLimit, spanCondition); + if (spanCondition == UnicodeSet.SpanCondition.NOT_CONTAINED) { + spanCondition = UnicodeSet.SpanCondition.SIMPLE; + } else { + if (!norm2.isNormalized(s.subSequence(prevSpanLimit, spanLimit))) { + return false; + } + spanCondition = UnicodeSet.SpanCondition.NOT_CONTAINED; + } + prevSpanLimit = spanLimit; + } + return true; + } + + /** + * {@inheritDoc} + * + * @stable ICU 4.4 + */ + @Override + public int spanQuickCheckYes(CharSequence s) { + UnicodeSet.SpanCondition spanCondition = UnicodeSet.SpanCondition.SIMPLE; + for (int prevSpanLimit = 0; prevSpanLimit < s.length();) { + int spanLimit = set.span(s, prevSpanLimit, spanCondition); + if (spanCondition == UnicodeSet.SpanCondition.NOT_CONTAINED) { + spanCondition = UnicodeSet.SpanCondition.SIMPLE; + } else { + int yesLimit = prevSpanLimit + norm2.spanQuickCheckYes(s.subSequence(prevSpanLimit, spanLimit)); + if (yesLimit < spanLimit) { + return yesLimit; + } + spanCondition = UnicodeSet.SpanCondition.NOT_CONTAINED; + } + prevSpanLimit = spanLimit; + } + return s.length(); + } + + /** + * {@inheritDoc} + * + * @stable ICU 4.4 + */ + @Override + public boolean hasBoundaryBefore(int c) { + return !set.contains(c) || norm2.hasBoundaryBefore(c); + } + + // 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, + // UnicodeSet.SpanCondition.SIMPLE should be passed in for the start of src + // and UnicodeSet.SpanCondition.NOT_CONTAINED should be passed in if we continue + // after + // an in-filter prefix. + private Appendable normalize(CharSequence src, Appendable dest, UnicodeSet.SpanCondition spanCondition) { + // Don't throw away destination buffer between iterations. + StringBuilder tempDest = new StringBuilder(); + try { + for (int prevSpanLimit = 0; prevSpanLimit < src.length();) { + int spanLimit = set.span(src, prevSpanLimit, spanCondition); + int spanLength = spanLimit - prevSpanLimit; + if (spanCondition == UnicodeSet.SpanCondition.NOT_CONTAINED) { + if (spanLength != 0) { + dest.append(src, prevSpanLimit, spanLimit); + } + spanCondition = UnicodeSet.SpanCondition.SIMPLE; + } else { + if (spanLength != 0) { + // Not norm2.normalizeSecondAndAppend() because we do not want + // to modify the non-filter part of dest. + dest.append(norm2.normalize(src.subSequence(prevSpanLimit, spanLimit), tempDest)); + } + spanCondition = UnicodeSet.SpanCondition.NOT_CONTAINED; + } + prevSpanLimit = spanLimit; + } + } catch (IOException e) { + throw new InternalError(e.toString(), e); + } + return dest; + } + + private StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second, boolean doNormalize) { + if (first == second) { + throw new IllegalArgumentException(); + } + if (first.length() == 0) { + if (doNormalize) { + return normalize(second, first); + } else { + return first.append(second); + } + } + // merge the in-filter suffix of the first string with the in-filter prefix of + // the second + int prefixLimit = set.span(second, 0, UnicodeSet.SpanCondition.SIMPLE); + if (prefixLimit != 0) { + CharSequence prefix = second.subSequence(0, prefixLimit); + int suffixStart = set.spanBack(first, 0x7fffffff, UnicodeSet.SpanCondition.SIMPLE); + if (suffixStart == 0) { + if (doNormalize) { + norm2.normalizeSecondAndAppend(first, prefix); + } else { + norm2.append(first, prefix); + } + } else { + StringBuilder middle = new StringBuilder(first.subSequence(suffixStart, first.length())); + if (doNormalize) { + norm2.normalizeSecondAndAppend(middle, prefix); + } else { + norm2.append(middle, prefix); + } + first.delete(suffixStart, 0x7fffffff).append(middle); + } + } + if (prefixLimit < second.length()) { + CharSequence rest = second.subSequence(prefixLimit, second.length()); + if (doNormalize) { + normalize(rest, first, UnicodeSet.SpanCondition.NOT_CONTAINED); + } else { + first.append(rest); + } + } + return first; + } + + private Normalizer2 norm2; + private UnicodeSet set; +}; diff --git a/src/main/java/jdk_internal/icu/text/Normalizer2.java b/src/main/java/jdk_internal/icu/text/Normalizer2.java index 5471fcd1..553b15e4 100755 --- a/src/main/java/jdk_internal/icu/text/Normalizer2.java +++ b/src/main/java/jdk_internal/icu/text/Normalizer2.java @@ -1,288 +1,288 @@ -/* - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ******************************************************************************* - * Copyright (C) 2009-2014, International Business Machines - * Corporation and others. All Rights Reserved. - ******************************************************************************* - */ - -package jdk_internal.icu.text; - -import jdk_internal.icu.impl.Norm2AllModes; - -/** - * Unicode normalization functionality for standard Unicode normalization or for - * using custom mapping tables. All instances of this class are - * unmodifiable/immutable. 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 - * Unicode Standard Annex #15: - * Unicode Normalization Forms. 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 - * @author Markus W. Scherer - */ -public abstract class Normalizer2 { - - /** - * Returns a Normalizer2 instance for Unicode NFC normalization. Same as - * getInstance(null, "nfc", Mode.COMPOSE). Returns an unmodifiable singleton - * instance. - * - * @return the requested Normalizer2, if successful - * @stable ICU 49 - */ - public static Normalizer2 getNFCInstance() { - return Norm2AllModes.getNFCInstance().comp; - } - - /** - * Returns a Normalizer2 instance for Unicode NFD normalization. Same as - * getInstance(null, "nfc", Mode.DECOMPOSE). Returns an unmodifiable singleton - * instance. - * - * @return the requested Normalizer2, if successful - * @stable ICU 49 - */ - public static Normalizer2 getNFDInstance() { - return Norm2AllModes.getNFCInstance().decomp; - } - - /** - * Returns a Normalizer2 instance for Unicode NFKC normalization. Same as - * getInstance(null, "nfkc", Mode.COMPOSE). Returns an unmodifiable singleton - * instance. - * - * @return the requested Normalizer2, if successful - * @stable ICU 49 - */ - public static Normalizer2 getNFKCInstance() { - return Norm2AllModes.getNFKCInstance().comp; - } - - /** - * Returns a Normalizer2 instance for Unicode NFKD normalization. Same as - * getInstance(null, "nfkc", Mode.DECOMPOSE). Returns an unmodifiable singleton - * instance. - * - * @return the requested Normalizer2, if successful - * @stable ICU 49 - */ - public static Normalizer2 getNFKDInstance() { - return Norm2AllModes.getNFKCInstance().decomp; - } - - /** - * Returns the normalized form of the source string. - * - * @param src source string - * @return normalized src - * @stable ICU 4.4 - */ - public String normalize(CharSequence src) { - if (src instanceof String) { - // Fastpath: Do not construct a new String if the src is a String - // and is already normalized. - int spanLength = spanQuickCheckYes(src); - if (spanLength == src.length()) { - return (String) src; - } - if (spanLength != 0) { - StringBuilder sb = new StringBuilder(src.length()).append(src, 0, spanLength); - return normalizeSecondAndAppend(sb, src.subSequence(spanLength, src.length())).toString(); - } - } - return normalize(src, new StringBuilder(src.length())).toString(); - } - - /** - * 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 - * @return dest - * @stable ICU 4.4 - */ - public abstract StringBuilder normalize(CharSequence src, StringBuilder dest); - - /** - * Writes the normalized form of the source string to the destination Appendable - * and returns the destination Appendable. The source and destination strings - * must be different objects. - * - *

- * Any {@link java.io.IOException} is wrapped into a - * {@link com.ibm.icu.util.ICUUncheckedIOException}. - * - * @param src source string - * @param dest destination Appendable; gets normalized src appended - * @return dest - * @stable ICU 4.6 - */ - public abstract Appendable normalize(CharSequence src, Appendable dest); - - /** - * 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 - * @return first - * @stable ICU 4.4 - */ - public abstract StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second); - - /** - * 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 - * @return first - * @stable ICU 4.4 - */ - public abstract StringBuilder append(StringBuilder first, CharSequence second); - - /** - * Gets the decomposition mapping of c. Roughly equivalent to normalizing the - * String form of c on a DECOMPOSE Normalizer2 instance, but much faster, and - * except that this function returns null 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 - * @return c's decomposition mapping, if any; otherwise null - * @stable ICU 4.6 - */ - public abstract String getDecomposition(int c); - - /** - * 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 - */ - public int getCombiningClass(int c) { - return 0; - } - - /** - * 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 - * @return true if s is normalized - * @stable ICU 4.4 - */ - public abstract boolean isNormalized(CharSequence s); - - /** - * Returns the end of the normalized substring of the input string. In other - * words, with end=spanQuickCheckYes(s); the substring - * s.subSequence(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 - * @return "yes" span end index - * @stable ICU 4.4 - */ - public abstract int spanQuickCheckYes(CharSequence s); - - /** - * 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 - */ - public abstract boolean hasBoundaryBefore(int c); - - /** - * Sole constructor. (For invocation by subclass constructors, typically - * implicit.) - * - * @internal deprecated This API is ICU internal only. - */ - protected Normalizer2() { - } -} +/* + * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ******************************************************************************* + * Copyright (C) 2009-2014, International Business Machines + * Corporation and others. All Rights Reserved. + ******************************************************************************* + */ + +package jdk_internal.icu.text; + +import jdk_internal.icu.impl.Norm2AllModes; + +/** + * Unicode normalization functionality for standard Unicode normalization or for + * using custom mapping tables. All instances of this class are + * unmodifiable/immutable. 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 + * Unicode Standard Annex #15: + * Unicode Normalization Forms. 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 + * @author Markus W. Scherer + */ +public abstract class Normalizer2 { + + /** + * Returns a Normalizer2 instance for Unicode NFC normalization. Same as + * getInstance(null, "nfc", Mode.COMPOSE). Returns an unmodifiable singleton + * instance. + * + * @return the requested Normalizer2, if successful + * @stable ICU 49 + */ + public static Normalizer2 getNFCInstance() { + return Norm2AllModes.getNFCInstance().comp; + } + + /** + * Returns a Normalizer2 instance for Unicode NFD normalization. Same as + * getInstance(null, "nfc", Mode.DECOMPOSE). Returns an unmodifiable singleton + * instance. + * + * @return the requested Normalizer2, if successful + * @stable ICU 49 + */ + public static Normalizer2 getNFDInstance() { + return Norm2AllModes.getNFCInstance().decomp; + } + + /** + * Returns a Normalizer2 instance for Unicode NFKC normalization. Same as + * getInstance(null, "nfkc", Mode.COMPOSE). Returns an unmodifiable singleton + * instance. + * + * @return the requested Normalizer2, if successful + * @stable ICU 49 + */ + public static Normalizer2 getNFKCInstance() { + return Norm2AllModes.getNFKCInstance().comp; + } + + /** + * Returns a Normalizer2 instance for Unicode NFKD normalization. Same as + * getInstance(null, "nfkc", Mode.DECOMPOSE). Returns an unmodifiable singleton + * instance. + * + * @return the requested Normalizer2, if successful + * @stable ICU 49 + */ + public static Normalizer2 getNFKDInstance() { + return Norm2AllModes.getNFKCInstance().decomp; + } + + /** + * Returns the normalized form of the source string. + * + * @param src source string + * @return normalized src + * @stable ICU 4.4 + */ + public String normalize(CharSequence src) { + if (src instanceof String) { + // Fastpath: Do not construct a new String if the src is a String + // and is already normalized. + int spanLength = spanQuickCheckYes(src); + if (spanLength == src.length()) { + return (String) src; + } + if (spanLength != 0) { + StringBuilder sb = new StringBuilder(src.length()).append(src, 0, spanLength); + return normalizeSecondAndAppend(sb, src.subSequence(spanLength, src.length())).toString(); + } + } + return normalize(src, new StringBuilder(src.length())).toString(); + } + + /** + * 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 + * @return dest + * @stable ICU 4.4 + */ + public abstract StringBuilder normalize(CharSequence src, StringBuilder dest); + + /** + * Writes the normalized form of the source string to the destination Appendable + * and returns the destination Appendable. The source and destination strings + * must be different objects. + * + *

+ * Any {@link java.io.IOException} is wrapped into a + * {@link com.ibm.icu.util.ICUUncheckedIOException}. + * + * @param src source string + * @param dest destination Appendable; gets normalized src appended + * @return dest + * @stable ICU 4.6 + */ + public abstract Appendable normalize(CharSequence src, Appendable dest); + + /** + * 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 + * @return first + * @stable ICU 4.4 + */ + public abstract StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second); + + /** + * 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 + * @return first + * @stable ICU 4.4 + */ + public abstract StringBuilder append(StringBuilder first, CharSequence second); + + /** + * Gets the decomposition mapping of c. Roughly equivalent to normalizing the + * String form of c on a DECOMPOSE Normalizer2 instance, but much faster, and + * except that this function returns null 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 + * @return c's decomposition mapping, if any; otherwise null + * @stable ICU 4.6 + */ + public abstract String getDecomposition(int c); + + /** + * 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 + */ + public int getCombiningClass(int c) { + return 0; + } + + /** + * 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 + * @return true if s is normalized + * @stable ICU 4.4 + */ + public abstract boolean isNormalized(CharSequence s); + + /** + * Returns the end of the normalized substring of the input string. In other + * words, with end=spanQuickCheckYes(s); the substring + * s.subSequence(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 + * @return "yes" span end index + * @stable ICU 4.4 + */ + public abstract int spanQuickCheckYes(CharSequence s); + + /** + * 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 + */ + public abstract boolean hasBoundaryBefore(int c); + + /** + * Sole constructor. (For invocation by subclass constructors, typically + * implicit.) + * + * @internal deprecated This API is ICU internal only. + */ + protected Normalizer2() { + } +} diff --git a/src/main/java/jdk_internal/icu/text/NormalizerBase.java b/src/main/java/jdk_internal/icu/text/NormalizerBase.java index d10b15f0..e16589b1 100755 --- a/src/main/java/jdk_internal/icu/text/NormalizerBase.java +++ b/src/main/java/jdk_internal/icu/text/NormalizerBase.java @@ -1,791 +1,791 @@ -/* - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ******************************************************************************* - * Copyright (C) 2000-2014, International Business Machines Corporation and - * others. All Rights Reserved. - ******************************************************************************* - */ -package jdk_internal.icu.text; - -import jdk_internal.bidi.CharacterIterator; -import jdk_internal.bidi.Normalizer; -import jdk_internal.icu.impl.Norm2AllModes; - -/** - * Unicode Normalization - * - *

Unicode normalization API

- * - * normalize transforms Unicode text into an equivalent composed or - * decomposed form, allowing for easier sorting and searching of text. - * 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): - * - *
- *      00C1    LATIN CAPITAL LETTER A WITH ACUTE
- * 
- * - * or as two separate characters (the "decomposed" form): - * - *
- *      0041    LATIN CAPITAL LETTER A
- *      0301    COMBINING ACUTE ACCENT
- * 
- * - * 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: - * - *
- *      0066    LATIN SMALL LETTER F
- *      0066    LATIN SMALL LETTER F
- *      0069    LATIN SMALL LETTER I
- * 
- * - * or as the single character - * - *
- *      FB03    LATIN SMALL LIGATURE FFI
- * 
- * - * 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. - * - * 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, 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 YES for - * most strings in practice. - * - * normalize(FCD) may be implemented with NFD. - * - * For more details on FCD see Unicode Technical Note #5 (Canonical Equivalence - * in Applications): http://www.unicode.org/notes/tn5/#FCD - * - * 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. - * - * Note: The Normalizer class also provides API for iterative normalization. - * 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. - * - * @stable ICU 2.8 - */ -// Original filename in ICU4J: Normalizer.java -public final class NormalizerBase implements Cloneable { - - // The input text and our position in it - private UCharacterIterator text; - private Normalizer2 norm2; - private Mode mode; - private int options; - - // The normalization buffer is the result of normalization - // of the source in [currentIndex..nextIndex] . - private int currentIndex; - private int nextIndex; - - // A buffer for holding intermediate results - private StringBuilder buffer; - private int bufferPos; - - // Helper classes to defer loading of normalization data. - private static final class ModeImpl { - private ModeImpl(Normalizer2 n2) { - normalizer2 = n2; - } - - private final Normalizer2 normalizer2; - } - - private static final class NFDModeImpl { - private static final ModeImpl INSTANCE = new ModeImpl(Normalizer2.getNFDInstance()); - } - - private static final class NFKDModeImpl { - private static final ModeImpl INSTANCE = new ModeImpl(Normalizer2.getNFKDInstance()); - } - - private static final class NFCModeImpl { - private static final ModeImpl INSTANCE = new ModeImpl(Normalizer2.getNFCInstance()); - } - - private static final class NFKCModeImpl { - private static final ModeImpl INSTANCE = new ModeImpl(Normalizer2.getNFKCInstance()); - } - - private static final class Unicode32 { - private static final UnicodeSet INSTANCE = new UnicodeSet("[:age=3.2:]").freeze(); - } - - private static final class NFD32ModeImpl { - private static final ModeImpl INSTANCE = new ModeImpl( - new FilteredNormalizer2(Normalizer2.getNFDInstance(), Unicode32.INSTANCE)); - } - - private static final class NFKD32ModeImpl { - private static final ModeImpl INSTANCE = new ModeImpl( - new FilteredNormalizer2(Normalizer2.getNFKDInstance(), Unicode32.INSTANCE)); - } - - private static final class NFC32ModeImpl { - private static final ModeImpl INSTANCE = new ModeImpl( - new FilteredNormalizer2(Normalizer2.getNFCInstance(), Unicode32.INSTANCE)); - } - - private static final class NFKC32ModeImpl { - private static final ModeImpl INSTANCE = new ModeImpl( - new FilteredNormalizer2(Normalizer2.getNFKCInstance(), Unicode32.INSTANCE)); - } - - /** - * Options bit set value to select Unicode 3.2 normalization (except - * NormalizationCorrections). At most one Unicode version can be selected at a - * time. - * - * @stable ICU 2.6 - */ - public static final int UNICODE_3_2 = 0x20; - - public static final int UNICODE_3_2_0_ORIGINAL = UNICODE_3_2; - - /* - * Default option for the latest Unicode normalization. This option is provided - * mainly for testing. The value zero means that normalization is done with the - * fixes for - Corrigendum 4 (Five CJK Canonical Mapping Errors) - Corrigendum 5 - * (Normalization Idempotency) - */ - public static final int UNICODE_LATEST = 0x00; - - /** - * Constant indicating that the end of the iteration has been reached. This is - * guaranteed to have the same value as {@link UCharacterIterator#DONE}. - * - * @stable ICU 2.8 - */ - public static final int DONE = UCharacterIterator.DONE; - - /** - * Constants for normalization modes. - *

- * The Mode class is not intended for public subclassing. Only the Mode - * constants provided by the Normalizer class should be used, and any fields or - * methods should not be called or overridden by users. - * - * @stable ICU 2.8 - */ - public abstract static class Mode { - - /** - * Sole constructor - * - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - protected Mode() { - } - - /** - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - protected abstract Normalizer2 getNormalizer2(int options); - } - - private static Mode toMode(Normalizer.Form form) { - switch (form) { - case NFC: - return NFC; - case NFD: - return NFD; - case NFKC: - return NFKC; - case NFKD: - return NFKD; - } - - throw new IllegalArgumentException("Unexpected normalization form: " + form); - } - - private static final class NONEMode extends Mode { - protected Normalizer2 getNormalizer2(int options) { - return Norm2AllModes.NOOP_NORMALIZER2; - } - } - - private static final class NFDMode extends Mode { - protected Normalizer2 getNormalizer2(int options) { - return (options & UNICODE_3_2) != 0 ? NFD32ModeImpl.INSTANCE.normalizer2 : NFDModeImpl.INSTANCE.normalizer2; - } - } - - private static final class NFKDMode extends Mode { - protected Normalizer2 getNormalizer2(int options) { - return (options & UNICODE_3_2) != 0 ? NFKD32ModeImpl.INSTANCE.normalizer2 - : NFKDModeImpl.INSTANCE.normalizer2; - } - } - - private static final class NFCMode extends Mode { - protected Normalizer2 getNormalizer2(int options) { - return (options & UNICODE_3_2) != 0 ? NFC32ModeImpl.INSTANCE.normalizer2 : NFCModeImpl.INSTANCE.normalizer2; - } - } - - private static final class NFKCMode extends Mode { - protected Normalizer2 getNormalizer2(int options) { - return (options & UNICODE_3_2) != 0 ? NFKC32ModeImpl.INSTANCE.normalizer2 - : NFKCModeImpl.INSTANCE.normalizer2; - } - } - - /** - * No decomposition/composition. - * - * @stable ICU 2.8 - */ - public static final Mode NONE = new NONEMode(); - - /** - * Canonical decomposition. - * - * @stable ICU 2.8 - */ - public static final Mode NFD = new NFDMode(); - - /** - * Compatibility decomposition. - * - * @stable ICU 2.8 - */ - public static final Mode NFKD = new NFKDMode(); - - /** - * Canonical decomposition followed by canonical composition. - * - * @stable ICU 2.8 - */ - public static final Mode NFC = new NFCMode(); - - public static final Mode NFKC = new NFKCMode(); - - // ------------------------------------------------------------------------- - // Iterator constructors - // ------------------------------------------------------------------------- - - /** - * Creates a new {@code NormalizerBase} object for iterating over the normalized - * form of a given string. - *

- * The {@code options} parameter specifies which optional {@code NormalizerBase} - * features are to be enabled for this object. - *

- * - * @param str The string to be normalized. The normalization will start at the - * beginning of the string. - * - * @param mode The normalization mode. - * - * @param opt Any optional features to be enabled. Currently the only available - * option is {@link #UNICODE_3_2}. If you want the default behavior - * corresponding to one of the standard Unicode Normalization Forms, - * use 0 for this argument. - * @stable ICU 2.6 - */ - public NormalizerBase(String str, Mode mode, int opt) { - this.text = UCharacterIterator.getInstance(str); - this.mode = mode; - this.options = opt; - norm2 = mode.getNormalizer2(opt); - buffer = new StringBuilder(); - } - - public NormalizerBase(String str, Mode mode) { - this(str, mode, 0); - } - - /** - * Creates a new {@code NormalizerBase} 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. - * - * @param opt Any optional features to be enabled. Currently the only available - * option is {@link #UNICODE_3_2}. If you want the default behavior - * corresponding to one of the standard Unicode Normalization Forms, - * use 0 for this argument. - * @stable ICU 2.6 - */ - public NormalizerBase(CharacterIterator iter, Mode mode, int opt) { - this.text = UCharacterIterator.getInstance((CharacterIterator) iter.clone()); - this.mode = mode; - this.options = opt; - norm2 = mode.getNormalizer2(opt); - buffer = new StringBuilder(); - } - - public NormalizerBase(CharacterIterator iter, Mode mode) { - this(iter, mode, 0); - } - - /** - * Clones this {@code NormalizerBase} object. All properties of this object are - * duplicated in the new object, including the cloning of any - * {@link CharacterIterator} that was passed in to the constructor or to - * {@link #setText(CharacterIterator) setText}. However, the text storage - * underlying the {@code CharacterIterator} is not duplicated unless the - * iterator's {@code clone} method does so. - * - * @stable ICU 2.8 - */ - public Object clone() { - try { - NormalizerBase copy = (NormalizerBase) super.clone(); - copy.text = (UCharacterIterator) text.clone(); - copy.mode = mode; - copy.options = options; - copy.norm2 = norm2; - copy.buffer = new StringBuilder(buffer); - copy.bufferPos = bufferPos; - copy.currentIndex = currentIndex; - copy.nextIndex = nextIndex; - return copy; - } catch (CloneNotSupportedException e) { - throw new InternalError(e.toString(), e); - } - } - - /** - * Normalizes a {@code String} using the given normalization operation. - *

- * The {@code options} parameter specifies which optional {@code NormalizerBase} - * features are to be enabled for this operation. Currently the only available - * option is {@link #UNICODE_3_2}. If you want the default behavior - * corresponding to one of the standard Unicode Normalization Forms, use 0 for - * this argument. - *

- * - * @param str the input string to be normalized. - * @param mode the normalization mode - * @param options the optional features to be enabled. - * @return String the normalized string - * @stable ICU 2.6 - */ - public static String normalize(String str, Mode mode, int options) { - return mode.getNormalizer2(options).normalize(str); - } - - public static String normalize(String str, Normalizer.Form form) { - return NormalizerBase.normalize(str, toMode(form), UNICODE_LATEST); - } - - public static String normalize(String str, Normalizer.Form form, int options) { - return NormalizerBase.normalize(str, toMode(form), options); - } - - /** - * Test if a string is in a given normalization form. This is semantically - * equivalent to source.equals(normalize(source, mode)). - * - * Unlike 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 str the input string to be checked to see if it is normalized - * @param mode the normalization mode - * @param options Options for use with exclusion set and tailored Normalization - * The only option that is currently recognized is UNICODE_3_2 - * @see #isNormalized - * @stable ICU 2.6 - */ - public static boolean isNormalized(String str, Mode mode, int options) { - return mode.getNormalizer2(options).isNormalized(str); - } - - public static boolean isNormalized(String str, Normalizer.Form form) { - return NormalizerBase.isNormalized(str, toMode(form), UNICODE_LATEST); - } - - public static boolean isNormalized(String str, Normalizer.Form form, int options) { - return NormalizerBase.isNormalized(str, toMode(form), options); - } - - // ------------------------------------------------------------------------- - // Iteration API - // ------------------------------------------------------------------------- - - /** - * Return the current character in the normalized text. - * - * @return The codepoint as an int - * @stable ICU 2.8 - */ - public int current() { - if (bufferPos < buffer.length() || nextNormalize()) { - return buffer.codePointAt(bufferPos); - } else { - return DONE; - } - } - - /** - * Return the next character in the normalized text and advance the iteration - * position by one. If the end of the text has already been reached, - * {@link #DONE} is returned. - * - * @return The codepoint as an int - * @stable ICU 2.8 - */ - public int next() { - if (bufferPos < buffer.length() || nextNormalize()) { - int c = buffer.codePointAt(bufferPos); - bufferPos += Character.charCount(c); - return c; - } else { - return DONE; - } - } - - /** - * Return the previous character in the normalized text and decrement the - * iteration position by one. If the beginning of the text has already been - * reached, {@link #DONE} is returned. - * - * @return The codepoint as an int - * @stable ICU 2.8 - */ - public int previous() { - if (bufferPos > 0 || previousNormalize()) { - int c = buffer.codePointBefore(bufferPos); - bufferPos -= Character.charCount(c); - return c; - } else { - return DONE; - } - } - - /** - * Reset the index to the beginning of the text. This is equivalent to - * setIndexOnly(startIndex)). - * - * @stable ICU 2.8 - */ - public void reset() { - text.setIndex(0); - currentIndex = nextIndex = 0; - clearBuffer(); - } - - /** - * 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. - * @stable ICU 2.8 - */ - public void setIndexOnly(int index) { - text.setIndex(index); // validates index - currentIndex = nextIndex = index; - clearBuffer(); - } - - /** - * Set the iteration position in the input text that is being normalized and - * return the first normalized character at that position. - *

- * Note: This method sets the position in the input text, while - * {@link #next} and {@link #previous} iterate through characters in the - * normalized output. This means that there is not necessarily a - * one-to-one correspondence between characters returned by {@code next} and - * {@code previous} and the indices passed to and returned from {@code setIndex} - * and {@link #getIndex}. - *

- * - * @param index the desired index in the input text. - * - * @return the first normalized character that is the result of iterating - * forward starting at the given index. - * - * @throws IllegalArgumentException if the given index is less than - * {@link #getBeginIndex} or greater than - * {@link #getEndIndex}. deprecated ICU 3.2 - * @obsolete ICU 3.2 - */ - public int setIndex(int index) { - setIndexOnly(index); - return current(); - } - - /** - * Retrieve the index of the start of the input text. This is the begin index of - * the {@code CharacterIterator} or the start (i.e. 0) of the {@code String} - * over which this {@code NormalizerBase} is iterating - * - * @deprecated ICU 2.2. Use startIndex() instead. - * @return The codepoint as an int - * @see #startIndex - */ - @Deprecated - public int getBeginIndex() { - return 0; - } - - /** - * Retrieve the index of the end of the input text. This is the end index of the - * {@code CharacterIterator} or the length of the {@code String} over which this - * {@code NormalizerBase} is iterating - * - * @deprecated ICU 2.2. Use endIndex() instead. - * @return The codepoint as an int - * @see #endIndex - */ - @Deprecated - public int getEndIndex() { - return endIndex(); - } - - /** - * 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 {@code next} and - * {@code previous} and the indices passed to and returned from {@code setIndex} - * and {@link #getIndex}. - * - * @return The current iteration position - * @stable ICU 2.8 - */ - public int getIndex() { - if (bufferPos < buffer.length()) { - return currentIndex; - } else { - return nextIndex; - } - } - - /** - * Retrieve the index of the end of the input text. This is the end index of the - * {@code CharacterIterator} or the length of the {@code String} over which this - * {@code NormalizerBase} is iterating - * - * @return The current iteration position - * @stable ICU 2.8 - */ - public int endIndex() { - return text.getLength(); - } - - // ------------------------------------------------------------------------- - // Iterator attributes - // ------------------------------------------------------------------------- - /** - * 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 #setText - * setText()}, {@link #first}, {@link #last}, etc. after calling - * {@code setMode}. - *

- * - * @param newMode the new mode for this {@code NormalizerBase}. The supported - * modes are: - *

    - *
  • {@link #NFC} - Unicode canonical decompositiion followed - * by canonical composition. - *
  • {@link #NFKC} - Unicode compatibility decompositiion - * follwed by canonical composition. - *
  • {@link #NFD} - Unicode canonical decomposition - *
  • {@link #NFKD} - Unicode compatibility decomposition. - *
  • {@link #NONE} - Do nothing but return characters from the - * underlying input text. - *
- * - * @see #getMode - * @stable ICU 2.8 - */ - public void setMode(Mode newMode) { - mode = newMode; - norm2 = mode.getNormalizer2(options); - } - - /** - * Return the basic operation performed by this {@code NormalizerBase} - * - * @see #setMode - * @stable ICU 2.8 - */ - public Mode getMode() { - return mode; - } - - /** - * Set the input text over which this {@code NormalizerBase} will iterate. The - * iteration position is set to the beginning of the input text. - * - * @param newText The new string to be normalized. - * @stable ICU 2.8 - */ - public void setText(String newText) { - UCharacterIterator newIter = UCharacterIterator.getInstance(newText); - if (newIter == null) { - throw new IllegalStateException("Could not create a new UCharacterIterator"); - } - text = newIter; - reset(); - } - - /** - * Set the input text over which this {@code NormalizerBase} will iterate. The - * iteration position is set to the beginning of the input text. - * - * @param newText The new string to be normalized. - * @stable ICU 2.8 - */ - public void setText(CharacterIterator newText) { - UCharacterIterator newIter = UCharacterIterator.getInstance(newText); - if (newIter == null) { - throw new IllegalStateException("Could not create a new UCharacterIterator"); - } - text = newIter; - currentIndex = nextIndex = 0; - clearBuffer(); - } - - private void clearBuffer() { - buffer.setLength(0); - bufferPos = 0; - } - - private boolean nextNormalize() { - clearBuffer(); - currentIndex = nextIndex; - text.setIndex(nextIndex); - // Skip at least one character so we make progress. - int c = text.nextCodePoint(); - if (c < 0) { - return false; - } - StringBuilder segment = new StringBuilder().appendCodePoint(c); - while ((c = text.nextCodePoint()) >= 0) { - if (norm2.hasBoundaryBefore(c)) { - text.moveCodePointIndex(-1); - break; - } - segment.appendCodePoint(c); - } - nextIndex = text.getIndex(); - norm2.normalize(segment, buffer); - return buffer.length() != 0; - } - - private boolean previousNormalize() { - clearBuffer(); - nextIndex = currentIndex; - text.setIndex(currentIndex); - StringBuilder segment = new StringBuilder(); - int c; - while ((c = text.previousCodePoint()) >= 0) { - if (c <= 0xffff) { - segment.insert(0, (char) c); - } else { - segment.insert(0, Character.toChars(c)); - } - if (norm2.hasBoundaryBefore(c)) { - break; - } - } - currentIndex = text.getIndex(); - norm2.normalize(segment, buffer); - bufferPos = buffer.length(); - return buffer.length() != 0; - } - -} +/* + * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ******************************************************************************* + * Copyright (C) 2000-2014, International Business Machines Corporation and + * others. All Rights Reserved. + ******************************************************************************* + */ +package jdk_internal.icu.text; + +import jdk_internal.bidi.CharacterIterator; +import jdk_internal.bidi.Normalizer; +import jdk_internal.icu.impl.Norm2AllModes; + +/** + * Unicode Normalization + * + *

Unicode normalization API

+ * + * normalize transforms Unicode text into an equivalent composed or + * decomposed form, allowing for easier sorting and searching of text. + * 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): + * + *
+ *      00C1    LATIN CAPITAL LETTER A WITH ACUTE
+ * 
+ * + * or as two separate characters (the "decomposed" form): + * + *
+ *      0041    LATIN CAPITAL LETTER A
+ *      0301    COMBINING ACUTE ACCENT
+ * 
+ * + * 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: + * + *
+ *      0066    LATIN SMALL LETTER F
+ *      0066    LATIN SMALL LETTER F
+ *      0069    LATIN SMALL LETTER I
+ * 
+ * + * or as the single character + * + *
+ *      FB03    LATIN SMALL LIGATURE FFI
+ * 
+ * + * 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. + * + * 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, 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 YES for + * most strings in practice. + * + * normalize(FCD) may be implemented with NFD. + * + * For more details on FCD see Unicode Technical Note #5 (Canonical Equivalence + * in Applications): http://www.unicode.org/notes/tn5/#FCD + * + * 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. + * + * Note: The Normalizer class also provides API for iterative normalization. + * 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. + * + * @stable ICU 2.8 + */ +// Original filename in ICU4J: Normalizer.java +public final class NormalizerBase implements Cloneable { + + // The input text and our position in it + private UCharacterIterator text; + private Normalizer2 norm2; + private Mode mode; + private int options; + + // The normalization buffer is the result of normalization + // of the source in [currentIndex..nextIndex] . + private int currentIndex; + private int nextIndex; + + // A buffer for holding intermediate results + private StringBuilder buffer; + private int bufferPos; + + // Helper classes to defer loading of normalization data. + private static final class ModeImpl { + private ModeImpl(Normalizer2 n2) { + normalizer2 = n2; + } + + private final Normalizer2 normalizer2; + } + + private static final class NFDModeImpl { + private static final ModeImpl INSTANCE = new ModeImpl(Normalizer2.getNFDInstance()); + } + + private static final class NFKDModeImpl { + private static final ModeImpl INSTANCE = new ModeImpl(Normalizer2.getNFKDInstance()); + } + + private static final class NFCModeImpl { + private static final ModeImpl INSTANCE = new ModeImpl(Normalizer2.getNFCInstance()); + } + + private static final class NFKCModeImpl { + private static final ModeImpl INSTANCE = new ModeImpl(Normalizer2.getNFKCInstance()); + } + + private static final class Unicode32 { + private static final UnicodeSet INSTANCE = new UnicodeSet("[:age=3.2:]").freeze(); + } + + private static final class NFD32ModeImpl { + private static final ModeImpl INSTANCE = new ModeImpl( + new FilteredNormalizer2(Normalizer2.getNFDInstance(), Unicode32.INSTANCE)); + } + + private static final class NFKD32ModeImpl { + private static final ModeImpl INSTANCE = new ModeImpl( + new FilteredNormalizer2(Normalizer2.getNFKDInstance(), Unicode32.INSTANCE)); + } + + private static final class NFC32ModeImpl { + private static final ModeImpl INSTANCE = new ModeImpl( + new FilteredNormalizer2(Normalizer2.getNFCInstance(), Unicode32.INSTANCE)); + } + + private static final class NFKC32ModeImpl { + private static final ModeImpl INSTANCE = new ModeImpl( + new FilteredNormalizer2(Normalizer2.getNFKCInstance(), Unicode32.INSTANCE)); + } + + /** + * Options bit set value to select Unicode 3.2 normalization (except + * NormalizationCorrections). At most one Unicode version can be selected at a + * time. + * + * @stable ICU 2.6 + */ + public static final int UNICODE_3_2 = 0x20; + + public static final int UNICODE_3_2_0_ORIGINAL = UNICODE_3_2; + + /* + * Default option for the latest Unicode normalization. This option is provided + * mainly for testing. The value zero means that normalization is done with the + * fixes for - Corrigendum 4 (Five CJK Canonical Mapping Errors) - Corrigendum 5 + * (Normalization Idempotency) + */ + public static final int UNICODE_LATEST = 0x00; + + /** + * Constant indicating that the end of the iteration has been reached. This is + * guaranteed to have the same value as {@link UCharacterIterator#DONE}. + * + * @stable ICU 2.8 + */ + public static final int DONE = UCharacterIterator.DONE; + + /** + * Constants for normalization modes. + *

+ * The Mode class is not intended for public subclassing. Only the Mode + * constants provided by the Normalizer class should be used, and any fields or + * methods should not be called or overridden by users. + * + * @stable ICU 2.8 + */ + public abstract static class Mode { + + /** + * Sole constructor + * + * @internal + * @deprecated This API is ICU internal only. + */ + @Deprecated + protected Mode() { + } + + /** + * @internal + * @deprecated This API is ICU internal only. + */ + @Deprecated + protected abstract Normalizer2 getNormalizer2(int options); + } + + private static Mode toMode(Normalizer.Form form) { + switch (form) { + case NFC: + return NFC; + case NFD: + return NFD; + case NFKC: + return NFKC; + case NFKD: + return NFKD; + } + + throw new IllegalArgumentException("Unexpected normalization form: " + form); + } + + private static final class NONEMode extends Mode { + protected Normalizer2 getNormalizer2(int options) { + return Norm2AllModes.NOOP_NORMALIZER2; + } + } + + private static final class NFDMode extends Mode { + protected Normalizer2 getNormalizer2(int options) { + return (options & UNICODE_3_2) != 0 ? NFD32ModeImpl.INSTANCE.normalizer2 : NFDModeImpl.INSTANCE.normalizer2; + } + } + + private static final class NFKDMode extends Mode { + protected Normalizer2 getNormalizer2(int options) { + return (options & UNICODE_3_2) != 0 ? NFKD32ModeImpl.INSTANCE.normalizer2 + : NFKDModeImpl.INSTANCE.normalizer2; + } + } + + private static final class NFCMode extends Mode { + protected Normalizer2 getNormalizer2(int options) { + return (options & UNICODE_3_2) != 0 ? NFC32ModeImpl.INSTANCE.normalizer2 : NFCModeImpl.INSTANCE.normalizer2; + } + } + + private static final class NFKCMode extends Mode { + protected Normalizer2 getNormalizer2(int options) { + return (options & UNICODE_3_2) != 0 ? NFKC32ModeImpl.INSTANCE.normalizer2 + : NFKCModeImpl.INSTANCE.normalizer2; + } + } + + /** + * No decomposition/composition. + * + * @stable ICU 2.8 + */ + public static final Mode NONE = new NONEMode(); + + /** + * Canonical decomposition. + * + * @stable ICU 2.8 + */ + public static final Mode NFD = new NFDMode(); + + /** + * Compatibility decomposition. + * + * @stable ICU 2.8 + */ + public static final Mode NFKD = new NFKDMode(); + + /** + * Canonical decomposition followed by canonical composition. + * + * @stable ICU 2.8 + */ + public static final Mode NFC = new NFCMode(); + + public static final Mode NFKC = new NFKCMode(); + + // ------------------------------------------------------------------------- + // Iterator constructors + // ------------------------------------------------------------------------- + + /** + * Creates a new {@code NormalizerBase} object for iterating over the normalized + * form of a given string. + *

+ * The {@code options} parameter specifies which optional {@code NormalizerBase} + * features are to be enabled for this object. + *

+ * + * @param str The string to be normalized. The normalization will start at the + * beginning of the string. + * + * @param mode The normalization mode. + * + * @param opt Any optional features to be enabled. Currently the only available + * option is {@link #UNICODE_3_2}. If you want the default behavior + * corresponding to one of the standard Unicode Normalization Forms, + * use 0 for this argument. + * @stable ICU 2.6 + */ + public NormalizerBase(String str, Mode mode, int opt) { + this.text = UCharacterIterator.getInstance(str); + this.mode = mode; + this.options = opt; + norm2 = mode.getNormalizer2(opt); + buffer = new StringBuilder(); + } + + public NormalizerBase(String str, Mode mode) { + this(str, mode, 0); + } + + /** + * Creates a new {@code NormalizerBase} 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. + * + * @param opt Any optional features to be enabled. Currently the only available + * option is {@link #UNICODE_3_2}. If you want the default behavior + * corresponding to one of the standard Unicode Normalization Forms, + * use 0 for this argument. + * @stable ICU 2.6 + */ + public NormalizerBase(CharacterIterator iter, Mode mode, int opt) { + this.text = UCharacterIterator.getInstance((CharacterIterator) iter.clone()); + this.mode = mode; + this.options = opt; + norm2 = mode.getNormalizer2(opt); + buffer = new StringBuilder(); + } + + public NormalizerBase(CharacterIterator iter, Mode mode) { + this(iter, mode, 0); + } + + /** + * Clones this {@code NormalizerBase} object. All properties of this object are + * duplicated in the new object, including the cloning of any + * {@link CharacterIterator} that was passed in to the constructor or to + * {@link #setText(CharacterIterator) setText}. However, the text storage + * underlying the {@code CharacterIterator} is not duplicated unless the + * iterator's {@code clone} method does so. + * + * @stable ICU 2.8 + */ + public Object clone() { + try { + NormalizerBase copy = (NormalizerBase) super.clone(); + copy.text = (UCharacterIterator) text.clone(); + copy.mode = mode; + copy.options = options; + copy.norm2 = norm2; + copy.buffer = new StringBuilder(buffer); + copy.bufferPos = bufferPos; + copy.currentIndex = currentIndex; + copy.nextIndex = nextIndex; + return copy; + } catch (CloneNotSupportedException e) { + throw new InternalError(e.toString(), e); + } + } + + /** + * Normalizes a {@code String} using the given normalization operation. + *

+ * The {@code options} parameter specifies which optional {@code NormalizerBase} + * features are to be enabled for this operation. Currently the only available + * option is {@link #UNICODE_3_2}. If you want the default behavior + * corresponding to one of the standard Unicode Normalization Forms, use 0 for + * this argument. + *

+ * + * @param str the input string to be normalized. + * @param mode the normalization mode + * @param options the optional features to be enabled. + * @return String the normalized string + * @stable ICU 2.6 + */ + public static String normalize(String str, Mode mode, int options) { + return mode.getNormalizer2(options).normalize(str); + } + + public static String normalize(String str, Normalizer.Form form) { + return NormalizerBase.normalize(str, toMode(form), UNICODE_LATEST); + } + + public static String normalize(String str, Normalizer.Form form, int options) { + return NormalizerBase.normalize(str, toMode(form), options); + } + + /** + * Test if a string is in a given normalization form. This is semantically + * equivalent to source.equals(normalize(source, mode)). + * + * Unlike 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 str the input string to be checked to see if it is normalized + * @param mode the normalization mode + * @param options Options for use with exclusion set and tailored Normalization + * The only option that is currently recognized is UNICODE_3_2 + * @see #isNormalized + * @stable ICU 2.6 + */ + public static boolean isNormalized(String str, Mode mode, int options) { + return mode.getNormalizer2(options).isNormalized(str); + } + + public static boolean isNormalized(String str, Normalizer.Form form) { + return NormalizerBase.isNormalized(str, toMode(form), UNICODE_LATEST); + } + + public static boolean isNormalized(String str, Normalizer.Form form, int options) { + return NormalizerBase.isNormalized(str, toMode(form), options); + } + + // ------------------------------------------------------------------------- + // Iteration API + // ------------------------------------------------------------------------- + + /** + * Return the current character in the normalized text. + * + * @return The codepoint as an int + * @stable ICU 2.8 + */ + public int current() { + if (bufferPos < buffer.length() || nextNormalize()) { + return buffer.codePointAt(bufferPos); + } else { + return DONE; + } + } + + /** + * Return the next character in the normalized text and advance the iteration + * position by one. If the end of the text has already been reached, + * {@link #DONE} is returned. + * + * @return The codepoint as an int + * @stable ICU 2.8 + */ + public int next() { + if (bufferPos < buffer.length() || nextNormalize()) { + int c = buffer.codePointAt(bufferPos); + bufferPos += Character.charCount(c); + return c; + } else { + return DONE; + } + } + + /** + * Return the previous character in the normalized text and decrement the + * iteration position by one. If the beginning of the text has already been + * reached, {@link #DONE} is returned. + * + * @return The codepoint as an int + * @stable ICU 2.8 + */ + public int previous() { + if (bufferPos > 0 || previousNormalize()) { + int c = buffer.codePointBefore(bufferPos); + bufferPos -= Character.charCount(c); + return c; + } else { + return DONE; + } + } + + /** + * Reset the index to the beginning of the text. This is equivalent to + * setIndexOnly(startIndex)). + * + * @stable ICU 2.8 + */ + public void reset() { + text.setIndex(0); + currentIndex = nextIndex = 0; + clearBuffer(); + } + + /** + * 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. + * @stable ICU 2.8 + */ + public void setIndexOnly(int index) { + text.setIndex(index); // validates index + currentIndex = nextIndex = index; + clearBuffer(); + } + + /** + * Set the iteration position in the input text that is being normalized and + * return the first normalized character at that position. + *

+ * Note: This method sets the position in the input text, while + * {@link #next} and {@link #previous} iterate through characters in the + * normalized output. This means that there is not necessarily a + * one-to-one correspondence between characters returned by {@code next} and + * {@code previous} and the indices passed to and returned from {@code setIndex} + * and {@link #getIndex}. + *

+ * + * @param index the desired index in the input text. + * + * @return the first normalized character that is the result of iterating + * forward starting at the given index. + * + * @throws IllegalArgumentException if the given index is less than + * {@link #getBeginIndex} or greater than + * {@link #getEndIndex}. deprecated ICU 3.2 + * @obsolete ICU 3.2 + */ + public int setIndex(int index) { + setIndexOnly(index); + return current(); + } + + /** + * Retrieve the index of the start of the input text. This is the begin index of + * the {@code CharacterIterator} or the start (i.e. 0) of the {@code String} + * over which this {@code NormalizerBase} is iterating + * + * @deprecated ICU 2.2. Use startIndex() instead. + * @return The codepoint as an int + * @see #startIndex + */ + @Deprecated + public int getBeginIndex() { + return 0; + } + + /** + * Retrieve the index of the end of the input text. This is the end index of the + * {@code CharacterIterator} or the length of the {@code String} over which this + * {@code NormalizerBase} is iterating + * + * @deprecated ICU 2.2. Use endIndex() instead. + * @return The codepoint as an int + * @see #endIndex + */ + @Deprecated + public int getEndIndex() { + return endIndex(); + } + + /** + * 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 {@code next} and + * {@code previous} and the indices passed to and returned from {@code setIndex} + * and {@link #getIndex}. + * + * @return The current iteration position + * @stable ICU 2.8 + */ + public int getIndex() { + if (bufferPos < buffer.length()) { + return currentIndex; + } else { + return nextIndex; + } + } + + /** + * Retrieve the index of the end of the input text. This is the end index of the + * {@code CharacterIterator} or the length of the {@code String} over which this + * {@code NormalizerBase} is iterating + * + * @return The current iteration position + * @stable ICU 2.8 + */ + public int endIndex() { + return text.getLength(); + } + + // ------------------------------------------------------------------------- + // Iterator attributes + // ------------------------------------------------------------------------- + /** + * 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 #setText + * setText()}, {@link #first}, {@link #last}, etc. after calling + * {@code setMode}. + *

+ * + * @param newMode the new mode for this {@code NormalizerBase}. The supported + * modes are: + *

    + *
  • {@link #NFC} - Unicode canonical decompositiion followed + * by canonical composition. + *
  • {@link #NFKC} - Unicode compatibility decompositiion + * follwed by canonical composition. + *
  • {@link #NFD} - Unicode canonical decomposition + *
  • {@link #NFKD} - Unicode compatibility decomposition. + *
  • {@link #NONE} - Do nothing but return characters from the + * underlying input text. + *
+ * + * @see #getMode + * @stable ICU 2.8 + */ + public void setMode(Mode newMode) { + mode = newMode; + norm2 = mode.getNormalizer2(options); + } + + /** + * Return the basic operation performed by this {@code NormalizerBase} + * + * @see #setMode + * @stable ICU 2.8 + */ + public Mode getMode() { + return mode; + } + + /** + * Set the input text over which this {@code NormalizerBase} will iterate. The + * iteration position is set to the beginning of the input text. + * + * @param newText The new string to be normalized. + * @stable ICU 2.8 + */ + public void setText(String newText) { + UCharacterIterator newIter = UCharacterIterator.getInstance(newText); + if (newIter == null) { + throw new IllegalStateException("Could not create a new UCharacterIterator"); + } + text = newIter; + reset(); + } + + /** + * Set the input text over which this {@code NormalizerBase} will iterate. The + * iteration position is set to the beginning of the input text. + * + * @param newText The new string to be normalized. + * @stable ICU 2.8 + */ + public void setText(CharacterIterator newText) { + UCharacterIterator newIter = UCharacterIterator.getInstance(newText); + if (newIter == null) { + throw new IllegalStateException("Could not create a new UCharacterIterator"); + } + text = newIter; + currentIndex = nextIndex = 0; + clearBuffer(); + } + + private void clearBuffer() { + buffer.setLength(0); + bufferPos = 0; + } + + private boolean nextNormalize() { + clearBuffer(); + currentIndex = nextIndex; + text.setIndex(nextIndex); + // Skip at least one character so we make progress. + int c = text.nextCodePoint(); + if (c < 0) { + return false; + } + StringBuilder segment = new StringBuilder().appendCodePoint(c); + while ((c = text.nextCodePoint()) >= 0) { + if (norm2.hasBoundaryBefore(c)) { + text.moveCodePointIndex(-1); + break; + } + segment.appendCodePoint(c); + } + nextIndex = text.getIndex(); + norm2.normalize(segment, buffer); + return buffer.length() != 0; + } + + private boolean previousNormalize() { + clearBuffer(); + nextIndex = currentIndex; + text.setIndex(currentIndex); + StringBuilder segment = new StringBuilder(); + int c; + while ((c = text.previousCodePoint()) >= 0) { + if (c <= 0xffff) { + segment.insert(0, (char) c); + } else { + segment.insert(0, Character.toChars(c)); + } + if (norm2.hasBoundaryBefore(c)) { + break; + } + } + currentIndex = text.getIndex(); + norm2.normalize(segment, buffer); + bufferPos = buffer.length(); + return buffer.length() != 0; + } + +} diff --git a/src/main/java/jdk_internal/icu/text/Replaceable.java b/src/main/java/jdk_internal/icu/text/Replaceable.java index cd843b17..d282efbd 100755 --- a/src/main/java/jdk_internal/icu/text/Replaceable.java +++ b/src/main/java/jdk_internal/icu/text/Replaceable.java @@ -1,124 +1,124 @@ -/* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ******************************************************************************* - * (C) Copyright IBM Corp. 1996-2005 - All Rights Reserved * - * * - * The original version of this source code and documentation is copyrighted * - * and owned by IBM, These materials are provided under terms of a License * - * Agreement between IBM and Sun. This technology is protected by multiple * - * US and International patents. This notice and attribution to IBM may not * - * to removed. * - ******************************************************************************* - */ - -package jdk_internal.icu.text; - -/** - * Replaceable is an interface 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. - * - *

- * Copyright © IBM Corporation 1999. All rights reserved. - * - * @author Alan Liu - * @stable ICU 2.0 - */ -public interface Replaceable { - /** - * Returns the number of 16-bit code units in the text. - * - * @return number of 16-bit code units in text - * @stable ICU 2.0 - */ - int length(); - - /** - * 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 2.0 - */ - char charAt(int offset); - - /** - * Copies characters from this object into the destination character array. The - * first character to be copied is at index srcStart; the last - * character to be copied is at index srcLimit-1 (thus the total - * number of characters to be copied is srcLimit-srcStart). The - * characters are copied into the subarray of dst starting at index - * dstStart and ending at index - * dstStart + (srcLimit-srcStart) - 1. - * - * @param srcStart the beginning index to copy, inclusive; - * {@code 0 <= start <= limit}. - * @param srcLimit the ending index to copy, exclusive; - * {@code start <= limit <= length()}. - * @param dst the destination array. - * @param dstStart the start offset in the destination array. - * @stable ICU 2.0 - */ - void getChars(int srcStart, int srcLimit, char dst[], int dstStart); -} +/* + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ******************************************************************************* + * (C) Copyright IBM Corp. 1996-2005 - All Rights Reserved * + * * + * The original version of this source code and documentation is copyrighted * + * and owned by IBM, These materials are provided under terms of a License * + * Agreement between IBM and Sun. This technology is protected by multiple * + * US and International patents. This notice and attribution to IBM may not * + * to removed. * + ******************************************************************************* + */ + +package jdk_internal.icu.text; + +/** + * Replaceable is an interface 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. + * + *

+ * Copyright © IBM Corporation 1999. All rights reserved. + * + * @author Alan Liu + * @stable ICU 2.0 + */ +public interface Replaceable { + /** + * Returns the number of 16-bit code units in the text. + * + * @return number of 16-bit code units in text + * @stable ICU 2.0 + */ + int length(); + + /** + * 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 2.0 + */ + char charAt(int offset); + + /** + * Copies characters from this object into the destination character array. The + * first character to be copied is at index srcStart; the last + * character to be copied is at index srcLimit-1 (thus the total + * number of characters to be copied is srcLimit-srcStart). The + * characters are copied into the subarray of dst starting at index + * dstStart and ending at index + * dstStart + (srcLimit-srcStart) - 1. + * + * @param srcStart the beginning index to copy, inclusive; + * {@code 0 <= start <= limit}. + * @param srcLimit the ending index to copy, exclusive; + * {@code start <= limit <= length()}. + * @param dst the destination array. + * @param dstStart the start offset in the destination array. + * @stable ICU 2.0 + */ + void getChars(int srcStart, int srcLimit, char dst[], int dstStart); +} diff --git a/src/main/java/jdk_internal/icu/text/ReplaceableString.java b/src/main/java/jdk_internal/icu/text/ReplaceableString.java index c9b75d6d..184ae0c1 100755 --- a/src/main/java/jdk_internal/icu/text/ReplaceableString.java +++ b/src/main/java/jdk_internal/icu/text/ReplaceableString.java @@ -1,121 +1,121 @@ -/* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ******************************************************************************* - * Copyright (C) 1996-2009, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - */ - -package jdk_internal.icu.text; - -/** - * ReplaceableString is an adapter class that implements the - * Replaceable API around an ordinary StringBuffer. - * - *

- * Note: This class does not support attributes and is not intended for - * general use. Most clients will need to implement {@link Replaceable} in their - * text representation class. - * - *

- * Copyright © IBM Corporation 1999. All rights reserved. - * - * @see Replaceable - * @author Alan Liu - * @stable ICU 2.0 - */ -public class ReplaceableString implements Replaceable { - - private StringBuffer buf; - - /** - * Construct a new object with the given initial contents. - * - * @param str initial contents - * @stable ICU 2.0 - */ - public ReplaceableString(String str) { - buf = new StringBuffer(str); - } - - /** - * Construct a new object using buf for internal storage. The - * contents of buf at the time of construction are used as the - * initial contents. Note! Modifications to buf will modify - * this object, and vice versa. - * - * @param buf object to be used as internal storage - * @stable ICU 2.0 - */ - public ReplaceableString(StringBuffer buf) { - this.buf = buf; - } - - /** - * Return the number of characters contained in this object. - * Replaceable API. - * - * @stable ICU 2.0 - */ - public int length() { - return buf.length(); - } - - /** - * Return the character at the given position in this object. - * Replaceable API. - * - * @param offset offset into the contents, from 0 to length() - 1 - * @stable ICU 2.0 - */ - public char charAt(int offset) { - return buf.charAt(offset); - } - - /** - * Copies characters from this object into the destination character array. The - * first character to be copied is at index srcStart; the last - * character to be copied is at index srcLimit-1 (thus the total - * number of characters to be copied is srcLimit-srcStart). The - * characters are copied into the subarray of dst starting at index - * dstStart and ending at index - * dstStart + (srcLimit-srcStart) - 1. - * - * @param srcStart the beginning index to copy, inclusive; - * {@code 0 <= start <= limit}. - * @param srcLimit the ending index to copy, exclusive; - * {@code start <= limit <= length()}. - * @param dst the destination array. - * @param dstStart the start offset in the destination array. - * @stable ICU 2.0 - */ - public void getChars(int srcStart, int srcLimit, char dst[], int dstStart) { - if (srcStart != srcLimit) { - buf.getChars(srcStart, srcLimit, dst, dstStart); - } - } -} +/* + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ******************************************************************************* + * Copyright (C) 1996-2009, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + */ + +package jdk_internal.icu.text; + +/** + * ReplaceableString is an adapter class that implements the + * Replaceable API around an ordinary StringBuffer. + * + *

+ * Note: This class does not support attributes and is not intended for + * general use. Most clients will need to implement {@link Replaceable} in their + * text representation class. + * + *

+ * Copyright © IBM Corporation 1999. All rights reserved. + * + * @see Replaceable + * @author Alan Liu + * @stable ICU 2.0 + */ +public class ReplaceableString implements Replaceable { + + private StringBuffer buf; + + /** + * Construct a new object with the given initial contents. + * + * @param str initial contents + * @stable ICU 2.0 + */ + public ReplaceableString(String str) { + buf = new StringBuffer(str); + } + + /** + * Construct a new object using buf for internal storage. The + * contents of buf at the time of construction are used as the + * initial contents. Note! Modifications to buf will modify + * this object, and vice versa. + * + * @param buf object to be used as internal storage + * @stable ICU 2.0 + */ + public ReplaceableString(StringBuffer buf) { + this.buf = buf; + } + + /** + * Return the number of characters contained in this object. + * Replaceable API. + * + * @stable ICU 2.0 + */ + public int length() { + return buf.length(); + } + + /** + * Return the character at the given position in this object. + * Replaceable API. + * + * @param offset offset into the contents, from 0 to length() - 1 + * @stable ICU 2.0 + */ + public char charAt(int offset) { + return buf.charAt(offset); + } + + /** + * Copies characters from this object into the destination character array. The + * first character to be copied is at index srcStart; the last + * character to be copied is at index srcLimit-1 (thus the total + * number of characters to be copied is srcLimit-srcStart). The + * characters are copied into the subarray of dst starting at index + * dstStart and ending at index + * dstStart + (srcLimit-srcStart) - 1. + * + * @param srcStart the beginning index to copy, inclusive; + * {@code 0 <= start <= limit}. + * @param srcLimit the ending index to copy, exclusive; + * {@code start <= limit <= length()}. + * @param dst the destination array. + * @param dstStart the start offset in the destination array. + * @stable ICU 2.0 + */ + public void getChars(int srcStart, int srcLimit, char dst[], int dstStart) { + if (srcStart != srcLimit) { + buf.getChars(srcStart, srcLimit, dst, dstStart); + } + } +} diff --git a/src/main/java/jdk_internal/icu/text/StringPrep.java b/src/main/java/jdk_internal/icu/text/StringPrep.java index 656c484b..ccdded6c 100755 --- a/src/main/java/jdk_internal/icu/text/StringPrep.java +++ b/src/main/java/jdk_internal/icu/text/StringPrep.java @@ -1,493 +1,493 @@ -/* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -/* -/* - ******************************************************************************* - * Copyright (C) 2003-2004, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - */ -// -// CHANGELOG -// 2005-05-19 Edward Wang -// - copy this file from icu4jsrc_3_2/src/com/ibm/icu/text/StringPrep.java -// - move from package com.ibm.icu.text to package sun.net.idn -// - use ParseException instead of StringPrepParseException -// - change 'Normalizer.getUnicodeVersion()' to 'NormalizerImpl.getUnicodeVersion()' -// - remove all @deprecated tag to make compiler happy -// 2007-08-14 Martin Buchholz -// - remove redundant casts -// -package jdk_internal.icu.text; - -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; - -import jdk_internal.bidi.Normalizer; -import jdk_internal.bidi.ParseException; -import jdk_internal.bidi.SunNormalizer; -import jdk_internal.icu.impl.CharTrie; -import jdk_internal.icu.impl.StringPrepDataReader; -import jdk_internal.icu.impl.Trie; -import jdk_internal.icu.lang.UCharacter; -import jdk_internal.icu.lang.UCharacterDirection; -import jdk_internal.icu.util.VersionInfo; - -/** - * 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 which the Unicode Strings are - * prepared. Each profiles contains tables which describe how a code point - * should be treated. The tables are broadly classied 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 prohibted from the output - * of the StringPrep processing function.
  • - *
  • Mapping Table: Contains code ponts 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 - * @draft ICU 2.8 - */ -public final class StringPrep { - /** - * Option to prohibit processing of unassigned code points in the input - * - * @see #prepare - * @draft ICU 2.8 - */ - public static final int DEFAULT = 0x0000; - - /** - * Option to allow processing of unassigned code points in the input - * - * @see #prepare - * @draft ICU 2.8 - */ - public static final int ALLOW_UNASSIGNED = 0x0001; - - private static final int UNASSIGNED = 0x0000; - private static final int MAP = 0x0001; - private static final int PROHIBITED = 0x0002; - private static final int DELETE = 0x0003; - private static final int TYPE_LIMIT = 0x0004; - - private static final int NORMALIZATION_ON = 0x0001; - private static final int CHECK_BIDI_ON = 0x0002; - - private static final int TYPE_THRESHOLD = 0xFFF0; - private static final int MAX_INDEX_VALUE = 0x3FBF; /* 16139 */ - private static final int MAX_INDEX_TOP_LENGTH = 0x0003; - - /* indexes[] value names */ - private static final int INDEX_TRIE_SIZE = 0; /* number of bytes in normalization trie */ - private static final int INDEX_MAPPING_DATA_SIZE = 1; /* The array that contains the mapping */ - private static final int NORM_CORRECTNS_LAST_UNI_VERSION = 2; /* - * The index of Unicode version of last entry in - * NormalizationCorrections.txt - */ - private static final int ONE_UCHAR_MAPPING_INDEX_START = 3; /* - * The starting index of 1 UChar mapping index in the - * mapping data array - */ - private static final int TWO_UCHARS_MAPPING_INDEX_START = 4; /* - * The starting index of 2 UChars mapping index in - * the mapping data array - */ - private static final int THREE_UCHARS_MAPPING_INDEX_START = 5; - private static final int FOUR_UCHARS_MAPPING_INDEX_START = 6; - private static final int OPTIONS = 7; /* Bit set of options to turn on in the profile */ - private static final int INDEX_TOP = 16; /* changing this requires a new formatVersion */ - - /** - * Default buffer size of datafile - */ - private static final int DATA_BUFFER_SIZE = 25000; - - /* Wrappers for Trie implementations */ - private static final class StringPrepTrieImpl implements Trie.DataManipulate { - private CharTrie sprepTrie = null; - - /** - * Called by com.ibm.icu.util.Trie to extract from a lead surrogate's data the - * index array offset of the indexes for that lead surrogate. - * - * @param property data value for a surrogate from the trie, including the - * folding offset - * @return data offset or 0 if there is no data for the lead surrogate - */ - public int getFoldingOffset(int value) { - return value; - } - } - - // CharTrie implementation for reading the trie data - private StringPrepTrieImpl sprepTrieImpl; - // Indexes read from the data file - private int[] indexes; - // mapping data read from the data file - private char[] mappingData; - // format version of the data file - private byte[] formatVersion; - // the version of Unicode supported by the data file - private VersionInfo sprepUniVer; - // the Unicode version of last entry in the - // NormalizationCorrections.txt file if normalization - // is turned on - private VersionInfo normCorrVer; - // Option to turn on Normalization - private boolean doNFKC; - // Option to turn on checking for BiDi rules - private boolean checkBiDi; - - private char getCodePointValue(int ch) { - return sprepTrieImpl.sprepTrie.getCodePointValue(ch); - } - - private static VersionInfo getVersionInfo(int comp) { - int micro = comp & 0xFF; - int milli = (comp >> 8) & 0xFF; - int minor = (comp >> 16) & 0xFF; - int major = (comp >> 24) & 0xFF; - return VersionInfo.getInstance(major, minor, milli, micro); - } - - private static VersionInfo getVersionInfo(byte[] version) { - if (version.length != 4) { - return null; - } - return VersionInfo.getInstance((int) version[0], (int) version[1], (int) version[2], (int) version[3]); - } - - /** - * Creates an StringPrep object after reading the input stream. The object does - * not hold a reference to the input steam, so the stream can be closed after - * the method returns. - * - * @param inputStream The stream for reading the StringPrep profile binarySun - * @throws IOException - * @draft ICU 2.8 - */ - public StringPrep(InputStream inputStream) throws IOException { - - BufferedInputStream b = new BufferedInputStream(inputStream, DATA_BUFFER_SIZE); - - StringPrepDataReader reader = new StringPrepDataReader(b); - - // read the indexes - indexes = reader.readIndexes(INDEX_TOP); - - byte[] sprepBytes = new byte[indexes[INDEX_TRIE_SIZE]]; - - // indexes[INDEX_MAPPING_DATA_SIZE] store the size of mappingData in bytes - mappingData = new char[indexes[INDEX_MAPPING_DATA_SIZE] / 2]; - // load the rest of the data and initialize the data members - reader.read(sprepBytes, mappingData); - - sprepTrieImpl = new StringPrepTrieImpl(); - sprepTrieImpl.sprepTrie = new CharTrie(new ByteArrayInputStream(sprepBytes), sprepTrieImpl); - - // get the data format version - formatVersion = reader.getDataFormatVersion(); - - // get the options - doNFKC = ((indexes[OPTIONS] & NORMALIZATION_ON) > 0); - checkBiDi = ((indexes[OPTIONS] & CHECK_BIDI_ON) > 0); - sprepUniVer = getVersionInfo(reader.getUnicodeVersion()); - normCorrVer = getVersionInfo(indexes[NORM_CORRECTNS_LAST_UNI_VERSION]); - VersionInfo normUniVer = UCharacter.getUnicodeVersion(); - if (normUniVer.compareTo(sprepUniVer) < 0 && /* - * the Unicode version of SPREP file must be less than the - * Unicode Vesion of the normalization data - */ - normUniVer.compareTo(normCorrVer) < 0 - && /* - * the Unicode version of the NormalizationCorrections.txt file should be less - * than the Unicode Vesion of the normalization data - */ - ((indexes[OPTIONS] & NORMALIZATION_ON) > 0) /* normalization turned on */ - ) { - throw new IOException("Normalization Correction version not supported"); - } - b.close(); - } - - private static final class Values { - boolean isIndex; - int value; - int type; - - public void reset() { - isIndex = false; - value = 0; - type = -1; - } - } - - private static final void getValues(char trieWord, Values values) { - values.reset(); - if (trieWord == 0) { - /* - * Initial value stored in the mapping table just return TYPE_LIMIT .. so that - * the source codepoint is copied to the destination - */ - values.type = TYPE_LIMIT; - } else if (trieWord >= TYPE_THRESHOLD) { - values.type = (trieWord - TYPE_THRESHOLD); - } else { - /* get the type */ - values.type = MAP; - /* ascertain if the value is index or delta */ - if ((trieWord & 0x02) > 0) { - values.isIndex = true; - values.value = trieWord >> 2; // mask off the lower 2 bits and shift - - } else { - values.isIndex = false; - values.value = (trieWord << 16) >> 16; - values.value = (values.value >> 2); - - } - - if ((trieWord >> 2) == MAX_INDEX_VALUE) { - values.type = DELETE; - values.isIndex = false; - values.value = 0; - } - } - } - - private StringBuffer map(UCharacterIterator iter, int options) throws ParseException { - - Values val = new Values(); - char result = 0; - int ch = UCharacterIterator.DONE; - StringBuffer dest = new StringBuffer(); - boolean allowUnassigned = ((options & ALLOW_UNASSIGNED) > 0); - - while ((ch = iter.nextCodePoint()) != UCharacterIterator.DONE) { - - result = getCodePointValue(ch); - getValues(result, val); - - // check if the source codepoint is unassigned - if (val.type == UNASSIGNED && allowUnassigned == false) { - throw new ParseException("An unassigned code point was found in the input " + iter.getText(), - iter.getIndex()); - } else if ((val.type == MAP)) { - int index, length; - - if (val.isIndex) { - index = val.value; - if (index >= indexes[ONE_UCHAR_MAPPING_INDEX_START] - && index < indexes[TWO_UCHARS_MAPPING_INDEX_START]) { - length = 1; - } else if (index >= indexes[TWO_UCHARS_MAPPING_INDEX_START] - && index < indexes[THREE_UCHARS_MAPPING_INDEX_START]) { - length = 2; - } else if (index >= indexes[THREE_UCHARS_MAPPING_INDEX_START] - && index < indexes[FOUR_UCHARS_MAPPING_INDEX_START]) { - length = 3; - } else { - length = mappingData[index++]; - } - /* copy mapping to destination */ - dest.append(mappingData, index, length); - continue; - - } else { - ch -= val.value; - } - } else if (val.type == DELETE) { - // just consume the codepoint and contine - continue; - } - // copy the source into destination - UTF16.append(dest, ch); - } - - return dest; - } - - private StringBuffer normalize(StringBuffer src) { - /* - * Option UNORM_BEFORE_PRI_29: - * - * IDNA as interpreted by IETF members (see unicode mailing list 2004H1) - * requires strict adherence to Unicode 3.2 normalization, including buggy - * composition from before fixing Public Review Issue #29. Note that this - * results in some valid but nonsensical text to be either corrupted or - * rejected, depending on the text. See - * http://www.unicode.org/review/resolved-pri.html#pri29 See unorm.cpp and - * cnormtst.c - */ - return new StringBuffer( - SunNormalizer.normalize(src.toString(), Normalizer.Form.NFKC, SunNormalizer.UNICODE_3_2)); - } - - /* - * boolean isLabelSeparator(int ch){ int result = getCodePointValue(ch); if( - * (result & 0x07) == LABEL_SEPARATOR){ return true; } return false; } - */ - /* - * 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. - */ - /** - * Prepare the input buffer for use in applications with the given profile. This - * operation maps, normalizes(NFKC), checks for prohited and BiDi characters in - * the order defined by RFC 3454 depending on the options specified in the - * profile. - * - * @param src A UCharacterIterator object containing the source string - * @param options A bit set of options: - * - * - StringPrep.NONE Prohibit processing of unassigned code - * points in the input - * - * - StringPrep.ALLOW_UNASSIGNED Treat the unassigned code points - * are in the input as normal Unicode code points. - * - * @return StringBuffer A StringBuffer containing the output - * @throws ParseException - * @draft ICU 2.8 - */ - public StringBuffer prepare(UCharacterIterator src, int options) throws ParseException { - - // map - StringBuffer mapOut = map(src, options); - StringBuffer normOut = mapOut;// initialize - - if (doNFKC) { - // normalize - normOut = normalize(mapOut); - } - - int ch; - char result; - UCharacterIterator iter = UCharacterIterator.getInstance(normOut); - Values val = new Values(); - int direction = UCharacterDirection.CHAR_DIRECTION_COUNT, - firstCharDir = UCharacterDirection.CHAR_DIRECTION_COUNT; - int rtlPos = -1, ltrPos = -1; - boolean rightToLeft = false, leftToRight = false; - - while ((ch = iter.nextCodePoint()) != UCharacterIterator.DONE) { - result = getCodePointValue(ch); - getValues(result, val); - - if (val.type == PROHIBITED) { - throw new ParseException("A prohibited code point was found in the input" + iter.getText(), val.value); - } - - direction = UCharacter.getDirection(ch); - if (firstCharDir == UCharacterDirection.CHAR_DIRECTION_COUNT) { - firstCharDir = direction; - } - if (direction == UCharacterDirection.LEFT_TO_RIGHT) { - leftToRight = true; - ltrPos = iter.getIndex() - 1; - } - if (direction == UCharacterDirection.RIGHT_TO_LEFT - || direction == UCharacterDirection.RIGHT_TO_LEFT_ARABIC) { - rightToLeft = true; - rtlPos = iter.getIndex() - 1; - } - } - if (checkBiDi == true) { - // satisfy 2 - if (leftToRight == true && rightToLeft == true) { - throw new ParseException( - "The input does not conform to the rules for BiDi code points." + iter.getText(), - (rtlPos > ltrPos) ? rtlPos : ltrPos); - } - - // satisfy 3 - if (rightToLeft == true && !((firstCharDir == UCharacterDirection.RIGHT_TO_LEFT - || firstCharDir == UCharacterDirection.RIGHT_TO_LEFT_ARABIC) - && (direction == UCharacterDirection.RIGHT_TO_LEFT - || direction == UCharacterDirection.RIGHT_TO_LEFT_ARABIC))) { - throw new ParseException( - "The input does not conform to the rules for BiDi code points." + iter.getText(), - (rtlPos > ltrPos) ? rtlPos : ltrPos); - } - } - return normOut; - - } -} +/* + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* +/* + ******************************************************************************* + * Copyright (C) 2003-2004, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + */ +// +// CHANGELOG +// 2005-05-19 Edward Wang +// - copy this file from icu4jsrc_3_2/src/com/ibm/icu/text/StringPrep.java +// - move from package com.ibm.icu.text to package sun.net.idn +// - use ParseException instead of StringPrepParseException +// - change 'Normalizer.getUnicodeVersion()' to 'NormalizerImpl.getUnicodeVersion()' +// - remove all @deprecated tag to make compiler happy +// 2007-08-14 Martin Buchholz +// - remove redundant casts +// +package jdk_internal.icu.text; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +import jdk_internal.bidi.Normalizer; +import jdk_internal.bidi.ParseException; +import jdk_internal.bidi.SunNormalizer; +import jdk_internal.icu.impl.CharTrie; +import jdk_internal.icu.impl.StringPrepDataReader; +import jdk_internal.icu.impl.Trie; +import jdk_internal.icu.lang.UCharacter; +import jdk_internal.icu.lang.UCharacterDirection; +import jdk_internal.icu.util.VersionInfo; + +/** + * 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 which the Unicode Strings are + * prepared. Each profiles contains tables which describe how a code point + * should be treated. The tables are broadly classied 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 prohibted from the output + * of the StringPrep processing function.
  • + *
  • Mapping Table: Contains code ponts 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 + * @draft ICU 2.8 + */ +public final class StringPrep { + /** + * Option to prohibit processing of unassigned code points in the input + * + * @see #prepare + * @draft ICU 2.8 + */ + public static final int DEFAULT = 0x0000; + + /** + * Option to allow processing of unassigned code points in the input + * + * @see #prepare + * @draft ICU 2.8 + */ + public static final int ALLOW_UNASSIGNED = 0x0001; + + private static final int UNASSIGNED = 0x0000; + private static final int MAP = 0x0001; + private static final int PROHIBITED = 0x0002; + private static final int DELETE = 0x0003; + private static final int TYPE_LIMIT = 0x0004; + + private static final int NORMALIZATION_ON = 0x0001; + private static final int CHECK_BIDI_ON = 0x0002; + + private static final int TYPE_THRESHOLD = 0xFFF0; + private static final int MAX_INDEX_VALUE = 0x3FBF; /* 16139 */ + private static final int MAX_INDEX_TOP_LENGTH = 0x0003; + + /* indexes[] value names */ + private static final int INDEX_TRIE_SIZE = 0; /* number of bytes in normalization trie */ + private static final int INDEX_MAPPING_DATA_SIZE = 1; /* The array that contains the mapping */ + private static final int NORM_CORRECTNS_LAST_UNI_VERSION = 2; /* + * The index of Unicode version of last entry in + * NormalizationCorrections.txt + */ + private static final int ONE_UCHAR_MAPPING_INDEX_START = 3; /* + * The starting index of 1 UChar mapping index in the + * mapping data array + */ + private static final int TWO_UCHARS_MAPPING_INDEX_START = 4; /* + * The starting index of 2 UChars mapping index in + * the mapping data array + */ + private static final int THREE_UCHARS_MAPPING_INDEX_START = 5; + private static final int FOUR_UCHARS_MAPPING_INDEX_START = 6; + private static final int OPTIONS = 7; /* Bit set of options to turn on in the profile */ + private static final int INDEX_TOP = 16; /* changing this requires a new formatVersion */ + + /** + * Default buffer size of datafile + */ + private static final int DATA_BUFFER_SIZE = 25000; + + /* Wrappers for Trie implementations */ + private static final class StringPrepTrieImpl implements Trie.DataManipulate { + private CharTrie sprepTrie = null; + + /** + * Called by com.ibm.icu.util.Trie to extract from a lead surrogate's data the + * index array offset of the indexes for that lead surrogate. + * + * @param property data value for a surrogate from the trie, including the + * folding offset + * @return data offset or 0 if there is no data for the lead surrogate + */ + public int getFoldingOffset(int value) { + return value; + } + } + + // CharTrie implementation for reading the trie data + private StringPrepTrieImpl sprepTrieImpl; + // Indexes read from the data file + private int[] indexes; + // mapping data read from the data file + private char[] mappingData; + // format version of the data file + private byte[] formatVersion; + // the version of Unicode supported by the data file + private VersionInfo sprepUniVer; + // the Unicode version of last entry in the + // NormalizationCorrections.txt file if normalization + // is turned on + private VersionInfo normCorrVer; + // Option to turn on Normalization + private boolean doNFKC; + // Option to turn on checking for BiDi rules + private boolean checkBiDi; + + private char getCodePointValue(int ch) { + return sprepTrieImpl.sprepTrie.getCodePointValue(ch); + } + + private static VersionInfo getVersionInfo(int comp) { + int micro = comp & 0xFF; + int milli = (comp >> 8) & 0xFF; + int minor = (comp >> 16) & 0xFF; + int major = (comp >> 24) & 0xFF; + return VersionInfo.getInstance(major, minor, milli, micro); + } + + private static VersionInfo getVersionInfo(byte[] version) { + if (version.length != 4) { + return null; + } + return VersionInfo.getInstance((int) version[0], (int) version[1], (int) version[2], (int) version[3]); + } + + /** + * Creates an StringPrep object after reading the input stream. The object does + * not hold a reference to the input steam, so the stream can be closed after + * the method returns. + * + * @param inputStream The stream for reading the StringPrep profile binarySun + * @throws IOException + * @draft ICU 2.8 + */ + public StringPrep(InputStream inputStream) throws IOException { + + BufferedInputStream b = new BufferedInputStream(inputStream, DATA_BUFFER_SIZE); + + StringPrepDataReader reader = new StringPrepDataReader(b); + + // read the indexes + indexes = reader.readIndexes(INDEX_TOP); + + byte[] sprepBytes = new byte[indexes[INDEX_TRIE_SIZE]]; + + // indexes[INDEX_MAPPING_DATA_SIZE] store the size of mappingData in bytes + mappingData = new char[indexes[INDEX_MAPPING_DATA_SIZE] / 2]; + // load the rest of the data and initialize the data members + reader.read(sprepBytes, mappingData); + + sprepTrieImpl = new StringPrepTrieImpl(); + sprepTrieImpl.sprepTrie = new CharTrie(new ByteArrayInputStream(sprepBytes), sprepTrieImpl); + + // get the data format version + formatVersion = reader.getDataFormatVersion(); + + // get the options + doNFKC = ((indexes[OPTIONS] & NORMALIZATION_ON) > 0); + checkBiDi = ((indexes[OPTIONS] & CHECK_BIDI_ON) > 0); + sprepUniVer = getVersionInfo(reader.getUnicodeVersion()); + normCorrVer = getVersionInfo(indexes[NORM_CORRECTNS_LAST_UNI_VERSION]); + VersionInfo normUniVer = UCharacter.getUnicodeVersion(); + if (normUniVer.compareTo(sprepUniVer) < 0 && /* + * the Unicode version of SPREP file must be less than the + * Unicode Vesion of the normalization data + */ + normUniVer.compareTo(normCorrVer) < 0 + && /* + * the Unicode version of the NormalizationCorrections.txt file should be less + * than the Unicode Vesion of the normalization data + */ + ((indexes[OPTIONS] & NORMALIZATION_ON) > 0) /* normalization turned on */ + ) { + throw new IOException("Normalization Correction version not supported"); + } + b.close(); + } + + private static final class Values { + boolean isIndex; + int value; + int type; + + public void reset() { + isIndex = false; + value = 0; + type = -1; + } + } + + private static final void getValues(char trieWord, Values values) { + values.reset(); + if (trieWord == 0) { + /* + * Initial value stored in the mapping table just return TYPE_LIMIT .. so that + * the source codepoint is copied to the destination + */ + values.type = TYPE_LIMIT; + } else if (trieWord >= TYPE_THRESHOLD) { + values.type = (trieWord - TYPE_THRESHOLD); + } else { + /* get the type */ + values.type = MAP; + /* ascertain if the value is index or delta */ + if ((trieWord & 0x02) > 0) { + values.isIndex = true; + values.value = trieWord >> 2; // mask off the lower 2 bits and shift + + } else { + values.isIndex = false; + values.value = (trieWord << 16) >> 16; + values.value = (values.value >> 2); + + } + + if ((trieWord >> 2) == MAX_INDEX_VALUE) { + values.type = DELETE; + values.isIndex = false; + values.value = 0; + } + } + } + + private StringBuffer map(UCharacterIterator iter, int options) throws ParseException { + + Values val = new Values(); + char result = 0; + int ch = UCharacterIterator.DONE; + StringBuffer dest = new StringBuffer(); + boolean allowUnassigned = ((options & ALLOW_UNASSIGNED) > 0); + + while ((ch = iter.nextCodePoint()) != UCharacterIterator.DONE) { + + result = getCodePointValue(ch); + getValues(result, val); + + // check if the source codepoint is unassigned + if (val.type == UNASSIGNED && allowUnassigned == false) { + throw new ParseException("An unassigned code point was found in the input " + iter.getText(), + iter.getIndex()); + } else if ((val.type == MAP)) { + int index, length; + + if (val.isIndex) { + index = val.value; + if (index >= indexes[ONE_UCHAR_MAPPING_INDEX_START] + && index < indexes[TWO_UCHARS_MAPPING_INDEX_START]) { + length = 1; + } else if (index >= indexes[TWO_UCHARS_MAPPING_INDEX_START] + && index < indexes[THREE_UCHARS_MAPPING_INDEX_START]) { + length = 2; + } else if (index >= indexes[THREE_UCHARS_MAPPING_INDEX_START] + && index < indexes[FOUR_UCHARS_MAPPING_INDEX_START]) { + length = 3; + } else { + length = mappingData[index++]; + } + /* copy mapping to destination */ + dest.append(mappingData, index, length); + continue; + + } else { + ch -= val.value; + } + } else if (val.type == DELETE) { + // just consume the codepoint and contine + continue; + } + // copy the source into destination + UTF16.append(dest, ch); + } + + return dest; + } + + private StringBuffer normalize(StringBuffer src) { + /* + * Option UNORM_BEFORE_PRI_29: + * + * IDNA as interpreted by IETF members (see unicode mailing list 2004H1) + * requires strict adherence to Unicode 3.2 normalization, including buggy + * composition from before fixing Public Review Issue #29. Note that this + * results in some valid but nonsensical text to be either corrupted or + * rejected, depending on the text. See + * http://www.unicode.org/review/resolved-pri.html#pri29 See unorm.cpp and + * cnormtst.c + */ + return new StringBuffer( + SunNormalizer.normalize(src.toString(), Normalizer.Form.NFKC, SunNormalizer.UNICODE_3_2)); + } + + /* + * boolean isLabelSeparator(int ch){ int result = getCodePointValue(ch); if( + * (result & 0x07) == LABEL_SEPARATOR){ return true; } return false; } + */ + /* + * 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. + */ + /** + * Prepare the input buffer for use in applications with the given profile. This + * operation maps, normalizes(NFKC), checks for prohited and BiDi characters in + * the order defined by RFC 3454 depending on the options specified in the + * profile. + * + * @param src A UCharacterIterator object containing the source string + * @param options A bit set of options: + * + * - StringPrep.NONE Prohibit processing of unassigned code + * points in the input + * + * - StringPrep.ALLOW_UNASSIGNED Treat the unassigned code points + * are in the input as normal Unicode code points. + * + * @return StringBuffer A StringBuffer containing the output + * @throws ParseException + * @draft ICU 2.8 + */ + public StringBuffer prepare(UCharacterIterator src, int options) throws ParseException { + + // map + StringBuffer mapOut = map(src, options); + StringBuffer normOut = mapOut;// initialize + + if (doNFKC) { + // normalize + normOut = normalize(mapOut); + } + + int ch; + char result; + UCharacterIterator iter = UCharacterIterator.getInstance(normOut); + Values val = new Values(); + int direction = UCharacterDirection.CHAR_DIRECTION_COUNT, + firstCharDir = UCharacterDirection.CHAR_DIRECTION_COUNT; + int rtlPos = -1, ltrPos = -1; + boolean rightToLeft = false, leftToRight = false; + + while ((ch = iter.nextCodePoint()) != UCharacterIterator.DONE) { + result = getCodePointValue(ch); + getValues(result, val); + + if (val.type == PROHIBITED) { + throw new ParseException("A prohibited code point was found in the input" + iter.getText(), val.value); + } + + direction = UCharacter.getDirection(ch); + if (firstCharDir == UCharacterDirection.CHAR_DIRECTION_COUNT) { + firstCharDir = direction; + } + if (direction == UCharacterDirection.LEFT_TO_RIGHT) { + leftToRight = true; + ltrPos = iter.getIndex() - 1; + } + if (direction == UCharacterDirection.RIGHT_TO_LEFT + || direction == UCharacterDirection.RIGHT_TO_LEFT_ARABIC) { + rightToLeft = true; + rtlPos = iter.getIndex() - 1; + } + } + if (checkBiDi == true) { + // satisfy 2 + if (leftToRight == true && rightToLeft == true) { + throw new ParseException( + "The input does not conform to the rules for BiDi code points." + iter.getText(), + (rtlPos > ltrPos) ? rtlPos : ltrPos); + } + + // satisfy 3 + if (rightToLeft == true && !((firstCharDir == UCharacterDirection.RIGHT_TO_LEFT + || firstCharDir == UCharacterDirection.RIGHT_TO_LEFT_ARABIC) + && (direction == UCharacterDirection.RIGHT_TO_LEFT + || direction == UCharacterDirection.RIGHT_TO_LEFT_ARABIC))) { + throw new ParseException( + "The input does not conform to the rules for BiDi code points." + iter.getText(), + (rtlPos > ltrPos) ? rtlPos : ltrPos); + } + } + return normOut; + + } +} diff --git a/src/main/java/jdk_internal/icu/text/UCharacterIterator.java b/src/main/java/jdk_internal/icu/text/UCharacterIterator.java index 55031d63..d8868af9 100755 --- a/src/main/java/jdk_internal/icu/text/UCharacterIterator.java +++ b/src/main/java/jdk_internal/icu/text/UCharacterIterator.java @@ -1,326 +1,326 @@ -/* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ******************************************************************************* - * Copyright (C) 1996-2014, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - */ - -package jdk_internal.icu.text; - -import jdk_internal.bidi.CharacterIterator; -import jdk_internal.icu.impl.CharacterIteratorWrapper; -import jdk_internal.icu.impl.ReplaceableUCharacterIterator; -import jdk_internal.icu.impl.UCharacterProperty; - -/** - * 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. Forward iteration is done with post-increment and backward iteration - * is done with pre-decrement semantics, while the - * java.text.CharacterIterator interface methods provided forward - * iteration with "pre-increment" and backward iteration with pre-decrement - * semantics. This API is more efficient for forward iteration over code points. - * The other major difference is that this API can do both code unit and code - * point iteration, java.text.CharacterIterator can only iterate - * over code units and is limited to BMP (0 - 0xFFFF) - * - * @author Ram - * @stable ICU 2.4 - */ -public abstract class UCharacterIterator implements Cloneable { - - /** - * Protected default constructor for the subclasses - * - * @stable ICU 2.4 - */ - protected UCharacterIterator() { - } - - /** - * Indicator that we have reached the ends of the UTF16 text. Moved from - * UForwardCharacterIterator.java - * - * @stable ICU 2.4 - */ - public static final int DONE = -1; - - // static final methods ---------------------------------------------------- - - /** - * Returns a UCharacterIterator object given a source string. - * - * @param source a string - * @return UCharacterIterator object - * @exception IllegalArgumentException if the argument is null - * @stable ICU 2.4 - */ - public static final UCharacterIterator getInstance(String source) { - return new ReplaceableUCharacterIterator(source); - } - - /** - * Returns a UCharacterIterator object given a source StringBuffer. - * - * @param source an string buffer of UTF-16 code units - * @return UCharacterIterator object - * @exception IllegalArgumentException if the argument is null - * @stable ICU 2.4 - */ - public static final UCharacterIterator getInstance(StringBuffer source) { - return new ReplaceableUCharacterIterator(source); - } - - /** - * Returns a UCharacterIterator object given a CharacterIterator. - * - * @param source a valid CharacterIterator object. - * @return UCharacterIterator object - * @exception IllegalArgumentException if the argument is null - * @stable ICU 2.4 - */ - public static final UCharacterIterator getInstance(CharacterIterator source) { - return new CharacterIteratorWrapper(source); - } - - // public methods ---------------------------------------------------------- - - /** - * Returns the length of the text - * - * @return length of the text - * @stable ICU 2.4 - */ - public abstract int getLength(); - - /** - * Gets the current index in text. - * - * @return current index in text. - * @stable ICU 2.4 - */ - public abstract int getIndex(); - - /** - * Returns the UTF16 code unit at index, and increments to the next code unit - * (post-increment semantics). If index is out of range, DONE is returned, and - * the iterator is reset to the limit of the text. - * - * @return the next UTF16 code unit, or DONE if the index is at the limit of the - * text. - * @stable ICU 2.4 - */ - public abstract int next(); - - /** - * Returns the code point at index, and increments to the next code point - * (post-increment semantics). If index does not point to a valid surrogate - * pair, the behavior is the same as next(). Otherwise the iterator - * is incremented past the surrogate pair, and the code point represented by the - * pair is returned. - * - * @return the next codepoint in text, or DONE if the index is at the limit of - * the text. - * @stable ICU 2.4 - */ - public int nextCodePoint() { - int ch1 = next(); - if (UTF16.isLeadSurrogate((char) ch1)) { - int ch2 = next(); - if (UTF16.isTrailSurrogate((char) ch2)) { - return UCharacterProperty.getRawSupplementary((char) ch1, (char) ch2); - } else if (ch2 != DONE) { - // unmatched surrogate so back out - previous(); - } - } - return ch1; - } - - /** - * Decrement to the position of the previous code unit in the text, and return - * it (pre-decrement semantics). If the resulting index is less than 0, the - * index is reset to 0 and DONE is returned. - * - * @return the previous code unit in the text, or DONE if the new index is - * before the start of the text. - * @stable ICU 2.4 - */ - public abstract int previous(); - - /** - * Retreat to the start of the previous code point in the text, and return it - * (pre-decrement semantics). If the index is not preceeded by a valid surrogate - * pair, the behavior is the same as previous(). Otherwise the - * iterator is decremented to the start of the surrogate pair, and the code - * point represented by the pair is returned. - * - * @return the previous code point in the text, or DONE if the new index is - * before the start of the text. - * @stable ICU 2.4 - */ - public int previousCodePoint() { - int ch1 = previous(); - if (UTF16.isTrailSurrogate((char) ch1)) { - int ch2 = previous(); - if (UTF16.isLeadSurrogate((char) ch2)) { - return UCharacterProperty.getRawSupplementary((char) ch2, (char) ch1); - } else if (ch2 != DONE) { - // unmatched trail surrogate so back out - next(); - } - } - return ch1; - } - - /** - * Sets the index to the specified index in the text. - * - * @param index the index within the text. - * @exception IndexOutOfBoundsException is thrown if an invalid index is - * supplied - * @stable ICU 2.4 - */ - public abstract void setIndex(int index); - - /** - * Sets the current index to the start. - * - * @stable ICU 2.4 - */ - public void setToStart() { - setIndex(0); - } - - /** - * Fills the buffer with the underlying text storage of the iterator If the - * buffer capacity is not enough a exception is thrown. The capacity of the fill - * in buffer should at least be equal to length of text in the iterator obtained - * by calling getLength(). Usage: - * - *
{@code
-	 *         UChacterIterator iter = new UCharacterIterator.getInstance(text);
-	 *         char[] buf = new char[iter.getLength()];
-	 *         iter.getText(buf);
-	 *
-	 *         OR
-	 *         char[] buf= new char[1];
-	 *         int len = 0;
-	 *         for(;;){
-	 *             try{
-	 *                 len = iter.getText(buf);
-	 *                 break;
-	 *             }catch(IndexOutOfBoundsException e){
-	 *                 buf = new char[iter.getLength()];
-	 *             }
-	 *         }
-	 * }
- * - * @param fillIn an array of chars to fill with the underlying UTF-16 code - * units. - * @param offset the position within the array to start putting the data. - * @return the number of code units added to fillIn, as a convenience - * @exception IndexOutOfBoundsException exception if there is not enough room - * after offset in the array, or if offset - * < 0. - * @stable ICU 2.4 - */ - public abstract int getText(char[] fillIn, int offset); - - /** - * Convenience override for getText(char[], int) that provides an - * offset of 0. - * - * @param fillIn an array of chars to fill with the underlying UTF-16 code - * units. - * @return the number of code units added to fillIn, as a convenience - * @exception IndexOutOfBoundsException exception if there is not enough room in - * the array. - * @stable ICU 2.4 - */ - public final int getText(char[] fillIn) { - return getText(fillIn, 0); - } - - /** - * Convenience method for returning the underlying text storage as a string - * - * @return the underlying text storage in the iterator as a string - * @stable ICU 2.4 - */ - public String getText() { - char[] text = new char[getLength()]; - getText(text); - return new String(text); - } - - /** - * Moves the current position by the number of code points specified, either - * forward or backward depending on the sign of delta (positive or negative - * respectively). If the current index is at a trail surrogate then the first - * adjustment is by code unit, and the remaining adjustments are by code points. - * If the resulting index would be less than zero, the index is set to zero, and - * if the resulting index would be greater than limit, the index is set to - * limit. - * - * @param delta the number of code units to move the current index. - * @return the new index - * @exception IndexOutOfBoundsException is thrown if an invalid delta is - * supplied - * @stable ICU 2.4 - * - */ - public int moveCodePointIndex(int delta) { - if (delta > 0) { - while (delta > 0 && nextCodePoint() != DONE) { - delta--; - } - } else { - while (delta < 0 && previousCodePoint() != DONE) { - delta++; - } - } - if (delta != 0) { - throw new IndexOutOfBoundsException(); - } - - return getIndex(); - } - - /** - * Creates a copy of this iterator, independent from other iterators. If it is - * not possible to clone the iterator, returns null. - * - * @return copy of this iterator - * @stable ICU 2.4 - */ - public Object clone() throws CloneNotSupportedException { - return super.clone(); - } - -} +/* + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ******************************************************************************* + * Copyright (C) 1996-2014, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + */ + +package jdk_internal.icu.text; + +import jdk_internal.bidi.CharacterIterator; +import jdk_internal.icu.impl.CharacterIteratorWrapper; +import jdk_internal.icu.impl.ReplaceableUCharacterIterator; +import jdk_internal.icu.impl.UCharacterProperty; + +/** + * 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. Forward iteration is done with post-increment and backward iteration + * is done with pre-decrement semantics, while the + * java.text.CharacterIterator interface methods provided forward + * iteration with "pre-increment" and backward iteration with pre-decrement + * semantics. This API is more efficient for forward iteration over code points. + * The other major difference is that this API can do both code unit and code + * point iteration, java.text.CharacterIterator can only iterate + * over code units and is limited to BMP (0 - 0xFFFF) + * + * @author Ram + * @stable ICU 2.4 + */ +public abstract class UCharacterIterator implements Cloneable { + + /** + * Protected default constructor for the subclasses + * + * @stable ICU 2.4 + */ + protected UCharacterIterator() { + } + + /** + * Indicator that we have reached the ends of the UTF16 text. Moved from + * UForwardCharacterIterator.java + * + * @stable ICU 2.4 + */ + public static final int DONE = -1; + + // static final methods ---------------------------------------------------- + + /** + * Returns a UCharacterIterator object given a source string. + * + * @param source a string + * @return UCharacterIterator object + * @exception IllegalArgumentException if the argument is null + * @stable ICU 2.4 + */ + public static final UCharacterIterator getInstance(String source) { + return new ReplaceableUCharacterIterator(source); + } + + /** + * Returns a UCharacterIterator object given a source StringBuffer. + * + * @param source an string buffer of UTF-16 code units + * @return UCharacterIterator object + * @exception IllegalArgumentException if the argument is null + * @stable ICU 2.4 + */ + public static final UCharacterIterator getInstance(StringBuffer source) { + return new ReplaceableUCharacterIterator(source); + } + + /** + * Returns a UCharacterIterator object given a CharacterIterator. + * + * @param source a valid CharacterIterator object. + * @return UCharacterIterator object + * @exception IllegalArgumentException if the argument is null + * @stable ICU 2.4 + */ + public static final UCharacterIterator getInstance(CharacterIterator source) { + return new CharacterIteratorWrapper(source); + } + + // public methods ---------------------------------------------------------- + + /** + * Returns the length of the text + * + * @return length of the text + * @stable ICU 2.4 + */ + public abstract int getLength(); + + /** + * Gets the current index in text. + * + * @return current index in text. + * @stable ICU 2.4 + */ + public abstract int getIndex(); + + /** + * Returns the UTF16 code unit at index, and increments to the next code unit + * (post-increment semantics). If index is out of range, DONE is returned, and + * the iterator is reset to the limit of the text. + * + * @return the next UTF16 code unit, or DONE if the index is at the limit of the + * text. + * @stable ICU 2.4 + */ + public abstract int next(); + + /** + * Returns the code point at index, and increments to the next code point + * (post-increment semantics). If index does not point to a valid surrogate + * pair, the behavior is the same as next(). Otherwise the iterator + * is incremented past the surrogate pair, and the code point represented by the + * pair is returned. + * + * @return the next codepoint in text, or DONE if the index is at the limit of + * the text. + * @stable ICU 2.4 + */ + public int nextCodePoint() { + int ch1 = next(); + if (UTF16.isLeadSurrogate((char) ch1)) { + int ch2 = next(); + if (UTF16.isTrailSurrogate((char) ch2)) { + return UCharacterProperty.getRawSupplementary((char) ch1, (char) ch2); + } else if (ch2 != DONE) { + // unmatched surrogate so back out + previous(); + } + } + return ch1; + } + + /** + * Decrement to the position of the previous code unit in the text, and return + * it (pre-decrement semantics). If the resulting index is less than 0, the + * index is reset to 0 and DONE is returned. + * + * @return the previous code unit in the text, or DONE if the new index is + * before the start of the text. + * @stable ICU 2.4 + */ + public abstract int previous(); + + /** + * Retreat to the start of the previous code point in the text, and return it + * (pre-decrement semantics). If the index is not preceeded by a valid surrogate + * pair, the behavior is the same as previous(). Otherwise the + * iterator is decremented to the start of the surrogate pair, and the code + * point represented by the pair is returned. + * + * @return the previous code point in the text, or DONE if the new index is + * before the start of the text. + * @stable ICU 2.4 + */ + public int previousCodePoint() { + int ch1 = previous(); + if (UTF16.isTrailSurrogate((char) ch1)) { + int ch2 = previous(); + if (UTF16.isLeadSurrogate((char) ch2)) { + return UCharacterProperty.getRawSupplementary((char) ch2, (char) ch1); + } else if (ch2 != DONE) { + // unmatched trail surrogate so back out + next(); + } + } + return ch1; + } + + /** + * Sets the index to the specified index in the text. + * + * @param index the index within the text. + * @exception IndexOutOfBoundsException is thrown if an invalid index is + * supplied + * @stable ICU 2.4 + */ + public abstract void setIndex(int index); + + /** + * Sets the current index to the start. + * + * @stable ICU 2.4 + */ + public void setToStart() { + setIndex(0); + } + + /** + * Fills the buffer with the underlying text storage of the iterator If the + * buffer capacity is not enough a exception is thrown. The capacity of the fill + * in buffer should at least be equal to length of text in the iterator obtained + * by calling getLength(). Usage: + * + *
{@code
+	 *         UChacterIterator iter = new UCharacterIterator.getInstance(text);
+	 *         char[] buf = new char[iter.getLength()];
+	 *         iter.getText(buf);
+	 *
+	 *         OR
+	 *         char[] buf= new char[1];
+	 *         int len = 0;
+	 *         for(;;){
+	 *             try{
+	 *                 len = iter.getText(buf);
+	 *                 break;
+	 *             }catch(IndexOutOfBoundsException e){
+	 *                 buf = new char[iter.getLength()];
+	 *             }
+	 *         }
+	 * }
+ * + * @param fillIn an array of chars to fill with the underlying UTF-16 code + * units. + * @param offset the position within the array to start putting the data. + * @return the number of code units added to fillIn, as a convenience + * @exception IndexOutOfBoundsException exception if there is not enough room + * after offset in the array, or if offset + * < 0. + * @stable ICU 2.4 + */ + public abstract int getText(char[] fillIn, int offset); + + /** + * Convenience override for getText(char[], int) that provides an + * offset of 0. + * + * @param fillIn an array of chars to fill with the underlying UTF-16 code + * units. + * @return the number of code units added to fillIn, as a convenience + * @exception IndexOutOfBoundsException exception if there is not enough room in + * the array. + * @stable ICU 2.4 + */ + public final int getText(char[] fillIn) { + return getText(fillIn, 0); + } + + /** + * Convenience method for returning the underlying text storage as a string + * + * @return the underlying text storage in the iterator as a string + * @stable ICU 2.4 + */ + public String getText() { + char[] text = new char[getLength()]; + getText(text); + return new String(text); + } + + /** + * Moves the current position by the number of code points specified, either + * forward or backward depending on the sign of delta (positive or negative + * respectively). If the current index is at a trail surrogate then the first + * adjustment is by code unit, and the remaining adjustments are by code points. + * If the resulting index would be less than zero, the index is set to zero, and + * if the resulting index would be greater than limit, the index is set to + * limit. + * + * @param delta the number of code units to move the current index. + * @return the new index + * @exception IndexOutOfBoundsException is thrown if an invalid delta is + * supplied + * @stable ICU 2.4 + * + */ + public int moveCodePointIndex(int delta) { + if (delta > 0) { + while (delta > 0 && nextCodePoint() != DONE) { + delta--; + } + } else { + while (delta < 0 && previousCodePoint() != DONE) { + delta++; + } + } + if (delta != 0) { + throw new IndexOutOfBoundsException(); + } + + return getIndex(); + } + + /** + * Creates a copy of this iterator, independent from other iterators. If it is + * not possible to clone the iterator, returns null. + * + * @return copy of this iterator + * @stable ICU 2.4 + */ + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/src/main/java/jdk_internal/icu/text/UTF16.java b/src/main/java/jdk_internal/icu/text/UTF16.java index 3efde5af..2ba92b6a 100755 --- a/src/main/java/jdk_internal/icu/text/UTF16.java +++ b/src/main/java/jdk_internal/icu/text/UTF16.java @@ -1,609 +1,609 @@ -/* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -/** - ******************************************************************************* - * Copyright (C) 1996-2014, International Business Machines Corporation and - * others. All Rights Reserved. - ******************************************************************************* - */ - -package jdk_internal.icu.text; - -import jdk_internal.icu.impl.UCharacterProperty; - -/** - *

- * Standalone utility class providing UTF16 character conversions and indexing - * conversions. - *

- * Code that uses strings alone rarely need modification. By design, UTF-16 does - * not allow overlap, so searching for strings is a safe operation. Similarly, - * concatenation is always safe. Substringing is safe if the start and end are - * both on UTF-32 boundaries. In normal code, the values for start and end are - * on those boundaries, since they arose from operations like searching. If not, - * the nearest UTF-32 boundaries can be determined using bounds(). - * Examples: - *

- * The following examples illustrate use of some of these methods. - * - *

{@code
- * // iteration forwards: Original
- * for (int i = 0; i < s.length(); ++i) {
- * 	char ch = s.charAt(i);
- * 	doSomethingWith(ch);
- * }
- *
- * // iteration forwards: Changes for UTF-32
- * int ch;
- * for (int i = 0; i < s.length(); i += UTF16.getCharCount(ch)) {
- * 	ch = UTF16.charAt(s, i);
- * 	doSomethingWith(ch);
- * }
- *
- * // iteration backwards: Original
- * for (int i = s.length() - 1; i >= 0; --i) {
- * 	char ch = s.charAt(i);
- * 	doSomethingWith(ch);
- * }
- *
- * // iteration backwards: Changes for UTF-32
- * int ch;
- * for (int i = s.length() - 1; i > 0; i -= UTF16.getCharCount(ch)) {
- * 	ch = UTF16.charAt(s, i);
- * 	doSomethingWith(ch);
- * }
- * }
- * - * Notes: - *
    - *
  • Naming: For clarity, High and Low surrogates are called - * Lead and Trail in the API, which gives a better - * sense of their ordering in a string. offset16 and - * offset32 are used to distinguish offsets to UTF-16 boundaries vs - * offsets to UTF-32 boundaries. int char32 is used to contain - * UTF-32 characters, as opposed to char16, which is a UTF-16 code - * unit.
  • - *
  • Roundtripping Offsets: You can always roundtrip from a - * UTF-32 offset to a UTF-16 offset and back. Because of the difference in - * structure, you can roundtrip from a UTF-16 offset to a UTF-32 offset and back - * if and only if bounds(string, offset16) != TRAIL.
  • - *
  • Exceptions: The error checking will throw an exception - * if indices are out of bounds. Other than that, all methods will behave - * reasonably, even if unmatched surrogates or out-of-bounds UTF-32 values are - * present. UCharacter.isLegal() can be used to check for validity - * if desired.
  • - *
  • Unmatched Surrogates: If the string contains unmatched - * surrogates, then these are counted as one UTF-32 value. This matches their - * iteration behavior, which is vital. It also matches common display practice - * as missing glyphs (see the Unicode Standard Section 5.4, 5.5).
  • - *
  • Optimization: The method implementations may need - * optimization if the compiler doesn't fold static final methods. Since - * surrogate pairs will form an exceeding small percentage of all the text in - * the world, the singleton case should always be optimized for.
  • - *
- * - * @author Mark Davis, with help from Markus Scherer - * @stable ICU 2.1 - */ - -public final class UTF16 { - // public variables --------------------------------------------------- - - /** - * The lowest Unicode code point value. - * - * @stable ICU 2.1 - */ - public static final int CODEPOINT_MIN_VALUE = 0; - /** - * The highest Unicode code point value (scalar value) according to the Unicode - * Standard. - * - * @stable ICU 2.1 - */ - public static final int CODEPOINT_MAX_VALUE = 0x10ffff; - /** - * The minimum value for Supplementary code points - * - * @stable ICU 2.1 - */ - public static final int SUPPLEMENTARY_MIN_VALUE = 0x10000; - /** - * Lead surrogate minimum value - * - * @stable ICU 2.1 - */ - public static final int LEAD_SURROGATE_MIN_VALUE = 0xD800; - /** - * Trail surrogate minimum value - * - * @stable ICU 2.1 - */ - public static final int TRAIL_SURROGATE_MIN_VALUE = 0xDC00; - /** - * Lead surrogate maximum value - * - * @stable ICU 2.1 - */ - public static final int LEAD_SURROGATE_MAX_VALUE = 0xDBFF; - /** - * Trail surrogate maximum value - * - * @stable ICU 2.1 - */ - public static final int TRAIL_SURROGATE_MAX_VALUE = 0xDFFF; - /** - * Surrogate minimum value - * - * @stable ICU 2.1 - */ - public static final int SURROGATE_MIN_VALUE = LEAD_SURROGATE_MIN_VALUE; - /** - * Lead surrogate bitmask - */ - private static final int LEAD_SURROGATE_BITMASK = 0xFFFFFC00; - /** - * Trail surrogate bitmask - */ - private static final int TRAIL_SURROGATE_BITMASK = 0xFFFFFC00; - /** - * Surrogate bitmask - */ - private static final int SURROGATE_BITMASK = 0xFFFFF800; - /** - * Lead surrogate bits - */ - private static final int LEAD_SURROGATE_BITS = 0xD800; - /** - * Trail surrogate bits - */ - private static final int TRAIL_SURROGATE_BITS = 0xDC00; - /** - * Surrogate bits - */ - private static final int SURROGATE_BITS = 0xD800; - - // constructor -------------------------------------------------------- - - // /CLOVER:OFF - /** - * Prevent instance from being created. - */ - private UTF16() { - } - - // /CLOVER:ON - // public method ------------------------------------------------------ - - /** - * Extract a single UTF-32 value from a string. Used when iterating forwards or - * backwards (with UTF16.getCharCount(), as well as random access. - * If a validity check is required, use - * - * UCharacter.isLegal() on the return value. If the char retrieved is - * part of a surrogate pair, its supplementary character will be returned. If a - * complete supplementary character is not found the incomplete character will - * be returned - * - * @param source array of UTF-16 chars - * @param offset16 UTF-16 offset to the start of the character. - * @return UTF-32 value for the UTF-32 value that contains the char at offset16. - * The boundaries of that codepoint are the same as in - * bounds32(). - * @exception IndexOutOfBoundsException thrown if offset16 is out of bounds. - * @stable ICU 2.1 - */ - public static int charAt(String source, int offset16) { - char single = source.charAt(offset16); - if (single < LEAD_SURROGATE_MIN_VALUE) { - return single; - } - return _charAt(source, offset16, single); - } - - private static int _charAt(String source, int offset16, char single) { - if (single > TRAIL_SURROGATE_MAX_VALUE) { - return single; - } - - // Convert the UTF-16 surrogate pair if necessary. - // For simplicity in usage, and because the frequency of pairs is - // low, look both directions. - - if (single <= LEAD_SURROGATE_MAX_VALUE) { - ++offset16; - if (source.length() != offset16) { - char trail = source.charAt(offset16); - if (trail >= TRAIL_SURROGATE_MIN_VALUE && trail <= TRAIL_SURROGATE_MAX_VALUE) { - return UCharacterProperty.getRawSupplementary(single, trail); - } - } - } else { - --offset16; - if (offset16 >= 0) { - // single is a trail surrogate so - char lead = source.charAt(offset16); - if (lead >= LEAD_SURROGATE_MIN_VALUE && lead <= LEAD_SURROGATE_MAX_VALUE) { - return UCharacterProperty.getRawSupplementary(lead, single); - } - } - } - return single; // return unmatched surrogate - } - - /** - * Extract a single UTF-32 value from a string. Used when iterating forwards or - * backwards (with UTF16.getCharCount(), as well as random access. - * If a validity check is required, use - * UCharacter.isLegal() - * on the return value. If the char retrieved is part of a surrogate - * pair, its supplementary character will be returned. If a complete - * supplementary character is not found the incomplete character will be - * returned - * - * @param source array of UTF-16 chars - * @param offset16 UTF-16 offset to the start of the character. - * @return UTF-32 value for the UTF-32 value that contains the char at offset16. - * The boundaries of that codepoint are the same as in - * bounds32(). - * @exception IndexOutOfBoundsException thrown if offset16 is out of bounds. - * @stable ICU 2.1 - */ - public static int charAt(CharSequence source, int offset16) { - char single = source.charAt(offset16); - if (single < UTF16.LEAD_SURROGATE_MIN_VALUE) { - return single; - } - return _charAt(source, offset16, single); - } - - private static int _charAt(CharSequence source, int offset16, char single) { - if (single > UTF16.TRAIL_SURROGATE_MAX_VALUE) { - return single; - } - - // Convert the UTF-16 surrogate pair if necessary. - // For simplicity in usage, and because the frequency of pairs is - // low, look both directions. - - if (single <= UTF16.LEAD_SURROGATE_MAX_VALUE) { - ++offset16; - if (source.length() != offset16) { - char trail = source.charAt(offset16); - if (trail >= UTF16.TRAIL_SURROGATE_MIN_VALUE && trail <= UTF16.TRAIL_SURROGATE_MAX_VALUE) { - return UCharacterProperty.getRawSupplementary(single, trail); - } - } - } else { - --offset16; - if (offset16 >= 0) { - // single is a trail surrogate so - char lead = source.charAt(offset16); - if (lead >= UTF16.LEAD_SURROGATE_MIN_VALUE && lead <= UTF16.LEAD_SURROGATE_MAX_VALUE) { - return UCharacterProperty.getRawSupplementary(lead, single); - } - } - } - return single; // return unmatched surrogate - } - - /** - * Extract a single UTF-32 value from a substring. Used when iterating forwards - * or backwards (with UTF16.getCharCount(), as well as random - * access. If a validity check is required, use - * UCharacter.isLegal() - * on the return value. If the char retrieved is part of a surrogate - * pair, its supplementary character will be returned. If a complete - * supplementary character is not found the incomplete character will be - * returned - * - * @param source Array of UTF-16 chars - * @param start Offset to substring in the source array for analyzing - * @param limit Offset to substring in the source array for analyzing - * @param offset16 UTF-16 offset relative to start - * @return UTF-32 value for the UTF-32 value that contains the char at offset16. - * The boundaries of that codepoint are the same as in - * bounds32(). - * @exception IndexOutOfBoundsException Thrown if offset16 is not within the - * range of start and limit. - * @stable ICU 2.1 - */ - public static int charAt(char source[], int start, int limit, int offset16) { - offset16 += start; - if (offset16 < start || offset16 >= limit) { - throw new ArrayIndexOutOfBoundsException(offset16); - } - - char single = source[offset16]; - if (!isSurrogate(single)) { - return single; - } - - // Convert the UTF-16 surrogate pair if necessary. - // For simplicity in usage, and because the frequency of pairs is - // low, look both directions. - if (single <= LEAD_SURROGATE_MAX_VALUE) { - offset16++; - if (offset16 >= limit) { - return single; - } - char trail = source[offset16]; - if (isTrailSurrogate(trail)) { - return UCharacterProperty.getRawSupplementary(single, trail); - } - } else { // isTrailSurrogate(single), so - if (offset16 == start) { - return single; - } - offset16--; - char lead = source[offset16]; - if (isLeadSurrogate(lead)) - return UCharacterProperty.getRawSupplementary(lead, single); - } - return single; // return unmatched surrogate - } - - /** - * Determines how many chars this char32 requires. If a validity check is - * required, use - * isLegal() on - * char32 before calling. - * - * @param char32 the input codepoint. - * @return 2 if is in supplementary space, otherwise 1. - * @stable ICU 2.1 - */ - public static int getCharCount(int char32) { - if (char32 < SUPPLEMENTARY_MIN_VALUE) { - return 1; - } - return 2; - } - - /** - * Determines whether the code value is a surrogate. - * - * @param char16 the input character. - * @return true if the input character is a surrogate. - * @stable ICU 2.1 - */ - public static boolean isSurrogate(char char16) { - return (char16 & SURROGATE_BITMASK) == SURROGATE_BITS; - } - - /** - * Determines whether the character is a trail surrogate. - * - * @param char16 the input character. - * @return true if the input character is a trail surrogate. - * @stable ICU 2.1 - */ - public static boolean isTrailSurrogate(char char16) { - return (char16 & TRAIL_SURROGATE_BITMASK) == TRAIL_SURROGATE_BITS; - } - - /** - * Determines whether the character is a lead surrogate. - * - * @param char16 the input character. - * @return true if the input character is a lead surrogate - * @stable ICU 2.1 - */ - public static boolean isLeadSurrogate(char char16) { - return (char16 & LEAD_SURROGATE_BITMASK) == LEAD_SURROGATE_BITS; - } - - /** - * Returns the lead surrogate. If a validity check is required, use - * isLegal() on - * char32 before calling. - * - * @param char32 the input character. - * @return lead surrogate if the getCharCount(ch) is 2;
- * and 0 otherwise (note: 0 is not a valid lead surrogate). - * @stable ICU 2.1 - */ - public static char getLeadSurrogate(int char32) { - if (char32 >= SUPPLEMENTARY_MIN_VALUE) { - return (char) (LEAD_SURROGATE_OFFSET_ + (char32 >> LEAD_SURROGATE_SHIFT_)); - } - - return 0; - } - - /** - * Returns the trail surrogate. If a validity check is required, use - * isLegal() on - * char32 before calling. - * - * @param char32 the input character. - * @return the trail surrogate if the getCharCount(ch) is 2;
- * otherwise the character itself - * @stable ICU 2.1 - */ - public static char getTrailSurrogate(int char32) { - if (char32 >= SUPPLEMENTARY_MIN_VALUE) { - return (char) (TRAIL_SURROGATE_MIN_VALUE + (char32 & TRAIL_SURROGATE_MASK_)); - } - - return (char) char32; - } - - /** - * Convenience method corresponding to String.valueOf(char). Returns a one or - * two char string containing the UTF-32 value in UTF16 format. If a validity - * check is required, use - * isLegal() on - * char32 before calling. - * - * @param char32 the input character. - * @return string value of char32 in UTF16 format - * @exception IllegalArgumentException thrown if char32 is a invalid codepoint. - * @stable ICU 2.1 - */ - public static String valueOf(int char32) { - if (char32 < CODEPOINT_MIN_VALUE || char32 > CODEPOINT_MAX_VALUE) { - throw new IllegalArgumentException("Illegal codepoint"); - } - return toString(char32); - } - - /** - * Append a single UTF-32 value to the end of a StringBuffer. If a validity - * check is required, use - * isLegal() on - * char32 before calling. - * - * @param target the buffer to append to - * @param char32 value to append. - * @return the updated StringBuffer - * @exception IllegalArgumentException thrown when char32 does not lie within - * the range of the Unicode codepoints - * @stable ICU 2.1 - */ - public static StringBuffer append(StringBuffer target, int char32) { - // Check for irregular values - if (char32 < CODEPOINT_MIN_VALUE || char32 > CODEPOINT_MAX_VALUE) { - throw new IllegalArgumentException("Illegal codepoint: " + Integer.toHexString(char32)); - } - - // Write the UTF-16 values - if (char32 >= SUPPLEMENTARY_MIN_VALUE) { - target.append(getLeadSurrogate(char32)); - target.append(getTrailSurrogate(char32)); - } else { - target.append((char) char32); - } - return target; - } - - /** - * Shifts offset16 by the argument number of codepoints within a subarray. - * - * @param source char array - * @param start position of the subarray to be performed on - * @param limit position of the subarray to be performed on - * @param offset16 UTF16 position to shift relative to start - * @param shift32 number of codepoints to shift - * @return new shifted offset16 relative to start - * @exception IndexOutOfBoundsException if the new offset16 is out of bounds - * with respect to the subarray or the - * subarray bounds are out of range. - * @stable ICU 2.1 - */ - public static int moveCodePointOffset(char source[], int start, int limit, int offset16, int shift32) { - int size = source.length; - int count; - char ch; - int result = offset16 + start; - if (start < 0 || limit < start) { - throw new StringIndexOutOfBoundsException(start); - } - if (limit > size) { - throw new StringIndexOutOfBoundsException(limit); - } - if (offset16 < 0 || result > limit) { - throw new StringIndexOutOfBoundsException(offset16); - } - if (shift32 > 0) { - if (shift32 + result > size) { - throw new StringIndexOutOfBoundsException(result); - } - count = shift32; - while (result < limit && count > 0) { - ch = source[result]; - if (isLeadSurrogate(ch) && (result + 1 < limit) && isTrailSurrogate(source[result + 1])) { - result++; - } - count--; - result++; - } - } else { - if (result + shift32 < start) { - throw new StringIndexOutOfBoundsException(result); - } - for (count = -shift32; count > 0; count--) { - result--; - if (result < start) { - break; - } - ch = source[result]; - if (isTrailSurrogate(ch) && result > start && isLeadSurrogate(source[result - 1])) { - result--; - } - } - } - if (count != 0) { - throw new StringIndexOutOfBoundsException(shift32); - } - result -= start; - return result; - } - - // private data members ------------------------------------------------- - - /** - * Shift value for lead surrogate to form a supplementary character. - */ - private static final int LEAD_SURROGATE_SHIFT_ = 10; - - /** - * Mask to retrieve the significant value from a trail surrogate. - */ - private static final int TRAIL_SURROGATE_MASK_ = 0x3FF; - - /** - * Value that all lead surrogate starts with - */ - private static final int LEAD_SURROGATE_OFFSET_ = LEAD_SURROGATE_MIN_VALUE - - (SUPPLEMENTARY_MIN_VALUE >> LEAD_SURROGATE_SHIFT_); - - // private methods ------------------------------------------------------ - - /** - *

- * Converts argument code point and returns a String object representing the - * code point's value in UTF16 format. - *

- * This method does not check for the validity of the codepoint, the results are - * not guaranteed if a invalid codepoint is passed as argument. - *

- * The result is a string whose length is 1 for non-supplementary code points, 2 - * otherwise. - * - * @param ch code point - * @return string representation of the code point - */ - private static String toString(int ch) { - if (ch < SUPPLEMENTARY_MIN_VALUE) { - return String.valueOf((char) ch); - } - - StringBuilder result = new StringBuilder(); - result.append(getLeadSurrogate(ch)); - result.append(getTrailSurrogate(ch)); - return result.toString(); - } -} +/* + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/** + ******************************************************************************* + * Copyright (C) 1996-2014, International Business Machines Corporation and + * others. All Rights Reserved. + ******************************************************************************* + */ + +package jdk_internal.icu.text; + +import jdk_internal.icu.impl.UCharacterProperty; + +/** + *

+ * Standalone utility class providing UTF16 character conversions and indexing + * conversions. + *

+ * Code that uses strings alone rarely need modification. By design, UTF-16 does + * not allow overlap, so searching for strings is a safe operation. Similarly, + * concatenation is always safe. Substringing is safe if the start and end are + * both on UTF-32 boundaries. In normal code, the values for start and end are + * on those boundaries, since they arose from operations like searching. If not, + * the nearest UTF-32 boundaries can be determined using bounds(). + * Examples: + *

+ * The following examples illustrate use of some of these methods. + * + *

{@code
+ * // iteration forwards: Original
+ * for (int i = 0; i < s.length(); ++i) {
+ * 	char ch = s.charAt(i);
+ * 	doSomethingWith(ch);
+ * }
+ *
+ * // iteration forwards: Changes for UTF-32
+ * int ch;
+ * for (int i = 0; i < s.length(); i += UTF16.getCharCount(ch)) {
+ * 	ch = UTF16.charAt(s, i);
+ * 	doSomethingWith(ch);
+ * }
+ *
+ * // iteration backwards: Original
+ * for (int i = s.length() - 1; i >= 0; --i) {
+ * 	char ch = s.charAt(i);
+ * 	doSomethingWith(ch);
+ * }
+ *
+ * // iteration backwards: Changes for UTF-32
+ * int ch;
+ * for (int i = s.length() - 1; i > 0; i -= UTF16.getCharCount(ch)) {
+ * 	ch = UTF16.charAt(s, i);
+ * 	doSomethingWith(ch);
+ * }
+ * }
+ * + * Notes: + *
    + *
  • Naming: For clarity, High and Low surrogates are called + * Lead and Trail in the API, which gives a better + * sense of their ordering in a string. offset16 and + * offset32 are used to distinguish offsets to UTF-16 boundaries vs + * offsets to UTF-32 boundaries. int char32 is used to contain + * UTF-32 characters, as opposed to char16, which is a UTF-16 code + * unit.
  • + *
  • Roundtripping Offsets: You can always roundtrip from a + * UTF-32 offset to a UTF-16 offset and back. Because of the difference in + * structure, you can roundtrip from a UTF-16 offset to a UTF-32 offset and back + * if and only if bounds(string, offset16) != TRAIL.
  • + *
  • Exceptions: The error checking will throw an exception + * if indices are out of bounds. Other than that, all methods will behave + * reasonably, even if unmatched surrogates or out-of-bounds UTF-32 values are + * present. UCharacter.isLegal() can be used to check for validity + * if desired.
  • + *
  • Unmatched Surrogates: If the string contains unmatched + * surrogates, then these are counted as one UTF-32 value. This matches their + * iteration behavior, which is vital. It also matches common display practice + * as missing glyphs (see the Unicode Standard Section 5.4, 5.5).
  • + *
  • Optimization: The method implementations may need + * optimization if the compiler doesn't fold static final methods. Since + * surrogate pairs will form an exceeding small percentage of all the text in + * the world, the singleton case should always be optimized for.
  • + *
+ * + * @author Mark Davis, with help from Markus Scherer + * @stable ICU 2.1 + */ + +public final class UTF16 { + // public variables --------------------------------------------------- + + /** + * The lowest Unicode code point value. + * + * @stable ICU 2.1 + */ + public static final int CODEPOINT_MIN_VALUE = 0; + /** + * The highest Unicode code point value (scalar value) according to the Unicode + * Standard. + * + * @stable ICU 2.1 + */ + public static final int CODEPOINT_MAX_VALUE = 0x10ffff; + /** + * The minimum value for Supplementary code points + * + * @stable ICU 2.1 + */ + public static final int SUPPLEMENTARY_MIN_VALUE = 0x10000; + /** + * Lead surrogate minimum value + * + * @stable ICU 2.1 + */ + public static final int LEAD_SURROGATE_MIN_VALUE = 0xD800; + /** + * Trail surrogate minimum value + * + * @stable ICU 2.1 + */ + public static final int TRAIL_SURROGATE_MIN_VALUE = 0xDC00; + /** + * Lead surrogate maximum value + * + * @stable ICU 2.1 + */ + public static final int LEAD_SURROGATE_MAX_VALUE = 0xDBFF; + /** + * Trail surrogate maximum value + * + * @stable ICU 2.1 + */ + public static final int TRAIL_SURROGATE_MAX_VALUE = 0xDFFF; + /** + * Surrogate minimum value + * + * @stable ICU 2.1 + */ + public static final int SURROGATE_MIN_VALUE = LEAD_SURROGATE_MIN_VALUE; + /** + * Lead surrogate bitmask + */ + private static final int LEAD_SURROGATE_BITMASK = 0xFFFFFC00; + /** + * Trail surrogate bitmask + */ + private static final int TRAIL_SURROGATE_BITMASK = 0xFFFFFC00; + /** + * Surrogate bitmask + */ + private static final int SURROGATE_BITMASK = 0xFFFFF800; + /** + * Lead surrogate bits + */ + private static final int LEAD_SURROGATE_BITS = 0xD800; + /** + * Trail surrogate bits + */ + private static final int TRAIL_SURROGATE_BITS = 0xDC00; + /** + * Surrogate bits + */ + private static final int SURROGATE_BITS = 0xD800; + + // constructor -------------------------------------------------------- + + // /CLOVER:OFF + /** + * Prevent instance from being created. + */ + private UTF16() { + } + + // /CLOVER:ON + // public method ------------------------------------------------------ + + /** + * Extract a single UTF-32 value from a string. Used when iterating forwards or + * backwards (with UTF16.getCharCount(), as well as random access. + * If a validity check is required, use + * + * UCharacter.isLegal() on the return value. If the char retrieved is + * part of a surrogate pair, its supplementary character will be returned. If a + * complete supplementary character is not found the incomplete character will + * be returned + * + * @param source array of UTF-16 chars + * @param offset16 UTF-16 offset to the start of the character. + * @return UTF-32 value for the UTF-32 value that contains the char at offset16. + * The boundaries of that codepoint are the same as in + * bounds32(). + * @exception IndexOutOfBoundsException thrown if offset16 is out of bounds. + * @stable ICU 2.1 + */ + public static int charAt(String source, int offset16) { + char single = source.charAt(offset16); + if (single < LEAD_SURROGATE_MIN_VALUE) { + return single; + } + return _charAt(source, offset16, single); + } + + private static int _charAt(String source, int offset16, char single) { + if (single > TRAIL_SURROGATE_MAX_VALUE) { + return single; + } + + // Convert the UTF-16 surrogate pair if necessary. + // For simplicity in usage, and because the frequency of pairs is + // low, look both directions. + + if (single <= LEAD_SURROGATE_MAX_VALUE) { + ++offset16; + if (source.length() != offset16) { + char trail = source.charAt(offset16); + if (trail >= TRAIL_SURROGATE_MIN_VALUE && trail <= TRAIL_SURROGATE_MAX_VALUE) { + return UCharacterProperty.getRawSupplementary(single, trail); + } + } + } else { + --offset16; + if (offset16 >= 0) { + // single is a trail surrogate so + char lead = source.charAt(offset16); + if (lead >= LEAD_SURROGATE_MIN_VALUE && lead <= LEAD_SURROGATE_MAX_VALUE) { + return UCharacterProperty.getRawSupplementary(lead, single); + } + } + } + return single; // return unmatched surrogate + } + + /** + * Extract a single UTF-32 value from a string. Used when iterating forwards or + * backwards (with UTF16.getCharCount(), as well as random access. + * If a validity check is required, use + * UCharacter.isLegal() + * on the return value. If the char retrieved is part of a surrogate + * pair, its supplementary character will be returned. If a complete + * supplementary character is not found the incomplete character will be + * returned + * + * @param source array of UTF-16 chars + * @param offset16 UTF-16 offset to the start of the character. + * @return UTF-32 value for the UTF-32 value that contains the char at offset16. + * The boundaries of that codepoint are the same as in + * bounds32(). + * @exception IndexOutOfBoundsException thrown if offset16 is out of bounds. + * @stable ICU 2.1 + */ + public static int charAt(CharSequence source, int offset16) { + char single = source.charAt(offset16); + if (single < UTF16.LEAD_SURROGATE_MIN_VALUE) { + return single; + } + return _charAt(source, offset16, single); + } + + private static int _charAt(CharSequence source, int offset16, char single) { + if (single > UTF16.TRAIL_SURROGATE_MAX_VALUE) { + return single; + } + + // Convert the UTF-16 surrogate pair if necessary. + // For simplicity in usage, and because the frequency of pairs is + // low, look both directions. + + if (single <= UTF16.LEAD_SURROGATE_MAX_VALUE) { + ++offset16; + if (source.length() != offset16) { + char trail = source.charAt(offset16); + if (trail >= UTF16.TRAIL_SURROGATE_MIN_VALUE && trail <= UTF16.TRAIL_SURROGATE_MAX_VALUE) { + return UCharacterProperty.getRawSupplementary(single, trail); + } + } + } else { + --offset16; + if (offset16 >= 0) { + // single is a trail surrogate so + char lead = source.charAt(offset16); + if (lead >= UTF16.LEAD_SURROGATE_MIN_VALUE && lead <= UTF16.LEAD_SURROGATE_MAX_VALUE) { + return UCharacterProperty.getRawSupplementary(lead, single); + } + } + } + return single; // return unmatched surrogate + } + + /** + * Extract a single UTF-32 value from a substring. Used when iterating forwards + * or backwards (with UTF16.getCharCount(), as well as random + * access. If a validity check is required, use + * UCharacter.isLegal() + * on the return value. If the char retrieved is part of a surrogate + * pair, its supplementary character will be returned. If a complete + * supplementary character is not found the incomplete character will be + * returned + * + * @param source Array of UTF-16 chars + * @param start Offset to substring in the source array for analyzing + * @param limit Offset to substring in the source array for analyzing + * @param offset16 UTF-16 offset relative to start + * @return UTF-32 value for the UTF-32 value that contains the char at offset16. + * The boundaries of that codepoint are the same as in + * bounds32(). + * @exception IndexOutOfBoundsException Thrown if offset16 is not within the + * range of start and limit. + * @stable ICU 2.1 + */ + public static int charAt(char source[], int start, int limit, int offset16) { + offset16 += start; + if (offset16 < start || offset16 >= limit) { + throw new ArrayIndexOutOfBoundsException(offset16); + } + + char single = source[offset16]; + if (!isSurrogate(single)) { + return single; + } + + // Convert the UTF-16 surrogate pair if necessary. + // For simplicity in usage, and because the frequency of pairs is + // low, look both directions. + if (single <= LEAD_SURROGATE_MAX_VALUE) { + offset16++; + if (offset16 >= limit) { + return single; + } + char trail = source[offset16]; + if (isTrailSurrogate(trail)) { + return UCharacterProperty.getRawSupplementary(single, trail); + } + } else { // isTrailSurrogate(single), so + if (offset16 == start) { + return single; + } + offset16--; + char lead = source[offset16]; + if (isLeadSurrogate(lead)) + return UCharacterProperty.getRawSupplementary(lead, single); + } + return single; // return unmatched surrogate + } + + /** + * Determines how many chars this char32 requires. If a validity check is + * required, use + * isLegal() on + * char32 before calling. + * + * @param char32 the input codepoint. + * @return 2 if is in supplementary space, otherwise 1. + * @stable ICU 2.1 + */ + public static int getCharCount(int char32) { + if (char32 < SUPPLEMENTARY_MIN_VALUE) { + return 1; + } + return 2; + } + + /** + * Determines whether the code value is a surrogate. + * + * @param char16 the input character. + * @return true if the input character is a surrogate. + * @stable ICU 2.1 + */ + public static boolean isSurrogate(char char16) { + return (char16 & SURROGATE_BITMASK) == SURROGATE_BITS; + } + + /** + * Determines whether the character is a trail surrogate. + * + * @param char16 the input character. + * @return true if the input character is a trail surrogate. + * @stable ICU 2.1 + */ + public static boolean isTrailSurrogate(char char16) { + return (char16 & TRAIL_SURROGATE_BITMASK) == TRAIL_SURROGATE_BITS; + } + + /** + * Determines whether the character is a lead surrogate. + * + * @param char16 the input character. + * @return true if the input character is a lead surrogate + * @stable ICU 2.1 + */ + public static boolean isLeadSurrogate(char char16) { + return (char16 & LEAD_SURROGATE_BITMASK) == LEAD_SURROGATE_BITS; + } + + /** + * Returns the lead surrogate. If a validity check is required, use + * isLegal() on + * char32 before calling. + * + * @param char32 the input character. + * @return lead surrogate if the getCharCount(ch) is 2;
+ * and 0 otherwise (note: 0 is not a valid lead surrogate). + * @stable ICU 2.1 + */ + public static char getLeadSurrogate(int char32) { + if (char32 >= SUPPLEMENTARY_MIN_VALUE) { + return (char) (LEAD_SURROGATE_OFFSET_ + (char32 >> LEAD_SURROGATE_SHIFT_)); + } + + return 0; + } + + /** + * Returns the trail surrogate. If a validity check is required, use + * isLegal() on + * char32 before calling. + * + * @param char32 the input character. + * @return the trail surrogate if the getCharCount(ch) is 2;
+ * otherwise the character itself + * @stable ICU 2.1 + */ + public static char getTrailSurrogate(int char32) { + if (char32 >= SUPPLEMENTARY_MIN_VALUE) { + return (char) (TRAIL_SURROGATE_MIN_VALUE + (char32 & TRAIL_SURROGATE_MASK_)); + } + + return (char) char32; + } + + /** + * Convenience method corresponding to String.valueOf(char). Returns a one or + * two char string containing the UTF-32 value in UTF16 format. If a validity + * check is required, use + * isLegal() on + * char32 before calling. + * + * @param char32 the input character. + * @return string value of char32 in UTF16 format + * @exception IllegalArgumentException thrown if char32 is a invalid codepoint. + * @stable ICU 2.1 + */ + public static String valueOf(int char32) { + if (char32 < CODEPOINT_MIN_VALUE || char32 > CODEPOINT_MAX_VALUE) { + throw new IllegalArgumentException("Illegal codepoint"); + } + return toString(char32); + } + + /** + * Append a single UTF-32 value to the end of a StringBuffer. If a validity + * check is required, use + * isLegal() on + * char32 before calling. + * + * @param target the buffer to append to + * @param char32 value to append. + * @return the updated StringBuffer + * @exception IllegalArgumentException thrown when char32 does not lie within + * the range of the Unicode codepoints + * @stable ICU 2.1 + */ + public static StringBuffer append(StringBuffer target, int char32) { + // Check for irregular values + if (char32 < CODEPOINT_MIN_VALUE || char32 > CODEPOINT_MAX_VALUE) { + throw new IllegalArgumentException("Illegal codepoint: " + Integer.toHexString(char32)); + } + + // Write the UTF-16 values + if (char32 >= SUPPLEMENTARY_MIN_VALUE) { + target.append(getLeadSurrogate(char32)); + target.append(getTrailSurrogate(char32)); + } else { + target.append((char) char32); + } + return target; + } + + /** + * Shifts offset16 by the argument number of codepoints within a subarray. + * + * @param source char array + * @param start position of the subarray to be performed on + * @param limit position of the subarray to be performed on + * @param offset16 UTF16 position to shift relative to start + * @param shift32 number of codepoints to shift + * @return new shifted offset16 relative to start + * @exception IndexOutOfBoundsException if the new offset16 is out of bounds + * with respect to the subarray or the + * subarray bounds are out of range. + * @stable ICU 2.1 + */ + public static int moveCodePointOffset(char source[], int start, int limit, int offset16, int shift32) { + int size = source.length; + int count; + char ch; + int result = offset16 + start; + if (start < 0 || limit < start) { + throw new StringIndexOutOfBoundsException(start); + } + if (limit > size) { + throw new StringIndexOutOfBoundsException(limit); + } + if (offset16 < 0 || result > limit) { + throw new StringIndexOutOfBoundsException(offset16); + } + if (shift32 > 0) { + if (shift32 + result > size) { + throw new StringIndexOutOfBoundsException(result); + } + count = shift32; + while (result < limit && count > 0) { + ch = source[result]; + if (isLeadSurrogate(ch) && (result + 1 < limit) && isTrailSurrogate(source[result + 1])) { + result++; + } + count--; + result++; + } + } else { + if (result + shift32 < start) { + throw new StringIndexOutOfBoundsException(result); + } + for (count = -shift32; count > 0; count--) { + result--; + if (result < start) { + break; + } + ch = source[result]; + if (isTrailSurrogate(ch) && result > start && isLeadSurrogate(source[result - 1])) { + result--; + } + } + } + if (count != 0) { + throw new StringIndexOutOfBoundsException(shift32); + } + result -= start; + return result; + } + + // private data members ------------------------------------------------- + + /** + * Shift value for lead surrogate to form a supplementary character. + */ + private static final int LEAD_SURROGATE_SHIFT_ = 10; + + /** + * Mask to retrieve the significant value from a trail surrogate. + */ + private static final int TRAIL_SURROGATE_MASK_ = 0x3FF; + + /** + * Value that all lead surrogate starts with + */ + private static final int LEAD_SURROGATE_OFFSET_ = LEAD_SURROGATE_MIN_VALUE + - (SUPPLEMENTARY_MIN_VALUE >> LEAD_SURROGATE_SHIFT_); + + // private methods ------------------------------------------------------ + + /** + *

+ * Converts argument code point and returns a String object representing the + * code point's value in UTF16 format. + *

+ * This method does not check for the validity of the codepoint, the results are + * not guaranteed if a invalid codepoint is passed as argument. + *

+ * The result is a string whose length is 1 for non-supplementary code points, 2 + * otherwise. + * + * @param ch code point + * @return string representation of the code point + */ + private static String toString(int ch) { + if (ch < SUPPLEMENTARY_MIN_VALUE) { + return String.valueOf((char) ch); + } + + StringBuilder result = new StringBuilder(); + result.append(getLeadSurrogate(ch)); + result.append(getTrailSurrogate(ch)); + return result.toString(); + } +} diff --git a/src/main/java/jdk_internal/icu/text/UnicodeSet.java b/src/main/java/jdk_internal/icu/text/UnicodeSet.java index cd46cdc4..56daf740 100755 --- a/src/main/java/jdk_internal/icu/text/UnicodeSet.java +++ b/src/main/java/jdk_internal/icu/text/UnicodeSet.java @@ -1,1515 +1,1515 @@ -/* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ******************************************************************************* - * Copyright (C) 1996-2015, International Business Machines Corporation and - * others. All Rights Reserved. - ******************************************************************************* - */ -package jdk_internal.icu.text; - -import java.text.ParsePosition; -import java.util.ArrayList; -import java.util.TreeSet; - -import jdk_internal.icu.impl.BMPSet; -import jdk_internal.icu.impl.UCharacterProperty; -import jdk_internal.icu.impl.UnicodeSetStringSpan; -import jdk_internal.icu.impl.Utility; -import jdk_internal.icu.lang.UCharacter; -import jdk_internal.icu.util.OutputInt; -import jdk_internal.icu.util.VersionInfo; - -/** - * 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. - * - * Note: method freeze() will not only make the set immutable, but also makes - * important methods much higher performance: contains(c), containsNone(...), - * span(...), spanBack(...) etc. After the object is frozen, any subsequent call - * that wants to change the object will throw UnsupportedOperationException. - * - *

- * 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: - * - *
- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
[]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
- *
- * - * Any character may be preceded by a backslash in order to remove any special - * meaning. White space characters, as defined by the Unicode - * Pattern_White_Space property, 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 - * - * http://www.icu-project.org/userguide/unicodeSet.html. 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. - * - *

- * 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 {@literal '&'} operator or the asymmetric - * set difference may be taken using the '-' operator, for example, - * "{@code [[:L:]&[\\u0000-\\u0FFF]]}" indicates the set of all Unicode letters - * with values less than 4096. Operators ({@literal '&'} 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 - *
- * - *

- * Warning: you cannot add an empty string ("") to a UnicodeSet. - *

- * - *

- * Formal syntax - *

- * - *
- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
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 :=  any character for which - * Character.digit(c, 16) returns a non-negative - * result
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
- *
- *
- *

- * To iterate over contents of UnicodeSet, the following are available: - *

    - *
  • {@link #ranges()} to iterate through the ranges
  • - *
  • {@link #strings()} to iterate through the strings
  • - *
  • {@link #iterator()} to iterate through the entire contents in a single - * loop. That method is, however, not particularly efficient, since it "boxes" - * each code point into a String. - *
- * All of the above can be used in for loops. The - * {@link com.ibm.icu.text.UnicodeSetIterator UnicodeSetIterator} can also be - * used, but not in for loops. - *

- * To replace, count elements, or delete spans, see - * {@link com.ibm.icu.text.UnicodeSetSpanner UnicodeSetSpanner}. - * - * @author Alan Liu - * @stable ICU 2.0 - */ -public class UnicodeSet { - - private static final int LOW = 0x000000; // LOW <= all valid values. ZERO for codepoints - private static final int HIGH = 0x110000; // HIGH > all valid values. 10000 for code units. - // 110000 for codepoints - - /** - * Minimum value that can be stored in a UnicodeSet. - * - * @stable ICU 2.0 - */ - public static final int MIN_VALUE = LOW; - - /** - * Maximum value that can be stored in a UnicodeSet. - * - * @stable ICU 2.0 - */ - public static final int MAX_VALUE = HIGH - 1; - - private int len; // length used; list may be longer to minimize reallocs - private int[] list; // MUST be terminated with HIGH - private int[] rangeList; // internal buffer - private int[] buffer; // internal buffer - - // NOTE: normally the field should be of type SortedSet; but that is missing a - // public clone!! - // is not private so that UnicodeSetIterator can get access - TreeSet strings = new TreeSet(); - - /** - * 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 - * null, indicating that toPattern() must generate a pattern representation from - * the inversion list. - */ - - private static final int START_EXTRA = 16; // initial storage. Must be >= 0 - private static final int GROW_EXTRA = START_EXTRA; // extra amount for growth. Must be >= 0 - - private static UnicodeSet INCLUSION = null; - - private volatile BMPSet bmpSet; // The set is frozen if bmpSet or stringSpan is not null. - private volatile UnicodeSetStringSpan stringSpan; - - // ---------------------------------------------------------------- - // Public API - // ---------------------------------------------------------------- - - /** - * Constructs an empty set. - * - * @stable ICU 2.0 - */ - private UnicodeSet() { - list = new int[1 + START_EXTRA]; - list[len++] = HIGH; - } - - /** - * Constructs a copy of an existing set. - * - * @stable ICU 2.0 - */ - private UnicodeSet(UnicodeSet other) { - set(other); - } - - /** - * 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.0 - */ - public UnicodeSet(int start, int end) { - this(); - complement(start, end); - } - - /** - * Constructs a set from the given pattern. See the class description for the - * syntax of the pattern language. Whitespace is ignored. - * - * @param pattern a string specifying what characters are in the set - * @exception java.lang.IllegalArgumentException if the pattern contains a - * syntax error. - * @stable ICU 2.0 - */ - public UnicodeSet(String pattern) { - this(); - applyPattern(pattern, null); - } - - /** - * Make this object represent the same set as other. - * - * @param other a UnicodeSet whose value will be copied to this - * object - * @stable ICU 2.0 - */ - public UnicodeSet set(UnicodeSet other) { - checkFrozen(); - list = other.list.clone(); - len = other.len; - strings = new TreeSet(other.strings); - return this; - } - - /** - * 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). - * @stable ICU 2.0 - */ - public int size() { - int n = 0; - int count = getRangeCount(); - for (int i = 0; i < count; ++i) { - n += getRangeEnd(i) - getRangeStart(i) + 1; - } - return n + strings.size(); - } - - // for internal use, after checkFrozen has been called - private UnicodeSet add_unchecked(int start, int end) { - if (start < MIN_VALUE || start > MAX_VALUE) { - throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(start, 6)); - } - if (end < MIN_VALUE || end > MAX_VALUE) { - throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(end, 6)); - } - if (start < end) { - add(range(start, end), 2, 0); - } else if (start == end) { - add(start); - } - return this; - } - - /** - * 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. - * - * @stable ICU 2.0 - */ - public final UnicodeSet add(int c) { - checkFrozen(); - return add_unchecked(c); - } - - // for internal use only, after checkFrozen has been called - private final UnicodeSet add_unchecked(int c) { - if (c < MIN_VALUE || c > MAX_VALUE) { - throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(c, 6)); - } - - // find smallest i such that c < list[i] - // if odd, then it is IN the set - // if even, then it is OUT of the set - int i = findCodePoint(c); - - // already in set? - if ((i & 1) != 0) - return this; - - // HIGH is 0x110000 - // assert(list[len-1] == HIGH); - - // empty = [HIGH] - // [start_0, limit_0, start_1, limit_1, HIGH] - - // [..., start_k-1, limit_k-1, start_k, limit_k, ..., HIGH] - // ^ - // list[i] - - // i == 0 means c is before the first range - - 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 == MAX_VALUE) { - ensureCapacity(len + 1); - list[len++] = HIGH; - } - if (i > 0 && c == list[i - 1]) { - // collapse adjacent ranges - - // [..., start_k-1, c, c, limit_k, ..., HIGH] - // ^ - // list[i] - System.arraycopy(list, i + 1, list, i - 1, len - i - 1); - len -= 2; - } - } - - else if (i > 0 && c == list[i - 1]) { - // c is after end of prior range - list[i - 1]++; - // no need to chcek 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] - - // Don't use ensureCapacity() to save on copying. - // NOTE: This has no measurable impact on performance, - // but it might help in some usage patterns. - if (len + 2 > list.length) { - int[] temp = new int[len + 2 + GROW_EXTRA]; - if (i != 0) - System.arraycopy(list, 0, temp, 0, i); - System.arraycopy(list, i, temp, i + 2, len - i); - list = temp; - } else { - System.arraycopy(list, i, list, i + 2, len - i); - } - - list[i] = c; - list[i + 1] = c + 1; - len += 2; - } - - return this; - } - - /** - * 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 {@code "ch" => {"ch"}}
- * Warning: you cannot add an empty string ("") to a UnicodeSet. - * - * @param s the source string - * @return this object, for chaining - * @stable ICU 2.0 - */ - public final UnicodeSet add(CharSequence s) { - checkFrozen(); - int cp = getSingleCP(s); - if (cp < 0) { - strings.add(s.toString()); - } else { - add_unchecked(cp, cp); - } - return this; - } - - /** - * Utility for getting code point from single code point CharSequence. See the - * public UTF16.getSingleCodePoint() - * - * @return a code point IF the string consists of a single one. otherwise - * returns -1. - * @param s to test - */ - private static int getSingleCP(CharSequence s) { - if (s.length() < 1) { - throw new IllegalArgumentException("Can't use zero-length strings in UnicodeSet"); - } - if (s.length() > 2) - return -1; - if (s.length() == 1) - return s.charAt(0); - - // at this point, len = 2 - int cp = UTF16.charAt(s, 0); - if (cp > 0xFFFF) { // is surrogate pair - return cp; - } - return -1; - } - - /** - * 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 {@code end > start} then an empty range is complemented, 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. - * @stable ICU 2.0 - */ - public UnicodeSet complement(int start, int end) { - checkFrozen(); - if (start < MIN_VALUE || start > MAX_VALUE) { - throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(start, 6)); - } - if (end < MIN_VALUE || end > MAX_VALUE) { - throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(end, 6)); - } - if (start <= end) { - xor(range(start, end), 2, 0); - } - return this; - } - - /** - * 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 - * @stable ICU 2.0 - */ - public boolean contains(int c) { - if (c < MIN_VALUE || c > MAX_VALUE) { - throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(c, 6)); - } - if (bmpSet != null) { - return bmpSet.contains(c); - } - if (stringSpan != null) { - return stringSpan.contains(c); - } - - /* - * // Set i to the index of the start item greater than ch // We know we will - * terminate without length test! int i = -1; while (true) { if (c < list[++i]) - * break; } - */ - - int i = findCodePoint(c); - - return ((i & 1) != 0); // 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] - */ - private final int findCodePoint(int c) { - /* - * 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 [:all:] [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. - if (len >= 2 && c >= list[len - 2]) - return len - 1; - int lo = 0; - int hi = len - 1; - // invariant: c >= list[lo] - // invariant: c < list[hi] - for (;;) { - int i = (lo + hi) >>> 1; - if (i == lo) - return hi; - if (c < list[i]) { - hi = i; - } else { - lo = i; - } - } - } - - /** - * 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. - * @stable ICU 2.0 - */ - public UnicodeSet retainAll(UnicodeSet c) { - checkFrozen(); - retain(c.list, c.len, 0); - strings.retainAll(c.strings); - return this; - } - - /** - * Removes all of the elements from this set. This set will be empty after this - * call returns. - * - * @stable ICU 2.0 - */ - public UnicodeSet clear() { - checkFrozen(); - list[0] = HIGH; - len = 1; - strings.clear(); - return this; - } - - /** - * Iteration method that returns the number of ranges contained in this set. - * - * @see #getRangeStart - * @see #getRangeEnd - * @stable ICU 2.0 - */ - public int getRangeCount() { - return len / 2; - } - - /** - * Iteration method that returns the first character in the specified range of - * this set. - * - * @exception ArrayIndexOutOfBoundsException if index is outside the range - * 0..getRangeCount()-1 - * @see #getRangeCount - * @see #getRangeEnd - * @stable ICU 2.0 - */ - public int getRangeStart(int index) { - return list[index * 2]; - } - - /** - * Iteration method that returns the last character in the specified range of - * this set. - * - * @exception ArrayIndexOutOfBoundsException if index is outside the range - * 0..getRangeCount()-1 - * @see #getRangeStart - * @see #getRangeEnd - * @stable ICU 2.0 - */ - public int getRangeEnd(int index) { - return (list[index * 2 + 1] - 1); - } - - // ---------------------------------------------------------------- - // Implementation: Pattern parsing - // ---------------------------------------------------------------- - - /** - * 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 an inversion list for the parsed - * pattern is returned. This method calls itself recursively to parse embedded - * subpatterns. - * - * @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. - * @return an inversion list for the parsed substring of pattern - * @exception java.lang.IllegalArgumentException if the parse fails. - */ - private UnicodeSet applyPattern(String pattern, ParsePosition pos) { - if ("[:age=3.2:]".equals(pattern)) { - checkFrozen(); - VersionInfo version = VersionInfo.getInstance("3.2"); - applyFilter(new VersionFilter(version), UCharacterProperty.SRC_PROPSVEC); - } else { - throw new IllegalStateException("UnicodeSet.applyPattern(unexpected pattern " + pattern + ")"); - } - - return this; - } - - // ---------------------------------------------------------------- - // Implementation: Utility methods - // ---------------------------------------------------------------- - - private void ensureCapacity(int newLen) { - if (newLen <= list.length) - return; - int[] temp = new int[newLen + GROW_EXTRA]; - System.arraycopy(list, 0, temp, 0, len); - list = temp; - } - - private void ensureBufferCapacity(int newLen) { - if (buffer != null && newLen <= buffer.length) - return; - buffer = new int[newLen + GROW_EXTRA]; - } - - /** - * Assumes start <= end. - */ - private int[] range(int start, int end) { - if (rangeList == null) { - rangeList = new int[] { start, end + 1, HIGH }; - } else { - rangeList[0] = start; - rangeList[1] = end + 1; - } - return rangeList; - } - - // ---------------------------------------------------------------- - // Implementation: Fundamental operations - // ---------------------------------------------------------------- - - // polarity = 0, 3 is normal: x xor y - // polarity = 1, 2: x xor ~y == x === y - - private UnicodeSet xor(int[] other, int otherLen, int polarity) { - ensureBufferCapacity(len + otherLen); - int i = 0, j = 0, k = 0; - int a = list[i++]; - int b; - if (polarity == 1 || polarity == 2) { - b = LOW; - if (other[j] == LOW) { // skip base if already LOW - ++j; - b = other[j]; - } - } else { - b = other[j++]; - } - // simplest of all the routines - // sort the values, discarding identicals! - while (true) { - if (a < b) { - buffer[k++] = a; - a = list[i++]; - } else if (b < a) { - buffer[k++] = b; - b = other[j++]; - } else if (a != HIGH) { // at this point, a == b - // discard both values! - a = list[i++]; - b = other[j++]; - } else { // DONE! - buffer[k++] = HIGH; - len = k; - break; - } - } - // swap list and buffer - int[] temp = list; - list = buffer; - buffer = temp; - return this; - } - - // polarity = 0 is normal: x union y - // polarity = 2: x union ~y - // polarity = 1: ~x union y - // polarity = 3: ~x union ~y - - private UnicodeSet add(int[] other, int otherLen, int polarity) { - ensureBufferCapacity(len + otherLen); - int i = 0, j = 0, k = 0; - int a = list[i++]; - int 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. - main: while (true) { - 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 == HIGH) - break main; - // 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 == HIGH) - break main; - buffer[k++] = a; - } else { // take b - if (b == HIGH) - break main; - 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 == HIGH) - break main; - 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 == HIGH) - break main; - a = list[i++]; - polarity ^= 1; - b = other[j++]; - polarity ^= 2; - } - break; - } - } - buffer[k++] = HIGH; // terminate - len = k; - // swap list and buffer - int[] temp = list; - list = buffer; - buffer = temp; - return this; - } - - // polarity = 0 is normal: x intersect y - // polarity = 2: x intersect ~y == set-minus - // polarity = 1: ~x intersect y - // polarity = 3: ~x intersect ~y - - private UnicodeSet retain(int[] other, int otherLen, int polarity) { - ensureBufferCapacity(len + otherLen); - int i = 0, j = 0, k = 0; - int a = list[i++]; - int 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. - main: while (true) { - 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 == HIGH) - break main; - 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 == HIGH) - break main; - 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 == HIGH) - break main; - 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 == HIGH) - break main; - a = list[i++]; - polarity ^= 1; - b = other[j++]; - polarity ^= 2; - } - break; - } - } - buffer[k++] = HIGH; // terminate - len = k; - // swap list and buffer - int[] temp = list; - list = buffer; - buffer = temp; - return this; - } - - private static final int max(int a, int b) { - return (a > b) ? a : b; - } - - // ---------------------------------------------------------------- - // Generic filter-based scanning code - // ---------------------------------------------------------------- - - private static interface Filter { - boolean contains(int codePoint); - } - - private static final VersionInfo NO_VERSION = VersionInfo.getInstance(0, 0, 0, 0); - - private static class VersionFilter implements Filter { - VersionInfo version; - - VersionFilter(VersionInfo version) { - this.version = version; - } - - public boolean contains(int ch) { - VersionInfo v = UCharacter.getAge(ch); - // Reference comparison ok; VersionInfo caches and reuses - // unique objects. - return v != NO_VERSION && v.compareTo(version) <= 0; - } - } - - private static synchronized UnicodeSet getInclusions(int src) { - if (src != UCharacterProperty.SRC_PROPSVEC) { - throw new IllegalStateException("UnicodeSet.getInclusions(unknown src " + src + ")"); - } - - if (INCLUSION == null) { - UnicodeSet incl = new UnicodeSet(); - UCharacterProperty.INSTANCE.upropsvec_addPropertyStarts(incl); - INCLUSION = incl; - } - return INCLUSION; - } - - /** - * Generic filter-based scanning code for UCD property UnicodeSets. - */ - private UnicodeSet applyFilter(Filter filter, int src) { - // 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. - // getInclusions(src) contains exactly the first characters of - // same-value ranges for the given properties "source". - - clear(); - - int startHasProperty = -1; - UnicodeSet inclusions = getInclusions(src); - int limitRange = inclusions.getRangeCount(); - - for (int j = 0; j < limitRange; ++j) { - // get current range - int start = inclusions.getRangeStart(j); - int end = inclusions.getRangeEnd(j); - - // for all the code points in the range, process - for (int ch = start; ch <= end; ++ch) { - // only add to the unicodeset on inflection points -- - // where the hasProperty value changes to false - if (filter.contains(ch)) { - if (startHasProperty < 0) { - startHasProperty = ch; - } - } else if (startHasProperty >= 0) { - add_unchecked(startHasProperty, ch - 1); - startHasProperty = -1; - } - } - } - if (startHasProperty >= 0) { - add_unchecked(startHasProperty, 0x10FFFF); - } - - return this; - } - - /** - * Is this frozen, according to the Freezable interface? - * - * @return value - * @stable ICU 3.8 - */ - public boolean isFrozen() { - return (bmpSet != null || stringSpan != null); - } - - /** - * Freeze this class, according to the Freezable interface. - * - * @return this - * @stable ICU 4.4 - */ - public UnicodeSet freeze() { - if (!isFrozen()) { - // Do most of what compact() does before freezing because - // compact() will not work when the set is frozen. - // Small modification: Don't shrink if the savings would be tiny (<=GROW_EXTRA). - - // Delete buffer first to defragment memory less. - buffer = null; - if (list.length > (len + GROW_EXTRA)) { - // Make the capacity equal to len or 1. - // We don't want to realloc of 0 size. - int capacity = (len == 0) ? 1 : len; - int[] oldList = list; - list = new int[capacity]; - for (int i = capacity; i-- > 0;) { - list[i] = oldList[i]; - } - } - - // Optimize contains() and span() and similar functions. - if (!strings.isEmpty()) { - stringSpan = new UnicodeSetStringSpan(this, new ArrayList(strings), UnicodeSetStringSpan.ALL); - } - if (stringSpan == null || !stringSpan.needsStringSpanUTF16()) { - // Optimize for code point spans. - // There are no strings, or - // all strings are irrelevant for span() etc. because - // all of each string's code points are contained in this set. - // However, fully contained strings are relevant for spanAndCount(), - // so we create both objects. - bmpSet = new BMPSet(list, len); - } - } - return this; - } - - /** - * Span a string using this UnicodeSet. - *

- * To replace, count elements, or delete spans, see - * {@link com.ibm.icu.text.UnicodeSetSpanner UnicodeSetSpanner}. - * - * @param s The string to be spanned - * @param spanCondition The span condition - * @return the length of the span - * @stable ICU 4.4 - */ - public int span(CharSequence s, SpanCondition spanCondition) { - return span(s, 0, spanCondition); - } - - /** - * Span a string using this UnicodeSet. If the start index is less than 0, span - * will start from 0. If the start index is greater than the string length, span - * returns the string length. - *

- * To replace, count elements, or delete spans, see - * {@link com.ibm.icu.text.UnicodeSetSpanner UnicodeSetSpanner}. - * - * @param s The string to be spanned - * @param start The start index that the span begins - * @param spanCondition The span condition - * @return the string index which ends the span (i.e. exclusive) - * @stable ICU 4.4 - */ - public int span(CharSequence s, int start, SpanCondition spanCondition) { - int end = s.length(); - if (start < 0) { - start = 0; - } else if (start >= end) { - return end; - } - if (bmpSet != null) { - // Frozen set without strings, or no string is relevant for span(). - return bmpSet.span(s, start, spanCondition, null); - } - if (stringSpan != null) { - return stringSpan.span(s, start, spanCondition); - } else if (!strings.isEmpty()) { - int which = spanCondition == SpanCondition.NOT_CONTAINED ? UnicodeSetStringSpan.FWD_UTF16_NOT_CONTAINED - : UnicodeSetStringSpan.FWD_UTF16_CONTAINED; - UnicodeSetStringSpan strSpan = new UnicodeSetStringSpan(this, new ArrayList(strings), which); - if (strSpan.needsStringSpanUTF16()) { - return strSpan.span(s, start, spanCondition); - } - } - - return spanCodePointsAndCount(s, start, spanCondition, null); - } - - /** - * Same as span() but also counts the smallest number of set elements on any - * path across the span. - *

- * To replace, count elements, or delete spans, see - * {@link com.ibm.icu.text.UnicodeSetSpanner UnicodeSetSpanner}. - * - * @param outCount An output-only object (must not be null) for returning the - * count. - * @return the limit (exclusive end) of the span - */ - public int spanAndCount(CharSequence s, int start, SpanCondition spanCondition, OutputInt outCount) { - if (outCount == null) { - throw new IllegalArgumentException("outCount must not be null"); - } - int end = s.length(); - if (start < 0) { - start = 0; - } else if (start >= end) { - return end; - } - if (stringSpan != null) { - // We might also have bmpSet != null, - // but fully-contained strings are relevant for counting elements. - return stringSpan.spanAndCount(s, start, spanCondition, outCount); - } else if (bmpSet != null) { - return bmpSet.span(s, start, spanCondition, outCount); - } else if (!strings.isEmpty()) { - int which = spanCondition == SpanCondition.NOT_CONTAINED ? UnicodeSetStringSpan.FWD_UTF16_NOT_CONTAINED - : UnicodeSetStringSpan.FWD_UTF16_CONTAINED; - which |= UnicodeSetStringSpan.WITH_COUNT; - UnicodeSetStringSpan strSpan = new UnicodeSetStringSpan(this, new ArrayList(strings), which); - return strSpan.spanAndCount(s, start, spanCondition, outCount); - } - - return spanCodePointsAndCount(s, start, spanCondition, outCount); - } - - private int spanCodePointsAndCount(CharSequence s, int start, SpanCondition spanCondition, OutputInt outCount) { - // Pin to 0/1 values. - boolean spanContained = (spanCondition != SpanCondition.NOT_CONTAINED); - - int c; - int next = start; - int length = s.length(); - int count = 0; - do { - c = Character.codePointAt(s, next); - if (spanContained != contains(c)) { - break; - } - ++count; - next += Character.charCount(c); - } while (next < length); - if (outCount != null) { - outCount.value = count; - } - return next; - } - - /** - * Span a string backwards (from the fromIndex) using this UnicodeSet. If the - * fromIndex is less than 0, spanBack will return 0. If fromIndex is greater - * than the string length, spanBack will start from the string length. - *

- * To replace, count elements, or delete spans, see - * {@link com.ibm.icu.text.UnicodeSetSpanner UnicodeSetSpanner}. - * - * @param s The string to be spanned - * @param fromIndex The index of the char (exclusive) that the string should - * be spanned backwards - * @param spanCondition The span condition - * @return The string index which starts the span (i.e. inclusive). - * @stable ICU 4.4 - */ - public int spanBack(CharSequence s, int fromIndex, SpanCondition spanCondition) { - if (fromIndex <= 0) { - return 0; - } - if (fromIndex > s.length()) { - fromIndex = s.length(); - } - if (bmpSet != null) { - // Frozen set without strings, or no string is relevant for spanBack(). - return bmpSet.spanBack(s, fromIndex, spanCondition); - } - if (stringSpan != null) { - return stringSpan.spanBack(s, fromIndex, spanCondition); - } else if (!strings.isEmpty()) { - int which = (spanCondition == SpanCondition.NOT_CONTAINED) ? UnicodeSetStringSpan.BACK_UTF16_NOT_CONTAINED - : UnicodeSetStringSpan.BACK_UTF16_CONTAINED; - UnicodeSetStringSpan strSpan = new UnicodeSetStringSpan(this, new ArrayList(strings), which); - if (strSpan.needsStringSpanUTF16()) { - return strSpan.spanBack(s, fromIndex, spanCondition); - } - } - - // Pin to 0/1 values. - boolean spanContained = (spanCondition != SpanCondition.NOT_CONTAINED); - - int c; - int prev = fromIndex; - do { - c = Character.codePointBefore(s, prev); - if (spanContained != contains(c)) { - break; - } - prev -= Character.charCount(c); - } while (prev > 0); - return prev; - } - - /** - * Clone a thawed version of this class, according to the Freezable interface. - * - * @return the clone, not frozen - * @stable ICU 4.4 - */ - public UnicodeSet cloneAsThawed() { - UnicodeSet result = new UnicodeSet(this); - assert !result.isFrozen(); - return result; - } - - // internal function - private void checkFrozen() { - if (isFrozen()) { - throw new UnsupportedOperationException("Attempt to modify frozen object"); - } - } - - /** - * 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): - *

    - *
  • CONTAINED and SIMPLE work the same. - *
  • CONTAINED and SIMPLE are inverses of NOT_CONTAINED. - *
  • span() and spanBack() partition any string the same way when alternating - * between 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(SIMPLE) may be shorter than 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", CONTAINED) will return 4 - * but span("xyax", SIMPLE) will return 3. span(SIMPLE) will never be longer - * than 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. - * - * @stable ICU 4.4 - */ - public enum SpanCondition { - /** - * 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 4.4 - */ - NOT_CONTAINED, - - /** - * 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 4.4 - */ - CONTAINED, - - /** - * 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 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 4.4 - */ - SIMPLE, - } - -} +/* + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ******************************************************************************* + * Copyright (C) 1996-2015, International Business Machines Corporation and + * others. All Rights Reserved. + ******************************************************************************* + */ +package jdk_internal.icu.text; + +import java.text.ParsePosition; +import java.util.ArrayList; +import java.util.TreeSet; + +import jdk_internal.icu.impl.BMPSet; +import jdk_internal.icu.impl.UCharacterProperty; +import jdk_internal.icu.impl.UnicodeSetStringSpan; +import jdk_internal.icu.impl.Utility; +import jdk_internal.icu.lang.UCharacter; +import jdk_internal.icu.util.OutputInt; +import jdk_internal.icu.util.VersionInfo; + +/** + * 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. + * + * Note: method freeze() will not only make the set immutable, but also makes + * important methods much higher performance: contains(c), containsNone(...), + * span(...), spanBack(...) etc. After the object is frozen, any subsequent call + * that wants to change the object will throw UnsupportedOperationException. + * + *

+ * 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: + * + *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
[]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
+ *
+ * + * Any character may be preceded by a backslash in order to remove any special + * meaning. White space characters, as defined by the Unicode + * Pattern_White_Space property, 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 + * + * http://www.icu-project.org/userguide/unicodeSet.html. 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. + * + *

+ * 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 {@literal '&'} operator or the asymmetric + * set difference may be taken using the '-' operator, for example, + * "{@code [[:L:]&[\\u0000-\\u0FFF]]}" indicates the set of all Unicode letters + * with values less than 4096. Operators ({@literal '&'} 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 + *
+ * + *

+ * Warning: you cannot add an empty string ("") to a UnicodeSet. + *

+ * + *

+ * Formal syntax + *

+ * + *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
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 :=  any character for which + * Character.digit(c, 16) returns a non-negative + * result
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
+ *
+ *
+ *

+ * To iterate over contents of UnicodeSet, the following are available: + *

    + *
  • {@link #ranges()} to iterate through the ranges
  • + *
  • {@link #strings()} to iterate through the strings
  • + *
  • {@link #iterator()} to iterate through the entire contents in a single + * loop. That method is, however, not particularly efficient, since it "boxes" + * each code point into a String. + *
+ * All of the above can be used in for loops. The + * {@link com.ibm.icu.text.UnicodeSetIterator UnicodeSetIterator} can also be + * used, but not in for loops. + *

+ * To replace, count elements, or delete spans, see + * {@link com.ibm.icu.text.UnicodeSetSpanner UnicodeSetSpanner}. + * + * @author Alan Liu + * @stable ICU 2.0 + */ +public class UnicodeSet { + + private static final int LOW = 0x000000; // LOW <= all valid values. ZERO for codepoints + private static final int HIGH = 0x110000; // HIGH > all valid values. 10000 for code units. + // 110000 for codepoints + + /** + * Minimum value that can be stored in a UnicodeSet. + * + * @stable ICU 2.0 + */ + public static final int MIN_VALUE = LOW; + + /** + * Maximum value that can be stored in a UnicodeSet. + * + * @stable ICU 2.0 + */ + public static final int MAX_VALUE = HIGH - 1; + + private int len; // length used; list may be longer to minimize reallocs + private int[] list; // MUST be terminated with HIGH + private int[] rangeList; // internal buffer + private int[] buffer; // internal buffer + + // NOTE: normally the field should be of type SortedSet; but that is missing a + // public clone!! + // is not private so that UnicodeSetIterator can get access + TreeSet strings = new TreeSet(); + + /** + * 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 + * null, indicating that toPattern() must generate a pattern representation from + * the inversion list. + */ + + private static final int START_EXTRA = 16; // initial storage. Must be >= 0 + private static final int GROW_EXTRA = START_EXTRA; // extra amount for growth. Must be >= 0 + + private static UnicodeSet INCLUSION = null; + + private volatile BMPSet bmpSet; // The set is frozen if bmpSet or stringSpan is not null. + private volatile UnicodeSetStringSpan stringSpan; + + // ---------------------------------------------------------------- + // Public API + // ---------------------------------------------------------------- + + /** + * Constructs an empty set. + * + * @stable ICU 2.0 + */ + private UnicodeSet() { + list = new int[1 + START_EXTRA]; + list[len++] = HIGH; + } + + /** + * Constructs a copy of an existing set. + * + * @stable ICU 2.0 + */ + private UnicodeSet(UnicodeSet other) { + set(other); + } + + /** + * 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.0 + */ + public UnicodeSet(int start, int end) { + this(); + complement(start, end); + } + + /** + * Constructs a set from the given pattern. See the class description for the + * syntax of the pattern language. Whitespace is ignored. + * + * @param pattern a string specifying what characters are in the set + * @exception java.lang.IllegalArgumentException if the pattern contains a + * syntax error. + * @stable ICU 2.0 + */ + public UnicodeSet(String pattern) { + this(); + applyPattern(pattern, null); + } + + /** + * Make this object represent the same set as other. + * + * @param other a UnicodeSet whose value will be copied to this + * object + * @stable ICU 2.0 + */ + public UnicodeSet set(UnicodeSet other) { + checkFrozen(); + list = other.list.clone(); + len = other.len; + strings = new TreeSet(other.strings); + return this; + } + + /** + * 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). + * @stable ICU 2.0 + */ + public int size() { + int n = 0; + int count = getRangeCount(); + for (int i = 0; i < count; ++i) { + n += getRangeEnd(i) - getRangeStart(i) + 1; + } + return n + strings.size(); + } + + // for internal use, after checkFrozen has been called + private UnicodeSet add_unchecked(int start, int end) { + if (start < MIN_VALUE || start > MAX_VALUE) { + throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(start, 6)); + } + if (end < MIN_VALUE || end > MAX_VALUE) { + throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(end, 6)); + } + if (start < end) { + add(range(start, end), 2, 0); + } else if (start == end) { + add(start); + } + return this; + } + + /** + * 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. + * + * @stable ICU 2.0 + */ + public final UnicodeSet add(int c) { + checkFrozen(); + return add_unchecked(c); + } + + // for internal use only, after checkFrozen has been called + private final UnicodeSet add_unchecked(int c) { + if (c < MIN_VALUE || c > MAX_VALUE) { + throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(c, 6)); + } + + // find smallest i such that c < list[i] + // if odd, then it is IN the set + // if even, then it is OUT of the set + int i = findCodePoint(c); + + // already in set? + if ((i & 1) != 0) + return this; + + // HIGH is 0x110000 + // assert(list[len-1] == HIGH); + + // empty = [HIGH] + // [start_0, limit_0, start_1, limit_1, HIGH] + + // [..., start_k-1, limit_k-1, start_k, limit_k, ..., HIGH] + // ^ + // list[i] + + // i == 0 means c is before the first range + + 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 == MAX_VALUE) { + ensureCapacity(len + 1); + list[len++] = HIGH; + } + if (i > 0 && c == list[i - 1]) { + // collapse adjacent ranges + + // [..., start_k-1, c, c, limit_k, ..., HIGH] + // ^ + // list[i] + System.arraycopy(list, i + 1, list, i - 1, len - i - 1); + len -= 2; + } + } + + else if (i > 0 && c == list[i - 1]) { + // c is after end of prior range + list[i - 1]++; + // no need to chcek 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] + + // Don't use ensureCapacity() to save on copying. + // NOTE: This has no measurable impact on performance, + // but it might help in some usage patterns. + if (len + 2 > list.length) { + int[] temp = new int[len + 2 + GROW_EXTRA]; + if (i != 0) + System.arraycopy(list, 0, temp, 0, i); + System.arraycopy(list, i, temp, i + 2, len - i); + list = temp; + } else { + System.arraycopy(list, i, list, i + 2, len - i); + } + + list[i] = c; + list[i + 1] = c + 1; + len += 2; + } + + return this; + } + + /** + * 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 {@code "ch" => {"ch"}}
+ * Warning: you cannot add an empty string ("") to a UnicodeSet. + * + * @param s the source string + * @return this object, for chaining + * @stable ICU 2.0 + */ + public final UnicodeSet add(CharSequence s) { + checkFrozen(); + int cp = getSingleCP(s); + if (cp < 0) { + strings.add(s.toString()); + } else { + add_unchecked(cp, cp); + } + return this; + } + + /** + * Utility for getting code point from single code point CharSequence. See the + * public UTF16.getSingleCodePoint() + * + * @return a code point IF the string consists of a single one. otherwise + * returns -1. + * @param s to test + */ + private static int getSingleCP(CharSequence s) { + if (s.length() < 1) { + throw new IllegalArgumentException("Can't use zero-length strings in UnicodeSet"); + } + if (s.length() > 2) + return -1; + if (s.length() == 1) + return s.charAt(0); + + // at this point, len = 2 + int cp = UTF16.charAt(s, 0); + if (cp > 0xFFFF) { // is surrogate pair + return cp; + } + return -1; + } + + /** + * 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 {@code end > start} then an empty range is complemented, 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. + * @stable ICU 2.0 + */ + public UnicodeSet complement(int start, int end) { + checkFrozen(); + if (start < MIN_VALUE || start > MAX_VALUE) { + throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(start, 6)); + } + if (end < MIN_VALUE || end > MAX_VALUE) { + throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(end, 6)); + } + if (start <= end) { + xor(range(start, end), 2, 0); + } + return this; + } + + /** + * 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 + * @stable ICU 2.0 + */ + public boolean contains(int c) { + if (c < MIN_VALUE || c > MAX_VALUE) { + throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(c, 6)); + } + if (bmpSet != null) { + return bmpSet.contains(c); + } + if (stringSpan != null) { + return stringSpan.contains(c); + } + + /* + * // Set i to the index of the start item greater than ch // We know we will + * terminate without length test! int i = -1; while (true) { if (c < list[++i]) + * break; } + */ + + int i = findCodePoint(c); + + return ((i & 1) != 0); // 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] + */ + private final int findCodePoint(int c) { + /* + * 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 [:all:] [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. + if (len >= 2 && c >= list[len - 2]) + return len - 1; + int lo = 0; + int hi = len - 1; + // invariant: c >= list[lo] + // invariant: c < list[hi] + for (;;) { + int i = (lo + hi) >>> 1; + if (i == lo) + return hi; + if (c < list[i]) { + hi = i; + } else { + lo = i; + } + } + } + + /** + * 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. + * @stable ICU 2.0 + */ + public UnicodeSet retainAll(UnicodeSet c) { + checkFrozen(); + retain(c.list, c.len, 0); + strings.retainAll(c.strings); + return this; + } + + /** + * Removes all of the elements from this set. This set will be empty after this + * call returns. + * + * @stable ICU 2.0 + */ + public UnicodeSet clear() { + checkFrozen(); + list[0] = HIGH; + len = 1; + strings.clear(); + return this; + } + + /** + * Iteration method that returns the number of ranges contained in this set. + * + * @see #getRangeStart + * @see #getRangeEnd + * @stable ICU 2.0 + */ + public int getRangeCount() { + return len / 2; + } + + /** + * Iteration method that returns the first character in the specified range of + * this set. + * + * @exception ArrayIndexOutOfBoundsException if index is outside the range + * 0..getRangeCount()-1 + * @see #getRangeCount + * @see #getRangeEnd + * @stable ICU 2.0 + */ + public int getRangeStart(int index) { + return list[index * 2]; + } + + /** + * Iteration method that returns the last character in the specified range of + * this set. + * + * @exception ArrayIndexOutOfBoundsException if index is outside the range + * 0..getRangeCount()-1 + * @see #getRangeStart + * @see #getRangeEnd + * @stable ICU 2.0 + */ + public int getRangeEnd(int index) { + return (list[index * 2 + 1] - 1); + } + + // ---------------------------------------------------------------- + // Implementation: Pattern parsing + // ---------------------------------------------------------------- + + /** + * 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 an inversion list for the parsed + * pattern is returned. This method calls itself recursively to parse embedded + * subpatterns. + * + * @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. + * @return an inversion list for the parsed substring of pattern + * @exception java.lang.IllegalArgumentException if the parse fails. + */ + private UnicodeSet applyPattern(String pattern, ParsePosition pos) { + if ("[:age=3.2:]".equals(pattern)) { + checkFrozen(); + VersionInfo version = VersionInfo.getInstance("3.2"); + applyFilter(new VersionFilter(version), UCharacterProperty.SRC_PROPSVEC); + } else { + throw new IllegalStateException("UnicodeSet.applyPattern(unexpected pattern " + pattern + ")"); + } + + return this; + } + + // ---------------------------------------------------------------- + // Implementation: Utility methods + // ---------------------------------------------------------------- + + private void ensureCapacity(int newLen) { + if (newLen <= list.length) + return; + int[] temp = new int[newLen + GROW_EXTRA]; + System.arraycopy(list, 0, temp, 0, len); + list = temp; + } + + private void ensureBufferCapacity(int newLen) { + if (buffer != null && newLen <= buffer.length) + return; + buffer = new int[newLen + GROW_EXTRA]; + } + + /** + * Assumes start <= end. + */ + private int[] range(int start, int end) { + if (rangeList == null) { + rangeList = new int[] { start, end + 1, HIGH }; + } else { + rangeList[0] = start; + rangeList[1] = end + 1; + } + return rangeList; + } + + // ---------------------------------------------------------------- + // Implementation: Fundamental operations + // ---------------------------------------------------------------- + + // polarity = 0, 3 is normal: x xor y + // polarity = 1, 2: x xor ~y == x === y + + private UnicodeSet xor(int[] other, int otherLen, int polarity) { + ensureBufferCapacity(len + otherLen); + int i = 0, j = 0, k = 0; + int a = list[i++]; + int b; + if (polarity == 1 || polarity == 2) { + b = LOW; + if (other[j] == LOW) { // skip base if already LOW + ++j; + b = other[j]; + } + } else { + b = other[j++]; + } + // simplest of all the routines + // sort the values, discarding identicals! + while (true) { + if (a < b) { + buffer[k++] = a; + a = list[i++]; + } else if (b < a) { + buffer[k++] = b; + b = other[j++]; + } else if (a != HIGH) { // at this point, a == b + // discard both values! + a = list[i++]; + b = other[j++]; + } else { // DONE! + buffer[k++] = HIGH; + len = k; + break; + } + } + // swap list and buffer + int[] temp = list; + list = buffer; + buffer = temp; + return this; + } + + // polarity = 0 is normal: x union y + // polarity = 2: x union ~y + // polarity = 1: ~x union y + // polarity = 3: ~x union ~y + + private UnicodeSet add(int[] other, int otherLen, int polarity) { + ensureBufferCapacity(len + otherLen); + int i = 0, j = 0, k = 0; + int a = list[i++]; + int 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. + main: while (true) { + 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 == HIGH) + break main; + // 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 == HIGH) + break main; + buffer[k++] = a; + } else { // take b + if (b == HIGH) + break main; + 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 == HIGH) + break main; + 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 == HIGH) + break main; + a = list[i++]; + polarity ^= 1; + b = other[j++]; + polarity ^= 2; + } + break; + } + } + buffer[k++] = HIGH; // terminate + len = k; + // swap list and buffer + int[] temp = list; + list = buffer; + buffer = temp; + return this; + } + + // polarity = 0 is normal: x intersect y + // polarity = 2: x intersect ~y == set-minus + // polarity = 1: ~x intersect y + // polarity = 3: ~x intersect ~y + + private UnicodeSet retain(int[] other, int otherLen, int polarity) { + ensureBufferCapacity(len + otherLen); + int i = 0, j = 0, k = 0; + int a = list[i++]; + int 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. + main: while (true) { + 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 == HIGH) + break main; + 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 == HIGH) + break main; + 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 == HIGH) + break main; + 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 == HIGH) + break main; + a = list[i++]; + polarity ^= 1; + b = other[j++]; + polarity ^= 2; + } + break; + } + } + buffer[k++] = HIGH; // terminate + len = k; + // swap list and buffer + int[] temp = list; + list = buffer; + buffer = temp; + return this; + } + + private static final int max(int a, int b) { + return (a > b) ? a : b; + } + + // ---------------------------------------------------------------- + // Generic filter-based scanning code + // ---------------------------------------------------------------- + + private static interface Filter { + boolean contains(int codePoint); + } + + private static final VersionInfo NO_VERSION = VersionInfo.getInstance(0, 0, 0, 0); + + private static class VersionFilter implements Filter { + VersionInfo version; + + VersionFilter(VersionInfo version) { + this.version = version; + } + + public boolean contains(int ch) { + VersionInfo v = UCharacter.getAge(ch); + // Reference comparison ok; VersionInfo caches and reuses + // unique objects. + return v != NO_VERSION && v.compareTo(version) <= 0; + } + } + + private static synchronized UnicodeSet getInclusions(int src) { + if (src != UCharacterProperty.SRC_PROPSVEC) { + throw new IllegalStateException("UnicodeSet.getInclusions(unknown src " + src + ")"); + } + + if (INCLUSION == null) { + UnicodeSet incl = new UnicodeSet(); + UCharacterProperty.INSTANCE.upropsvec_addPropertyStarts(incl); + INCLUSION = incl; + } + return INCLUSION; + } + + /** + * Generic filter-based scanning code for UCD property UnicodeSets. + */ + private UnicodeSet applyFilter(Filter filter, int src) { + // 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. + // getInclusions(src) contains exactly the first characters of + // same-value ranges for the given properties "source". + + clear(); + + int startHasProperty = -1; + UnicodeSet inclusions = getInclusions(src); + int limitRange = inclusions.getRangeCount(); + + for (int j = 0; j < limitRange; ++j) { + // get current range + int start = inclusions.getRangeStart(j); + int end = inclusions.getRangeEnd(j); + + // for all the code points in the range, process + for (int ch = start; ch <= end; ++ch) { + // only add to the unicodeset on inflection points -- + // where the hasProperty value changes to false + if (filter.contains(ch)) { + if (startHasProperty < 0) { + startHasProperty = ch; + } + } else if (startHasProperty >= 0) { + add_unchecked(startHasProperty, ch - 1); + startHasProperty = -1; + } + } + } + if (startHasProperty >= 0) { + add_unchecked(startHasProperty, 0x10FFFF); + } + + return this; + } + + /** + * Is this frozen, according to the Freezable interface? + * + * @return value + * @stable ICU 3.8 + */ + public boolean isFrozen() { + return (bmpSet != null || stringSpan != null); + } + + /** + * Freeze this class, according to the Freezable interface. + * + * @return this + * @stable ICU 4.4 + */ + public UnicodeSet freeze() { + if (!isFrozen()) { + // Do most of what compact() does before freezing because + // compact() will not work when the set is frozen. + // Small modification: Don't shrink if the savings would be tiny (<=GROW_EXTRA). + + // Delete buffer first to defragment memory less. + buffer = null; + if (list.length > (len + GROW_EXTRA)) { + // Make the capacity equal to len or 1. + // We don't want to realloc of 0 size. + int capacity = (len == 0) ? 1 : len; + int[] oldList = list; + list = new int[capacity]; + for (int i = capacity; i-- > 0;) { + list[i] = oldList[i]; + } + } + + // Optimize contains() and span() and similar functions. + if (!strings.isEmpty()) { + stringSpan = new UnicodeSetStringSpan(this, new ArrayList(strings), UnicodeSetStringSpan.ALL); + } + if (stringSpan == null || !stringSpan.needsStringSpanUTF16()) { + // Optimize for code point spans. + // There are no strings, or + // all strings are irrelevant for span() etc. because + // all of each string's code points are contained in this set. + // However, fully contained strings are relevant for spanAndCount(), + // so we create both objects. + bmpSet = new BMPSet(list, len); + } + } + return this; + } + + /** + * Span a string using this UnicodeSet. + *

+ * To replace, count elements, or delete spans, see + * {@link com.ibm.icu.text.UnicodeSetSpanner UnicodeSetSpanner}. + * + * @param s The string to be spanned + * @param spanCondition The span condition + * @return the length of the span + * @stable ICU 4.4 + */ + public int span(CharSequence s, SpanCondition spanCondition) { + return span(s, 0, spanCondition); + } + + /** + * Span a string using this UnicodeSet. If the start index is less than 0, span + * will start from 0. If the start index is greater than the string length, span + * returns the string length. + *

+ * To replace, count elements, or delete spans, see + * {@link com.ibm.icu.text.UnicodeSetSpanner UnicodeSetSpanner}. + * + * @param s The string to be spanned + * @param start The start index that the span begins + * @param spanCondition The span condition + * @return the string index which ends the span (i.e. exclusive) + * @stable ICU 4.4 + */ + public int span(CharSequence s, int start, SpanCondition spanCondition) { + int end = s.length(); + if (start < 0) { + start = 0; + } else if (start >= end) { + return end; + } + if (bmpSet != null) { + // Frozen set without strings, or no string is relevant for span(). + return bmpSet.span(s, start, spanCondition, null); + } + if (stringSpan != null) { + return stringSpan.span(s, start, spanCondition); + } else if (!strings.isEmpty()) { + int which = spanCondition == SpanCondition.NOT_CONTAINED ? UnicodeSetStringSpan.FWD_UTF16_NOT_CONTAINED + : UnicodeSetStringSpan.FWD_UTF16_CONTAINED; + UnicodeSetStringSpan strSpan = new UnicodeSetStringSpan(this, new ArrayList(strings), which); + if (strSpan.needsStringSpanUTF16()) { + return strSpan.span(s, start, spanCondition); + } + } + + return spanCodePointsAndCount(s, start, spanCondition, null); + } + + /** + * Same as span() but also counts the smallest number of set elements on any + * path across the span. + *

+ * To replace, count elements, or delete spans, see + * {@link com.ibm.icu.text.UnicodeSetSpanner UnicodeSetSpanner}. + * + * @param outCount An output-only object (must not be null) for returning the + * count. + * @return the limit (exclusive end) of the span + */ + public int spanAndCount(CharSequence s, int start, SpanCondition spanCondition, OutputInt outCount) { + if (outCount == null) { + throw new IllegalArgumentException("outCount must not be null"); + } + int end = s.length(); + if (start < 0) { + start = 0; + } else if (start >= end) { + return end; + } + if (stringSpan != null) { + // We might also have bmpSet != null, + // but fully-contained strings are relevant for counting elements. + return stringSpan.spanAndCount(s, start, spanCondition, outCount); + } else if (bmpSet != null) { + return bmpSet.span(s, start, spanCondition, outCount); + } else if (!strings.isEmpty()) { + int which = spanCondition == SpanCondition.NOT_CONTAINED ? UnicodeSetStringSpan.FWD_UTF16_NOT_CONTAINED + : UnicodeSetStringSpan.FWD_UTF16_CONTAINED; + which |= UnicodeSetStringSpan.WITH_COUNT; + UnicodeSetStringSpan strSpan = new UnicodeSetStringSpan(this, new ArrayList(strings), which); + return strSpan.spanAndCount(s, start, spanCondition, outCount); + } + + return spanCodePointsAndCount(s, start, spanCondition, outCount); + } + + private int spanCodePointsAndCount(CharSequence s, int start, SpanCondition spanCondition, OutputInt outCount) { + // Pin to 0/1 values. + boolean spanContained = (spanCondition != SpanCondition.NOT_CONTAINED); + + int c; + int next = start; + int length = s.length(); + int count = 0; + do { + c = Character.codePointAt(s, next); + if (spanContained != contains(c)) { + break; + } + ++count; + next += Character.charCount(c); + } while (next < length); + if (outCount != null) { + outCount.value = count; + } + return next; + } + + /** + * Span a string backwards (from the fromIndex) using this UnicodeSet. If the + * fromIndex is less than 0, spanBack will return 0. If fromIndex is greater + * than the string length, spanBack will start from the string length. + *

+ * To replace, count elements, or delete spans, see + * {@link com.ibm.icu.text.UnicodeSetSpanner UnicodeSetSpanner}. + * + * @param s The string to be spanned + * @param fromIndex The index of the char (exclusive) that the string should + * be spanned backwards + * @param spanCondition The span condition + * @return The string index which starts the span (i.e. inclusive). + * @stable ICU 4.4 + */ + public int spanBack(CharSequence s, int fromIndex, SpanCondition spanCondition) { + if (fromIndex <= 0) { + return 0; + } + if (fromIndex > s.length()) { + fromIndex = s.length(); + } + if (bmpSet != null) { + // Frozen set without strings, or no string is relevant for spanBack(). + return bmpSet.spanBack(s, fromIndex, spanCondition); + } + if (stringSpan != null) { + return stringSpan.spanBack(s, fromIndex, spanCondition); + } else if (!strings.isEmpty()) { + int which = (spanCondition == SpanCondition.NOT_CONTAINED) ? UnicodeSetStringSpan.BACK_UTF16_NOT_CONTAINED + : UnicodeSetStringSpan.BACK_UTF16_CONTAINED; + UnicodeSetStringSpan strSpan = new UnicodeSetStringSpan(this, new ArrayList(strings), which); + if (strSpan.needsStringSpanUTF16()) { + return strSpan.spanBack(s, fromIndex, spanCondition); + } + } + + // Pin to 0/1 values. + boolean spanContained = (spanCondition != SpanCondition.NOT_CONTAINED); + + int c; + int prev = fromIndex; + do { + c = Character.codePointBefore(s, prev); + if (spanContained != contains(c)) { + break; + } + prev -= Character.charCount(c); + } while (prev > 0); + return prev; + } + + /** + * Clone a thawed version of this class, according to the Freezable interface. + * + * @return the clone, not frozen + * @stable ICU 4.4 + */ + public UnicodeSet cloneAsThawed() { + UnicodeSet result = new UnicodeSet(this); + assert !result.isFrozen(); + return result; + } + + // internal function + private void checkFrozen() { + if (isFrozen()) { + throw new UnsupportedOperationException("Attempt to modify frozen object"); + } + } + + /** + * 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): + *

    + *
  • CONTAINED and SIMPLE work the same. + *
  • CONTAINED and SIMPLE are inverses of NOT_CONTAINED. + *
  • span() and spanBack() partition any string the same way when alternating + * between 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(SIMPLE) may be shorter than 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", CONTAINED) will return 4 + * but span("xyax", SIMPLE) will return 3. span(SIMPLE) will never be longer + * than 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. + * + * @stable ICU 4.4 + */ + public enum SpanCondition { + /** + * 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 4.4 + */ + NOT_CONTAINED, + + /** + * 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 4.4 + */ + CONTAINED, + + /** + * 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 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 4.4 + */ + SIMPLE, + } + +} diff --git a/src/main/java/jdk_internal/icu/util/CodePointMap.java b/src/main/java/jdk_internal/icu/util/CodePointMap.java index e7b0c4ed..14d4e079 100755 --- a/src/main/java/jdk_internal/icu/util/CodePointMap.java +++ b/src/main/java/jdk_internal/icu/util/CodePointMap.java @@ -1,498 +1,498 @@ -/* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -// (c) 2018 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License - -// created: 2018may10 Markus W. Scherer - -package jdk_internal.icu.util; - -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - * Abstract map from Unicode code points (U+0000..U+10FFFF) to integer values. - * This does not implement java.util.Map. - * - * @stable ICU 63 - */ -public abstract class CodePointMap implements Iterable { - /** - * Selectors for how getRange() should report value ranges overlapping with - * surrogates. Most users should use NORMAL. - * - * @see #getRange - * @stable ICU 63 - */ - public enum RangeOption { - /** - * getRange() enumerates all same-value ranges as stored in the map. Most users - * should use this option. - * - * @stable ICU 63 - */ - NORMAL, - /** - * 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 {@link Character#isHighSurrogate}. - * - *

- * Most users should use 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 - */ - FIXED_LEAD_SURROGATES, - /** - * 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 {@link Character#isSurrogate}. - * - *

- * Most users should use 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 - */ - FIXED_ALL_SURROGATES - } - - /** - * Callback function interface: Modifies a map value. Optionally called by - * 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. - * - * @see #getRange - * @see #iterator - * @stable ICU 63 - */ - public interface ValueFilter { - /** - * Modifies the map value. - * - * @param value map value - * @return modified value - * @stable ICU 63 - */ - public int apply(int value); - } - - /** - * Range iteration result data. Code points from start to end map to the same - * value. The value may have been modified by {@link ValueFilter#apply(int)}, or - * it may be the surrogateValue if a RangeOption other than "normal" was used. - * - * @see #getRange - * @see #iterator - * @stable ICU 63 - */ - public static final class Range { - private int start; - private int end; - private int value; - - /** - * Constructor. Sets start and end to -1 and value to 0. - * - * @stable ICU 63 - */ - public Range() { - start = end = -1; - value = 0; - } - - /** - * @return the start code point - * @stable ICU 63 - */ - public int getStart() { - return start; - } - - /** - * @return the (inclusive) end code point - * @stable ICU 63 - */ - public int getEnd() { - return end; - } - - /** - * @return the range value - * @stable ICU 63 - */ - public int getValue() { - return value; - } - - /** - * Sets the range. When using {@link #iterator()}, iteration will resume after - * the newly set end. - * - * @param start new start code point - * @param end new end code point - * @param value new value - * @stable ICU 63 - */ - public void set(int start, int end, int value) { - this.start = start; - this.end = end; - this.value = value; - } - } - - private final class RangeIterator implements Iterator { - private Range range = new Range(); - - @Override - public boolean hasNext() { - return -1 <= range.end && range.end < 0x10ffff; - } - - @Override - public Range next() { - if (getRange(range.end + 1, null, range)) { - return range; - } else { - throw new NoSuchElementException(); - } - } - - @Override - public final void remove() { - throw new UnsupportedOperationException(); - } - } - - /** - * Iterates over code points of a string and fetches map values. This does not - * implement java.util.Iterator. - * - *

-	 * void onString(CodePointMap map, CharSequence s, int start) {
-	 * 	CodePointMap.StringIterator iter = map.stringIterator(s, start);
-	 * 	while (iter.next()) {
-	 * 		int end = iter.getIndex(); // code point from between start and end
-	 * 		useValue(s, start, end, iter.getCodePoint(), iter.getValue());
-	 * 		start = end;
-	 * 	}
-	 * }
-	 * 
- * - *

- * This class is not intended for public subclassing. - * - * @stable ICU 63 - */ - public class StringIterator { - /** - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - protected CharSequence s; - /** - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - protected int sIndex; - /** - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - protected int c; - /** - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - protected int value; - - /** - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - protected StringIterator(CharSequence s, int sIndex) { - this.s = s; - this.sIndex = sIndex; - c = -1; - value = 0; - } - - /** - * Resets the iterator to a new string and/or a new string index. - * - * @param s string to iterate over - * @param sIndex string index where the iteration will start - * @stable ICU 63 - */ - public void reset(CharSequence s, int sIndex) { - this.s = s; - this.sIndex = sIndex; - c = -1; - value = 0; - } - - /** - * Reads the next code point, post-increments the string index, and gets a value - * from the map. Sets an implementation-defined error value if the code point is - * an unpaired surrogate. - * - * @return true if the string index was not yet at the end of the string; - * otherwise the iterator did not advance - * @stable ICU 63 - */ - public boolean next() { - if (sIndex >= s.length()) { - return false; - } - c = Character.codePointAt(s, sIndex); - sIndex += Character.charCount(c); - value = get(c); - return true; - } - - /** - * Reads the previous code point, pre-decrements the string index, and gets a - * value from the map. Sets an implementation-defined error value if the code - * point is an unpaired surrogate. - * - * @return true if the string index was not yet at the start of the string; - * otherwise the iterator did not advance - * @stable ICU 63 - */ - public boolean previous() { - if (sIndex <= 0) { - return false; - } - c = Character.codePointBefore(s, sIndex); - sIndex -= Character.charCount(c); - value = get(c); - return true; - } - - /** - * @return the string index - * @stable ICU 63 - */ - public final int getIndex() { - return sIndex; - } - - /** - * @return the code point - * @stable ICU 63 - */ - public final int getCodePoint() { - return c; - } - - /** - * @return the map value, or an implementation-defined error value if the code - * point is an unpaired surrogate - * @stable ICU 63 - */ - public final int getValue() { - return value; - } - } - - /** - * Protected no-args constructor. - * - * @stable ICU 63 - */ - protected CodePointMap() { - } - - /** - * 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 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 - */ - public abstract int get(int c); - - /** - * Sets the range object to a range of code points beginning with the start - * parameter. The range start is the same as the start input parameter (even if - * there are preceding code points that have the same value). The range end is - * the last code point such that all those from start to there have the same - * value. Returns false if start is not 0..U+10FFFF. 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 may be much slower - * than a data structure that stores ranges directly.) - * - *

- * If the {@link ValueFilter} parameter is not null, then the value to be - * delivered is passed through that filter, 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 parameter is null. - * - *

- * Example: - * - *

-	 * int start = 0;
-	 * CodePointMap.Range range = new CodePointMap.Range();
-	 * while (map.getRange(start, null, range)) {
-	 * 	int end = range.getEnd();
-	 * 	int value = range.getValue();
-	 * 	// Work with the range start..end and its value.
-	 * 	start = end + 1;
-	 * }
-	 * 
- * - * @param start range start - * @param filter an object that may modify the map data value, or null if the - * values from the map are to be used unmodified - * @param range the range object that will be set to the code point range and - * value - * @return true if start is 0..U+10FFFF; otherwise no new range is fetched - * @stable ICU 63 - */ - public abstract boolean getRange(int start, ValueFilter filter, Range range); - - /** - * Sets the range object to a range of code points beginning with the start - * parameter. The range start is the same as the start input parameter (even if - * there are preceding code points that have the same value). The range end is - * the last code point such that all those from start to there have the same - * value. Returns false if start is not 0..U+10FFFF. - * - *

- * Same as the simpler {@link #getRange(int, ValueFilter, Range)} but optionally - * modifies the range if it overlaps with surrogate code points. - * - * @param start range start - * @param option defines whether surrogates are treated normally, or as - * having the surrogateValue; usually - * {@link RangeOption#NORMAL} - * @param surrogateValue value for surrogates; ignored if - * option=={@link RangeOption#NORMAL} - * @param filter an object that may modify the map data value, or null - * if the values from the map are to be used unmodified - * @param range the range object that will be set to the code point - * range and value - * @return true if start is 0..U+10FFFF; otherwise no new range is fetched - * @stable ICU 63 - */ - public boolean getRange(int start, RangeOption option, int surrogateValue, ValueFilter filter, Range range) { - assert option != null; - if (!getRange(start, filter, range)) { - return false; - } - if (option == RangeOption.NORMAL) { - return true; - } - int surrEnd = option == RangeOption.FIXED_ALL_SURROGATES ? 0xdfff : 0xdbff; - int end = range.end; - if (end < 0xd7ff || start > surrEnd) { - return true; - } - // The range overlaps with surrogates, or ends just before the first one. - if (range.value == surrogateValue) { - if (end >= surrEnd) { - // Surrogates followed by a non-surrValue range, - // or surrogates are part of a larger surrValue range. - return true; - } - } else { - if (start <= 0xd7ff) { - range.end = 0xd7ff; // Non-surrValue range ends before surrValue surrogates. - return true; - } - // Start is a surrogate with a non-surrValue code *unit* value. - // Return a surrValue code *point* range. - range.value = surrogateValue; - if (end > surrEnd) { - range.end = surrEnd; // Surrogate range ends before non-surrValue rest of range. - return true; - } - } - // See if the surrValue surrogate range can be merged with - // an immediately following range. - if (getRange(surrEnd + 1, filter, range) && range.value == surrogateValue) { - range.start = start; - return true; - } - range.start = start; - range.end = surrEnd; - range.value = surrogateValue; - return true; - } - - /** - * Convenience iterator over same-map-value code point ranges. Same as looping - * over all ranges with {@link #getRange(int, ValueFilter, Range)} without - * filtering. Adjacent ranges have different map values. - * - *

- * The iterator always returns the same Range object. - * - * @return a Range iterator - * @stable ICU 63 - */ - @Override - public Iterator iterator() { - return new RangeIterator(); - } - - /** - * Returns an iterator (not a java.util.Iterator) over code points of a string - * for fetching map values. - * - * @param s string to iterate over - * @param sIndex string index where the iteration will start - * @return the iterator - * @stable ICU 63 - */ - public StringIterator stringIterator(CharSequence s, int sIndex) { - return new StringIterator(s, sIndex); - } -} +/* + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +// (c) 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html#License + +// created: 2018may10 Markus W. Scherer + +package jdk_internal.icu.util; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * Abstract map from Unicode code points (U+0000..U+10FFFF) to integer values. + * This does not implement java.util.Map. + * + * @stable ICU 63 + */ +public abstract class CodePointMap implements Iterable { + /** + * Selectors for how getRange() should report value ranges overlapping with + * surrogates. Most users should use NORMAL. + * + * @see #getRange + * @stable ICU 63 + */ + public enum RangeOption { + /** + * getRange() enumerates all same-value ranges as stored in the map. Most users + * should use this option. + * + * @stable ICU 63 + */ + NORMAL, + /** + * 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 {@link Character#isHighSurrogate}. + * + *

+ * Most users should use 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 + */ + FIXED_LEAD_SURROGATES, + /** + * 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 {@link Character#isSurrogate}. + * + *

+ * Most users should use 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 + */ + FIXED_ALL_SURROGATES + } + + /** + * Callback function interface: Modifies a map value. Optionally called by + * 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. + * + * @see #getRange + * @see #iterator + * @stable ICU 63 + */ + public interface ValueFilter { + /** + * Modifies the map value. + * + * @param value map value + * @return modified value + * @stable ICU 63 + */ + public int apply(int value); + } + + /** + * Range iteration result data. Code points from start to end map to the same + * value. The value may have been modified by {@link ValueFilter#apply(int)}, or + * it may be the surrogateValue if a RangeOption other than "normal" was used. + * + * @see #getRange + * @see #iterator + * @stable ICU 63 + */ + public static final class Range { + private int start; + private int end; + private int value; + + /** + * Constructor. Sets start and end to -1 and value to 0. + * + * @stable ICU 63 + */ + public Range() { + start = end = -1; + value = 0; + } + + /** + * @return the start code point + * @stable ICU 63 + */ + public int getStart() { + return start; + } + + /** + * @return the (inclusive) end code point + * @stable ICU 63 + */ + public int getEnd() { + return end; + } + + /** + * @return the range value + * @stable ICU 63 + */ + public int getValue() { + return value; + } + + /** + * Sets the range. When using {@link #iterator()}, iteration will resume after + * the newly set end. + * + * @param start new start code point + * @param end new end code point + * @param value new value + * @stable ICU 63 + */ + public void set(int start, int end, int value) { + this.start = start; + this.end = end; + this.value = value; + } + } + + private final class RangeIterator implements Iterator { + private Range range = new Range(); + + @Override + public boolean hasNext() { + return -1 <= range.end && range.end < 0x10ffff; + } + + @Override + public Range next() { + if (getRange(range.end + 1, null, range)) { + return range; + } else { + throw new NoSuchElementException(); + } + } + + @Override + public final void remove() { + throw new UnsupportedOperationException(); + } + } + + /** + * Iterates over code points of a string and fetches map values. This does not + * implement java.util.Iterator. + * + *

+	 * void onString(CodePointMap map, CharSequence s, int start) {
+	 * 	CodePointMap.StringIterator iter = map.stringIterator(s, start);
+	 * 	while (iter.next()) {
+	 * 		int end = iter.getIndex(); // code point from between start and end
+	 * 		useValue(s, start, end, iter.getCodePoint(), iter.getValue());
+	 * 		start = end;
+	 * 	}
+	 * }
+	 * 
+ * + *

+ * This class is not intended for public subclassing. + * + * @stable ICU 63 + */ + public class StringIterator { + /** + * @internal + * @deprecated This API is ICU internal only. + */ + @Deprecated + protected CharSequence s; + /** + * @internal + * @deprecated This API is ICU internal only. + */ + @Deprecated + protected int sIndex; + /** + * @internal + * @deprecated This API is ICU internal only. + */ + @Deprecated + protected int c; + /** + * @internal + * @deprecated This API is ICU internal only. + */ + @Deprecated + protected int value; + + /** + * @internal + * @deprecated This API is ICU internal only. + */ + @Deprecated + protected StringIterator(CharSequence s, int sIndex) { + this.s = s; + this.sIndex = sIndex; + c = -1; + value = 0; + } + + /** + * Resets the iterator to a new string and/or a new string index. + * + * @param s string to iterate over + * @param sIndex string index where the iteration will start + * @stable ICU 63 + */ + public void reset(CharSequence s, int sIndex) { + this.s = s; + this.sIndex = sIndex; + c = -1; + value = 0; + } + + /** + * Reads the next code point, post-increments the string index, and gets a value + * from the map. Sets an implementation-defined error value if the code point is + * an unpaired surrogate. + * + * @return true if the string index was not yet at the end of the string; + * otherwise the iterator did not advance + * @stable ICU 63 + */ + public boolean next() { + if (sIndex >= s.length()) { + return false; + } + c = Character.codePointAt(s, sIndex); + sIndex += Character.charCount(c); + value = get(c); + return true; + } + + /** + * Reads the previous code point, pre-decrements the string index, and gets a + * value from the map. Sets an implementation-defined error value if the code + * point is an unpaired surrogate. + * + * @return true if the string index was not yet at the start of the string; + * otherwise the iterator did not advance + * @stable ICU 63 + */ + public boolean previous() { + if (sIndex <= 0) { + return false; + } + c = Character.codePointBefore(s, sIndex); + sIndex -= Character.charCount(c); + value = get(c); + return true; + } + + /** + * @return the string index + * @stable ICU 63 + */ + public final int getIndex() { + return sIndex; + } + + /** + * @return the code point + * @stable ICU 63 + */ + public final int getCodePoint() { + return c; + } + + /** + * @return the map value, or an implementation-defined error value if the code + * point is an unpaired surrogate + * @stable ICU 63 + */ + public final int getValue() { + return value; + } + } + + /** + * Protected no-args constructor. + * + * @stable ICU 63 + */ + protected CodePointMap() { + } + + /** + * 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 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 + */ + public abstract int get(int c); + + /** + * Sets the range object to a range of code points beginning with the start + * parameter. The range start is the same as the start input parameter (even if + * there are preceding code points that have the same value). The range end is + * the last code point such that all those from start to there have the same + * value. Returns false if start is not 0..U+10FFFF. 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 may be much slower + * than a data structure that stores ranges directly.) + * + *

+ * If the {@link ValueFilter} parameter is not null, then the value to be + * delivered is passed through that filter, 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 parameter is null. + * + *

+ * Example: + * + *

+	 * int start = 0;
+	 * CodePointMap.Range range = new CodePointMap.Range();
+	 * while (map.getRange(start, null, range)) {
+	 * 	int end = range.getEnd();
+	 * 	int value = range.getValue();
+	 * 	// Work with the range start..end and its value.
+	 * 	start = end + 1;
+	 * }
+	 * 
+ * + * @param start range start + * @param filter an object that may modify the map data value, or null if the + * values from the map are to be used unmodified + * @param range the range object that will be set to the code point range and + * value + * @return true if start is 0..U+10FFFF; otherwise no new range is fetched + * @stable ICU 63 + */ + public abstract boolean getRange(int start, ValueFilter filter, Range range); + + /** + * Sets the range object to a range of code points beginning with the start + * parameter. The range start is the same as the start input parameter (even if + * there are preceding code points that have the same value). The range end is + * the last code point such that all those from start to there have the same + * value. Returns false if start is not 0..U+10FFFF. + * + *

+ * Same as the simpler {@link #getRange(int, ValueFilter, Range)} but optionally + * modifies the range if it overlaps with surrogate code points. + * + * @param start range start + * @param option defines whether surrogates are treated normally, or as + * having the surrogateValue; usually + * {@link RangeOption#NORMAL} + * @param surrogateValue value for surrogates; ignored if + * option=={@link RangeOption#NORMAL} + * @param filter an object that may modify the map data value, or null + * if the values from the map are to be used unmodified + * @param range the range object that will be set to the code point + * range and value + * @return true if start is 0..U+10FFFF; otherwise no new range is fetched + * @stable ICU 63 + */ + public boolean getRange(int start, RangeOption option, int surrogateValue, ValueFilter filter, Range range) { + assert option != null; + if (!getRange(start, filter, range)) { + return false; + } + if (option == RangeOption.NORMAL) { + return true; + } + int surrEnd = option == RangeOption.FIXED_ALL_SURROGATES ? 0xdfff : 0xdbff; + int end = range.end; + if (end < 0xd7ff || start > surrEnd) { + return true; + } + // The range overlaps with surrogates, or ends just before the first one. + if (range.value == surrogateValue) { + if (end >= surrEnd) { + // Surrogates followed by a non-surrValue range, + // or surrogates are part of a larger surrValue range. + return true; + } + } else { + if (start <= 0xd7ff) { + range.end = 0xd7ff; // Non-surrValue range ends before surrValue surrogates. + return true; + } + // Start is a surrogate with a non-surrValue code *unit* value. + // Return a surrValue code *point* range. + range.value = surrogateValue; + if (end > surrEnd) { + range.end = surrEnd; // Surrogate range ends before non-surrValue rest of range. + return true; + } + } + // See if the surrValue surrogate range can be merged with + // an immediately following range. + if (getRange(surrEnd + 1, filter, range) && range.value == surrogateValue) { + range.start = start; + return true; + } + range.start = start; + range.end = surrEnd; + range.value = surrogateValue; + return true; + } + + /** + * Convenience iterator over same-map-value code point ranges. Same as looping + * over all ranges with {@link #getRange(int, ValueFilter, Range)} without + * filtering. Adjacent ranges have different map values. + * + *

+ * The iterator always returns the same Range object. + * + * @return a Range iterator + * @stable ICU 63 + */ + @Override + public Iterator iterator() { + return new RangeIterator(); + } + + /** + * Returns an iterator (not a java.util.Iterator) over code points of a string + * for fetching map values. + * + * @param s string to iterate over + * @param sIndex string index where the iteration will start + * @return the iterator + * @stable ICU 63 + */ + public StringIterator stringIterator(CharSequence s, int sIndex) { + return new StringIterator(s, sIndex); + } +} diff --git a/src/main/java/jdk_internal/icu/util/CodePointTrie.java b/src/main/java/jdk_internal/icu/util/CodePointTrie.java index 30f1bb76..65e4afd6 100755 --- a/src/main/java/jdk_internal/icu/util/CodePointTrie.java +++ b/src/main/java/jdk_internal/icu/util/CodePointTrie.java @@ -1,1357 +1,1357 @@ -/* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -// (c) 2018 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License - -// created: 2018may04 Markus W. Scherer - -package jdk_internal.icu.util; - -import static jdk_internal.icu.impl.NormalizerImpl.UTF16Plus; - -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import jdk_internal.icu.impl.ICUBinary; - -/** - * Immutable Unicode code point trie. Fast, reasonably compact, map from Unicode - * code points (U+0000..U+10FFFF) to integer values. For details see - * http://site.icu-project.org/design/struct/utrie - * - *

- * This class is not intended for public subclassing. - * - * @see MutableCodePointTrie - * @stable ICU 63 - */ -@SuppressWarnings("deprecation") -public abstract class CodePointTrie extends CodePointMap { - /** - * Selectors for the type of a CodePointTrie. Different trade-offs for size vs. - * speed. - * - *

- * Use null for {@link #fromBinary} to accept any type; {@link #getType} will - * return the actual type. - * - * @see MutableCodePointTrie#buildImmutable(CodePointTrie.Type, - * CodePointTrie.ValueWidth) - * @see #fromBinary - * @see #getType - * @stable ICU 63 - */ - public enum Type { - /** - * Fast/simple/larger BMP data structure. The {@link Fast} subclasses have - * additional functions for lookup for BMP and supplementary code points. - * - * @see Fast - * @stable ICU 63 - */ - FAST, - /** - * Small/slower BMP data structure. - * - * @see Small - * @stable ICU 63 - */ - SMALL - } - - /** - * Selectors for the number of bits in a CodePointTrie data value. - * - *

- * Use null for {@link #fromBinary} to accept any data value width; - * {@link #getValueWidth} will return the actual data value width. - * - * @stable ICU 63 - */ - public enum ValueWidth { - /** - * The trie stores 16 bits per data value. It returns them as unsigned values - * 0..0xffff=65535. - * - * @stable ICU 63 - */ - BITS_16, - /** - * The trie stores 32 bits per data value. - * - * @stable ICU 63 - */ - BITS_32, - /** - * The trie stores 8 bits per data value. It returns them as unsigned values - * 0..0xff=255. - * - * @stable ICU 63 - */ - BITS_8 - } - - private CodePointTrie(char[] index, Data data, int highStart, int index3NullOffset, int dataNullOffset) { - this.ascii = new int[ASCII_LIMIT]; - this.index = index; - this.data = data; - this.dataLength = data.getDataLength(); - this.highStart = highStart; - this.index3NullOffset = index3NullOffset; - this.dataNullOffset = dataNullOffset; - - for (int c = 0; c < ASCII_LIMIT; ++c) { - ascii[c] = data.getFromIndex(c); - } - - int nullValueOffset = dataNullOffset; - if (nullValueOffset >= dataLength) { - nullValueOffset = dataLength - HIGH_VALUE_NEG_DATA_OFFSET; - } - nullValue = data.getFromIndex(nullValueOffset); - } - - /** - * Creates a trie from its binary form, stored in the ByteBuffer starting at the - * current position. Advances the buffer position to just after the trie data. - * Inverse of {@link #toBinary(OutputStream)}. - * - *

- * The data is copied from the buffer; later modification of the buffer will not - * affect the trie. - * - * @param type selects the trie type; this method throws an exception if - * the type does not match the binary data; use null to accept - * any type - * @param valueWidth selects the number of bits in a data value; this method - * throws an exception if the valueWidth does not match the - * binary data; use null to accept any data value width - * @param bytes a buffer containing the binary data of a CodePointTrie - * @return the trie - * @see MutableCodePointTrie#MutableCodePointTrie(int, int) - * @see MutableCodePointTrie#buildImmutable(CodePointTrie.Type, - * CodePointTrie.ValueWidth) - * @see #toBinary(OutputStream) - * @stable ICU 63 - */ - public static CodePointTrie fromBinary(Type type, ValueWidth valueWidth, ByteBuffer bytes) { - ByteOrder outerByteOrder = bytes.order(); - try { - // Enough data for a trie header? - if (bytes.remaining() < 16 /* sizeof(UCPTrieHeader) */) { - throw new InternalError("Buffer too short for a CodePointTrie header"); - } - - // struct UCPTrieHeader - /** "Tri3" in big-endian US-ASCII (0x54726933) */ - int signature = bytes.getInt(); - - // Check the signature. - switch (signature) { - case 0x54726933: - // The buffer is already set to the trie data byte order. - break; - case 0x33697254: - // Temporarily reverse the byte order. - boolean isBigEndian = outerByteOrder == ByteOrder.BIG_ENDIAN; - bytes.order(isBigEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); - signature = 0x54726933; - break; - default: - throw new InternalError("Buffer does not contain a serialized CodePointTrie"); - } - - // struct UCPTrieHeader continued - /** - * 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 - */ - int options = bytes.getChar(); - - /** Total length of the index tables. */ - int indexLength = bytes.getChar(); - - /** Data length bits 15..0. */ - int dataLength = bytes.getChar(); - - /** Index-3 null block offset, 0x7fff or 0xffff if none. */ - int index3NullOffset = bytes.getChar(); - - /** Data null block offset bits 15..0, 0xfffff if none. */ - int dataNullOffset = bytes.getChar(); - - /** - * First code point of the single-value range ending with U+10ffff, rounded up - * and then shifted right by SHIFT_2. - */ - int shiftedHighStart = bytes.getChar(); - // struct UCPTrieHeader end - - int typeInt = (options >> 6) & 3; - Type actualType; - switch (typeInt) { - case 0: - actualType = Type.FAST; - break; - case 1: - actualType = Type.SMALL; - break; - default: - throw new InternalError("CodePointTrie data header has an unsupported type"); - } - - int valueWidthInt = options & OPTIONS_VALUE_BITS_MASK; - ValueWidth actualValueWidth; - switch (valueWidthInt) { - case 0: - actualValueWidth = ValueWidth.BITS_16; - break; - case 1: - actualValueWidth = ValueWidth.BITS_32; - break; - case 2: - actualValueWidth = ValueWidth.BITS_8; - break; - default: - throw new InternalError("CodePointTrie data header has an unsupported value width"); - } - - if ((options & OPTIONS_RESERVED_MASK) != 0) { - throw new InternalError("CodePointTrie data header has unsupported options"); - } - - if (type == null) { - type = actualType; - } - if (valueWidth == null) { - valueWidth = actualValueWidth; - } - if (type != actualType || valueWidth != actualValueWidth) { - throw new InternalError("CodePointTrie data header has a different type or value width than required"); - } - - // Get the length values and offsets. - dataLength |= ((options & OPTIONS_DATA_LENGTH_MASK) << 4); - dataNullOffset |= ((options & OPTIONS_DATA_NULL_OFFSET_MASK) << 8); - - int highStart = shiftedHighStart << SHIFT_2; - - // Calculate the actual length, minus the header. - int actualLength = indexLength * 2; - if (valueWidth == ValueWidth.BITS_16) { - actualLength += dataLength * 2; - } else if (valueWidth == ValueWidth.BITS_32) { - actualLength += dataLength * 4; - } else { - actualLength += dataLength; - } - if (bytes.remaining() < actualLength) { - throw new InternalError("Buffer too short for the CodePointTrie data"); - } - - char[] index = ICUBinary.getChars(bytes, indexLength, 0); - switch (valueWidth) { - case BITS_16: { - char[] data16 = ICUBinary.getChars(bytes, dataLength, 0); - return type == Type.FAST ? new Fast16(index, data16, highStart, index3NullOffset, dataNullOffset) - : new Small16(index, data16, highStart, index3NullOffset, dataNullOffset); - } - case BITS_32: { - int[] data32 = ICUBinary.getInts(bytes, dataLength, 0); - return type == Type.FAST ? new Fast32(index, data32, highStart, index3NullOffset, dataNullOffset) - : new Small32(index, data32, highStart, index3NullOffset, dataNullOffset); - } - case BITS_8: { - byte[] data8 = ICUBinary.getBytes(bytes, dataLength, 0); - return type == Type.FAST ? new Fast8(index, data8, highStart, index3NullOffset, dataNullOffset) - : new Small8(index, data8, highStart, index3NullOffset, dataNullOffset); - } - default: - throw new AssertionError("should be unreachable"); - } - } finally { - bytes.order(outerByteOrder); - } - } - - /** - * Returns the trie type. - * - * @return the trie type - * @stable ICU 63 - */ - public abstract Type getType(); - - /** - * Returns the number of bits in a trie data value. - * - * @return the number of bits in a trie data value - * @stable ICU 63 - */ - public final ValueWidth getValueWidth() { - return data.getValueWidth(); - } - - /** - * {@inheritDoc} - * - * @stable ICU 63 - */ - @Override - public int get(int c) { - return data.getFromIndex(cpIndex(c)); - } - - /** - * Returns a trie value for an ASCII code point, without range checking. - * - * @param c the input code point; must be U+0000..U+007F - * @return The ASCII code point's trie value. - * @stable ICU 63 - */ - public final int asciiGet(int c) { - return ascii[c]; - } - - private static final int MAX_UNICODE = 0x10ffff; - - private static final int ASCII_LIMIT = 0x80; - - private static final int maybeFilterValue(int value, int trieNullValue, int nullValue, ValueFilter filter) { - if (value == trieNullValue) { - value = nullValue; - } else if (filter != null) { - value = filter.apply(value); - } - return value; - } - - /** - * {@inheritDoc} - * - * @stable ICU 63 - */ - @Override - public final boolean getRange(int start, ValueFilter filter, Range range) { - if (start < 0 || MAX_UNICODE < start) { - return false; - } - if (start >= highStart) { - int di = dataLength - HIGH_VALUE_NEG_DATA_OFFSET; - int value = data.getFromIndex(di); - if (filter != null) { - value = filter.apply(value); - } - range.set(start, MAX_UNICODE, value); - return true; - } - - int nullValue = this.nullValue; - if (filter != null) { - nullValue = filter.apply(nullValue); - } - Type type = getType(); - - int prevI3Block = -1; - int prevBlock = -1; - int c = start; - // Initialize to make compiler happy. Real value when haveValue is true. - int trieValue = 0, value = 0; - boolean haveValue = false; - do { - int i3Block; - int i3; - int i3BlockLength; - int dataBlockLength; - if (c <= 0xffff && (type == Type.FAST || c <= SMALL_MAX)) { - i3Block = 0; - i3 = c >> FAST_SHIFT; - i3BlockLength = type == Type.FAST ? BMP_INDEX_LENGTH : SMALL_INDEX_LENGTH; - dataBlockLength = FAST_DATA_BLOCK_LENGTH; - } else { - // Use the multi-stage index. - int i1 = c >> SHIFT_1; - if (type == Type.FAST) { - assert (0xffff < c && c < highStart); - i1 += BMP_INDEX_LENGTH - OMITTED_BMP_INDEX_1_LENGTH; - } else { - assert (c < highStart && highStart > SMALL_LIMIT); - i1 += SMALL_INDEX_LENGTH; - } - i3Block = index[index[i1] + ((c >> SHIFT_2) & INDEX_2_MASK)]; - if (i3Block == prevI3Block && (c - start) >= CP_PER_INDEX_2_ENTRY) { - // The index-3 block is the same as the previous one, and filled with value. - assert ((c & (CP_PER_INDEX_2_ENTRY - 1)) == 0); - c += CP_PER_INDEX_2_ENTRY; - continue; - } - prevI3Block = i3Block; - if (i3Block == index3NullOffset) { - // This is the index-3 null block. - if (haveValue) { - if (nullValue != value) { - range.set(start, c - 1, value); - return true; - } - } else { - trieValue = this.nullValue; - value = nullValue; - haveValue = true; - } - prevBlock = dataNullOffset; - c = (c + CP_PER_INDEX_2_ENTRY) & ~(CP_PER_INDEX_2_ENTRY - 1); - continue; - } - i3 = (c >> SHIFT_3) & INDEX_3_MASK; - i3BlockLength = INDEX_3_BLOCK_LENGTH; - dataBlockLength = SMALL_DATA_BLOCK_LENGTH; - } - // Enumerate data blocks for one index-3 block. - do { - int block; - if ((i3Block & 0x8000) == 0) { - block = index[i3Block + i3]; - } else { - // 18-bit indexes stored in groups of 9 entries per 8 indexes. - int group = (i3Block & 0x7fff) + (i3 & ~7) + (i3 >> 3); - int gi = i3 & 7; - block = (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. - assert ((c & (dataBlockLength - 1)) == 0); - c += dataBlockLength; - } else { - int dataMask = dataBlockLength - 1; - prevBlock = block; - if (block == dataNullOffset) { - // This is the data null block. - if (haveValue) { - if (nullValue != value) { - range.set(start, c - 1, value); - return true; - } - } else { - trieValue = this.nullValue; - value = nullValue; - haveValue = true; - } - c = (c + dataBlockLength) & ~dataMask; - } else { - int di = block + (c & dataMask); - int trieValue2 = data.getFromIndex(di); - if (haveValue) { - if (trieValue2 != trieValue) { - if (filter == null - || maybeFilterValue(trieValue2, this.nullValue, nullValue, filter) != value) { - range.set(start, c - 1, value); - return true; - } - trieValue = trieValue2; // may or may not help - } - } else { - trieValue = trieValue2; - value = maybeFilterValue(trieValue2, this.nullValue, nullValue, filter); - haveValue = true; - } - while ((++c & dataMask) != 0) { - trieValue2 = data.getFromIndex(++di); - if (trieValue2 != trieValue) { - if (filter == null - || maybeFilterValue(trieValue2, this.nullValue, nullValue, filter) != value) { - range.set(start, c - 1, value); - return true; - } - trieValue = trieValue2; // may or may not help - } - } - } - } - } while (++i3 < i3BlockLength); - } while (c < highStart); - assert (haveValue); - int di = dataLength - HIGH_VALUE_NEG_DATA_OFFSET; - int highValue = data.getFromIndex(di); - if (maybeFilterValue(highValue, this.nullValue, nullValue, filter) != value) { - --c; - } else { - c = MAX_UNICODE; - } - range.set(start, c, value); - return true; - } - - /** - * Writes a representation of the trie to the output stream. Inverse of - * {@link #fromBinary}. - * - * @param os the output stream - * @return the number of bytes written - * @stable ICU 63 - */ - public final int toBinary(OutputStream os) { - try { - DataOutputStream dos = new DataOutputStream(os); - - // Write the UCPTrieHeader - dos.writeInt(0x54726933); // signature="Tri3" - dos.writeChar( // options - ((dataLength & 0xf0000) >> 4) | ((dataNullOffset & 0xf0000) >> 8) | (getType().ordinal() << 6) - | getValueWidth().ordinal()); - dos.writeChar(index.length); - dos.writeChar(dataLength); - dos.writeChar(index3NullOffset); - dos.writeChar(dataNullOffset); - dos.writeChar(highStart >> SHIFT_2); // shiftedHighStart - int length = 16; // sizeof(UCPTrieHeader) - - for (char i : index) { - dos.writeChar(i); - } - length += index.length * 2; - length += data.write(dos); - return length; - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - /** @internal */ - static final int FAST_SHIFT = 6; - - /** - * Number of entries in a data block for code points below the fast limit. - * 64=0x40 @internal - */ - static final int FAST_DATA_BLOCK_LENGTH = 1 << FAST_SHIFT; - - /** - * Mask for getting the lower bits for the in-fast-data-block offset. @internal - */ - private static final int FAST_DATA_MASK = FAST_DATA_BLOCK_LENGTH - 1; - - /** @internal */ - private static final int 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 - */ - private static final int ERROR_VALUE_NEG_DATA_OFFSET = 1; - /** - * Offset from dataLength (to be subtracted) for fetching the value returned for - * code points highStart..U+10FFFF. - * - * @internal - */ - private static final int HIGH_VALUE_NEG_DATA_OFFSET = 2; - - // ucptrie_impl.h - - /** The length of the BMP index table. 1024=0x400 */ - private static final int BMP_INDEX_LENGTH = 0x10000 >> FAST_SHIFT; - - static final int SMALL_LIMIT = 0x1000; - private static final int SMALL_INDEX_LENGTH = SMALL_LIMIT >> FAST_SHIFT; - - /** Shift size for getting the index-3 table offset. */ - static final int SHIFT_3 = 4; - - /** Shift size for getting the index-2 table offset. */ - private static final int SHIFT_2 = 5 + SHIFT_3; - - /** Shift size for getting the index-1 table offset. */ - private static final int SHIFT_1 = 5 + SHIFT_2; - - /** - * Difference between two shift sizes, for getting an index-2 offset from an - * index-3 offset. 5=9-4 - */ - static final int SHIFT_2_3 = SHIFT_2 - SHIFT_3; - - /** - * Difference between two shift sizes, for getting an index-1 offset from an - * index-2 offset. 5=14-9 - */ - static final int SHIFT_1_2 = SHIFT_1 - SHIFT_2; - - /** - * Number of index-1 entries for the BMP. (4) This part of the index-1 table is - * omitted from the serialized form. - */ - private static final int OMITTED_BMP_INDEX_1_LENGTH = 0x10000 >> SHIFT_1; - - /** Number of entries in an index-2 block. 32=0x20 */ - static final int INDEX_2_BLOCK_LENGTH = 1 << SHIFT_1_2; - - /** Mask for getting the lower bits for the in-index-2-block offset. */ - static final int INDEX_2_MASK = INDEX_2_BLOCK_LENGTH - 1; - - /** Number of code points per index-2 table entry. 512=0x200 */ - static final int CP_PER_INDEX_2_ENTRY = 1 << SHIFT_2; - - /** Number of entries in an index-3 block. 32=0x20 */ - static final int INDEX_3_BLOCK_LENGTH = 1 << SHIFT_2_3; - - /** Mask for getting the lower bits for the in-index-3-block offset. */ - private static final int INDEX_3_MASK = INDEX_3_BLOCK_LENGTH - 1; - - /** Number of entries in a small data block. 16=0x10 */ - static final int SMALL_DATA_BLOCK_LENGTH = 1 << SHIFT_3; - - /** Mask for getting the lower bits for the in-small-data-block offset. */ - static final int SMALL_DATA_MASK = SMALL_DATA_BLOCK_LENGTH - 1; - - // ucptrie_impl.h: Constants for use with UCPTrieHeader.options. - private static final int OPTIONS_DATA_LENGTH_MASK = 0xf000; - private static final int OPTIONS_DATA_NULL_OFFSET_MASK = 0xf00; - private static final int OPTIONS_RESERVED_MASK = 0x38; - private static final int 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. - */ - static final int NO_INDEX3_NULL_OFFSET = 0x7fff; - static final int NO_DATA_NULL_OFFSET = 0xfffff; - - private static abstract class Data { - abstract ValueWidth getValueWidth(); - - abstract int getDataLength(); - - abstract int getFromIndex(int index); - - abstract int write(DataOutputStream dos) throws IOException; - } - - private static final class Data16 extends Data { - char[] array; - - Data16(char[] a) { - array = a; - } - - @Override - ValueWidth getValueWidth() { - return ValueWidth.BITS_16; - } - - @Override - int getDataLength() { - return array.length; - } - - @Override - int getFromIndex(int index) { - return array[index]; - } - - @Override - int write(DataOutputStream dos) throws IOException { - for (char v : array) { - dos.writeChar(v); - } - return array.length * 2; - } - } - - private static final class Data32 extends Data { - int[] array; - - Data32(int[] a) { - array = a; - } - - @Override - ValueWidth getValueWidth() { - return ValueWidth.BITS_32; - } - - @Override - int getDataLength() { - return array.length; - } - - @Override - int getFromIndex(int index) { - return array[index]; - } - - @Override - int write(DataOutputStream dos) throws IOException { - for (int v : array) { - dos.writeInt(v); - } - return array.length * 4; - } - } - - private static final class Data8 extends Data { - byte[] array; - - Data8(byte[] a) { - array = a; - } - - @Override - ValueWidth getValueWidth() { - return ValueWidth.BITS_8; - } - - @Override - int getDataLength() { - return array.length; - } - - @Override - int getFromIndex(int index) { - return array[index] & 0xff; - } - - @Override - int write(DataOutputStream dos) throws IOException { - for (byte v : array) { - dos.writeByte(v); - } - return array.length; - } - } - - /** @internal */ - private final int[] ascii; - - /** @internal */ - private final char[] index; - - /** - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - protected final Data data; - /** - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - protected final int dataLength; - /** - * Start of the last range which ends at U+10FFFF. - * - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - protected final int highStart; - - /** - * 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 - */ - private final int 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 - */ - private final int dataNullOffset; - /** @internal */ - private final int nullValue; - - /** - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - protected final int fastIndex(int c) { - return index[c >> FAST_SHIFT] + (c & FAST_DATA_MASK); - } - - /** - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - protected final int smallIndex(Type type, int c) { - // Split into two methods to make this part inline-friendly. - // In C, this part is a macro. - if (c >= highStart) { - return dataLength - HIGH_VALUE_NEG_DATA_OFFSET; - } - return internalSmallIndex(type, c); - } - - private final int internalSmallIndex(Type type, int c) { - int i1 = c >> SHIFT_1; - if (type == Type.FAST) { - assert (0xffff < c && c < highStart); - i1 += BMP_INDEX_LENGTH - OMITTED_BMP_INDEX_1_LENGTH; - } else { - assert (0 <= c && c < highStart && highStart > SMALL_LIMIT); - i1 += SMALL_INDEX_LENGTH; - } - int i3Block = index[index[i1] + ((c >> SHIFT_2) & INDEX_2_MASK)]; - int i3 = (c >> SHIFT_3) & INDEX_3_MASK; - int dataBlock; - if ((i3Block & 0x8000) == 0) { - // 16-bit indexes - dataBlock = 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 = (index[i3Block++] << (2 + (2 * i3))) & 0x30000; - dataBlock |= index[i3Block + i3]; - } - return dataBlock + (c & SMALL_DATA_MASK); - } - - /** - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - protected abstract int cpIndex(int c); - - /** - * A CodePointTrie with {@link Type#FAST}. - * - * @stable ICU 63 - */ - public static abstract class Fast extends CodePointTrie { - private Fast(char[] index, Data data, int highStart, int index3NullOffset, int dataNullOffset) { - super(index, data, highStart, index3NullOffset, dataNullOffset); - } - - /** - * Creates a trie from its binary form. Same as - * {@link CodePointTrie#fromBinary(Type, ValueWidth, ByteBuffer)} with - * {@link Type#FAST}. - * - * @param valueWidth selects the number of bits in a data value; this method - * throws an exception if the valueWidth does not match the - * binary data; use null to accept any data value width - * @param bytes a buffer containing the binary data of a CodePointTrie - * @return the trie - * @stable ICU 63 - */ - public static Fast fromBinary(ValueWidth valueWidth, ByteBuffer bytes) { - return (Fast) CodePointTrie.fromBinary(Type.FAST, valueWidth, bytes); - } - - /** - * @return {@link Type#FAST} - * @stable ICU 63 - */ - @Override - public final Type getType() { - return Type.FAST; - } - - /** - * 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 c the input code point, must be U+0000..U+FFFF - * @return The BMP code point's trie value. - * @stable ICU 63 - */ - public abstract int bmpGet(int c); - - /** - * Returns a trie value for a supplementary code point (U+10000..U+10FFFF), - * without range checking. - * - * @param c the input code point, must be U+10000..U+10FFFF - * @return The supplementary code point's trie value. - * @stable ICU 63 - */ - public abstract int suppGet(int c); - - /** - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - @Override - protected final int cpIndex(int c) { - if (c >= 0) { - if (c <= 0xffff) { - return fastIndex(c); - } else if (c <= 0x10ffff) { - return smallIndex(Type.FAST, c); - } - } - return dataLength - ERROR_VALUE_NEG_DATA_OFFSET; - } - - /** - * {@inheritDoc} - * - * @stable ICU 63 - */ - @Override - public final StringIterator stringIterator(CharSequence s, int sIndex) { - return new FastStringIterator(s, sIndex); - } - - private final class FastStringIterator extends StringIterator { - private FastStringIterator(CharSequence s, int sIndex) { - super(s, sIndex); - } - - @Override - public boolean next() { - if (sIndex >= s.length()) { - return false; - } - char lead = s.charAt(sIndex++); - c = lead; - int dataIndex; - if (!Character.isSurrogate(lead)) { - dataIndex = fastIndex(c); - } else { - char trail; - if (UTF16Plus.isSurrogateLead(lead) && sIndex < s.length() - && Character.isLowSurrogate(trail = s.charAt(sIndex))) { - ++sIndex; - c = Character.toCodePoint(lead, trail); - dataIndex = smallIndex(Type.FAST, c); - } else { - dataIndex = dataLength - ERROR_VALUE_NEG_DATA_OFFSET; - } - } - value = data.getFromIndex(dataIndex); - return true; - } - - @Override - public boolean previous() { - if (sIndex <= 0) { - return false; - } - char trail = s.charAt(--sIndex); - c = trail; - int dataIndex; - if (!Character.isSurrogate(trail)) { - dataIndex = fastIndex(c); - } else { - char lead; - if (!UTF16Plus.isSurrogateLead(trail) && sIndex > 0 - && Character.isHighSurrogate(lead = s.charAt(sIndex - 1))) { - --sIndex; - c = Character.toCodePoint(lead, trail); - dataIndex = smallIndex(Type.FAST, c); - } else { - dataIndex = dataLength - ERROR_VALUE_NEG_DATA_OFFSET; - } - } - value = data.getFromIndex(dataIndex); - return true; - } - } - } - - /** - * A CodePointTrie with {@link Type#SMALL}. - * - * @stable ICU 63 - */ - public static abstract class Small extends CodePointTrie { - private Small(char[] index, Data data, int highStart, int index3NullOffset, int dataNullOffset) { - super(index, data, highStart, index3NullOffset, dataNullOffset); - } - - /** - * Creates a trie from its binary form. Same as - * {@link CodePointTrie#fromBinary(Type, ValueWidth, ByteBuffer)} with - * {@link Type#SMALL}. - * - * @param valueWidth selects the number of bits in a data value; this method - * throws an exception if the valueWidth does not match the - * binary data; use null to accept any data value width - * @param bytes a buffer containing the binary data of a CodePointTrie - * @return the trie - * @stable ICU 63 - */ - public static Small fromBinary(ValueWidth valueWidth, ByteBuffer bytes) { - return (Small) CodePointTrie.fromBinary(Type.SMALL, valueWidth, bytes); - } - - /** - * @return {@link Type#SMALL} - * @stable ICU 63 - */ - @Override - public final Type getType() { - return Type.SMALL; - } - - /** - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - @Override - protected final int cpIndex(int c) { - if (c >= 0) { - if (c <= SMALL_MAX) { - return fastIndex(c); - } else if (c <= 0x10ffff) { - return smallIndex(Type.SMALL, c); - } - } - return dataLength - ERROR_VALUE_NEG_DATA_OFFSET; - } - - /** - * {@inheritDoc} - * - * @stable ICU 63 - */ - @Override - public final StringIterator stringIterator(CharSequence s, int sIndex) { - return new SmallStringIterator(s, sIndex); - } - - private final class SmallStringIterator extends StringIterator { - private SmallStringIterator(CharSequence s, int sIndex) { - super(s, sIndex); - } - - @Override - public boolean next() { - if (sIndex >= s.length()) { - return false; - } - char lead = s.charAt(sIndex++); - c = lead; - int dataIndex; - if (!Character.isSurrogate(lead)) { - dataIndex = cpIndex(c); - } else { - char trail; - if (UTF16Plus.isSurrogateLead(lead) && sIndex < s.length() - && Character.isLowSurrogate(trail = s.charAt(sIndex))) { - ++sIndex; - c = Character.toCodePoint(lead, trail); - dataIndex = smallIndex(Type.SMALL, c); - } else { - dataIndex = dataLength - ERROR_VALUE_NEG_DATA_OFFSET; - } - } - value = data.getFromIndex(dataIndex); - return true; - } - - @Override - public boolean previous() { - if (sIndex <= 0) { - return false; - } - char trail = s.charAt(--sIndex); - c = trail; - int dataIndex; - if (!Character.isSurrogate(trail)) { - dataIndex = cpIndex(c); - } else { - char lead; - if (!UTF16Plus.isSurrogateLead(trail) && sIndex > 0 - && Character.isHighSurrogate(lead = s.charAt(sIndex - 1))) { - --sIndex; - c = Character.toCodePoint(lead, trail); - dataIndex = smallIndex(Type.SMALL, c); - } else { - dataIndex = dataLength - ERROR_VALUE_NEG_DATA_OFFSET; - } - } - value = data.getFromIndex(dataIndex); - return true; - } - } - } - - /** - * A CodePointTrie with {@link Type#FAST} and {@link ValueWidth#BITS_16}. - * - * @stable ICU 63 - */ - public static final class Fast16 extends Fast { - private final char[] dataArray; - - Fast16(char[] index, char[] data16, int highStart, int index3NullOffset, int dataNullOffset) { - super(index, new Data16(data16), highStart, index3NullOffset, dataNullOffset); - this.dataArray = data16; - } - - /** - * Creates a trie from its binary form. Same as - * {@link CodePointTrie#fromBinary(Type, ValueWidth, ByteBuffer)} with - * {@link Type#FAST} and {@link ValueWidth#BITS_16}. - * - * @param bytes a buffer containing the binary data of a CodePointTrie - * @return the trie - * @stable ICU 63 - */ - public static Fast16 fromBinary(ByteBuffer bytes) { - return (Fast16) CodePointTrie.fromBinary(Type.FAST, ValueWidth.BITS_16, bytes); - } - - /** - * {@inheritDoc} - * - * @stable ICU 63 - */ - @Override - public final int get(int c) { - return dataArray[cpIndex(c)]; - } - - /** - * {@inheritDoc} - * - * @stable ICU 63 - */ - @Override - public final int bmpGet(int c) { - assert 0 <= c && c <= 0xffff; - return dataArray[fastIndex(c)]; - } - - /** - * {@inheritDoc} - * - * @stable ICU 63 - */ - @Override - public final int suppGet(int c) { - assert 0x10000 <= c && c <= 0x10ffff; - return dataArray[smallIndex(Type.FAST, c)]; - } - } - - /** - * A CodePointTrie with {@link Type#FAST} and {@link ValueWidth#BITS_32}. - * - * @stable ICU 63 - */ - public static final class Fast32 extends Fast { - private final int[] dataArray; - - Fast32(char[] index, int[] data32, int highStart, int index3NullOffset, int dataNullOffset) { - super(index, new Data32(data32), highStart, index3NullOffset, dataNullOffset); - this.dataArray = data32; - } - - /** - * Creates a trie from its binary form. Same as - * {@link CodePointTrie#fromBinary(Type, ValueWidth, ByteBuffer)} with - * {@link Type#FAST} and {@link ValueWidth#BITS_32}. - * - * @param bytes a buffer containing the binary data of a CodePointTrie - * @return the trie - * @stable ICU 63 - */ - public static Fast32 fromBinary(ByteBuffer bytes) { - return (Fast32) CodePointTrie.fromBinary(Type.FAST, ValueWidth.BITS_32, bytes); - } - - /** - * {@inheritDoc} - * - * @stable ICU 63 - */ - @Override - public final int get(int c) { - return dataArray[cpIndex(c)]; - } - - /** - * {@inheritDoc} - * - * @stable ICU 63 - */ - @Override - public final int bmpGet(int c) { - assert 0 <= c && c <= 0xffff; - return dataArray[fastIndex(c)]; - } - - /** - * {@inheritDoc} - * - * @stable ICU 63 - */ - @Override - public final int suppGet(int c) { - assert 0x10000 <= c && c <= 0x10ffff; - return dataArray[smallIndex(Type.FAST, c)]; - } - } - - /** - * A CodePointTrie with {@link Type#FAST} and {@link ValueWidth#BITS_8}. - * - * @stable ICU 63 - */ - public static final class Fast8 extends Fast { - private final byte[] dataArray; - - Fast8(char[] index, byte[] data8, int highStart, int index3NullOffset, int dataNullOffset) { - super(index, new Data8(data8), highStart, index3NullOffset, dataNullOffset); - this.dataArray = data8; - } - - /** - * Creates a trie from its binary form. Same as - * {@link CodePointTrie#fromBinary(Type, ValueWidth, ByteBuffer)} with - * {@link Type#FAST} and {@link ValueWidth#BITS_8}. - * - * @param bytes a buffer containing the binary data of a CodePointTrie - * @return the trie - * @stable ICU 63 - */ - public static Fast8 fromBinary(ByteBuffer bytes) { - return (Fast8) CodePointTrie.fromBinary(Type.FAST, ValueWidth.BITS_8, bytes); - } - - /** - * {@inheritDoc} - * - * @stable ICU 63 - */ - @Override - public final int get(int c) { - return dataArray[cpIndex(c)] & 0xff; - } - - /** - * {@inheritDoc} - * - * @stable ICU 63 - */ - @Override - public final int bmpGet(int c) { - assert 0 <= c && c <= 0xffff; - return dataArray[fastIndex(c)] & 0xff; - } - - /** - * {@inheritDoc} - * - * @stable ICU 63 - */ - @Override - public final int suppGet(int c) { - assert 0x10000 <= c && c <= 0x10ffff; - return dataArray[smallIndex(Type.FAST, c)] & 0xff; - } - } - - /** - * A CodePointTrie with {@link Type#SMALL} and {@link ValueWidth#BITS_16}. - * - * @stable ICU 63 - */ - public static final class Small16 extends Small { - Small16(char[] index, char[] data16, int highStart, int index3NullOffset, int dataNullOffset) { - super(index, new Data16(data16), highStart, index3NullOffset, dataNullOffset); - } - - /** - * Creates a trie from its binary form. Same as - * {@link CodePointTrie#fromBinary(Type, ValueWidth, ByteBuffer)} with - * {@link Type#SMALL} and {@link ValueWidth#BITS_16}. - * - * @param bytes a buffer containing the binary data of a CodePointTrie - * @return the trie - * @stable ICU 63 - */ - public static Small16 fromBinary(ByteBuffer bytes) { - return (Small16) CodePointTrie.fromBinary(Type.SMALL, ValueWidth.BITS_16, bytes); - } - } - - /** - * A CodePointTrie with {@link Type#SMALL} and {@link ValueWidth#BITS_32}. - * - * @stable ICU 63 - */ - public static final class Small32 extends Small { - Small32(char[] index, int[] data32, int highStart, int index3NullOffset, int dataNullOffset) { - super(index, new Data32(data32), highStart, index3NullOffset, dataNullOffset); - } - - /** - * Creates a trie from its binary form. Same as - * {@link CodePointTrie#fromBinary(Type, ValueWidth, ByteBuffer)} with - * {@link Type#SMALL} and {@link ValueWidth#BITS_32}. - * - * @param bytes a buffer containing the binary data of a CodePointTrie - * @return the trie - * @stable ICU 63 - */ - public static Small32 fromBinary(ByteBuffer bytes) { - return (Small32) CodePointTrie.fromBinary(Type.SMALL, ValueWidth.BITS_32, bytes); - } - } - - /** - * A CodePointTrie with {@link Type#SMALL} and {@link ValueWidth#BITS_8}. - * - * @stable ICU 63 - */ - public static final class Small8 extends Small { - Small8(char[] index, byte[] data8, int highStart, int index3NullOffset, int dataNullOffset) { - super(index, new Data8(data8), highStart, index3NullOffset, dataNullOffset); - } - - /** - * Creates a trie from its binary form. Same as - * {@link CodePointTrie#fromBinary(Type, ValueWidth, ByteBuffer)} with - * {@link Type#SMALL} and {@link ValueWidth#BITS_8}. - * - * @param bytes a buffer containing the binary data of a CodePointTrie - * @return the trie - * @stable ICU 63 - */ - public static Small8 fromBinary(ByteBuffer bytes) { - return (Small8) CodePointTrie.fromBinary(Type.SMALL, ValueWidth.BITS_8, bytes); - } - } -} +/* + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +// (c) 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html#License + +// created: 2018may04 Markus W. Scherer + +package jdk_internal.icu.util; + +import static jdk_internal.icu.impl.NormalizerImpl.UTF16Plus; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import jdk_internal.icu.impl.ICUBinary; + +/** + * Immutable Unicode code point trie. Fast, reasonably compact, map from Unicode + * code points (U+0000..U+10FFFF) to integer values. For details see + * http://site.icu-project.org/design/struct/utrie + * + *

+ * This class is not intended for public subclassing. + * + * @see MutableCodePointTrie + * @stable ICU 63 + */ +@SuppressWarnings("deprecation") +public abstract class CodePointTrie extends CodePointMap { + /** + * Selectors for the type of a CodePointTrie. Different trade-offs for size vs. + * speed. + * + *

+ * Use null for {@link #fromBinary} to accept any type; {@link #getType} will + * return the actual type. + * + * @see MutableCodePointTrie#buildImmutable(CodePointTrie.Type, + * CodePointTrie.ValueWidth) + * @see #fromBinary + * @see #getType + * @stable ICU 63 + */ + public enum Type { + /** + * Fast/simple/larger BMP data structure. The {@link Fast} subclasses have + * additional functions for lookup for BMP and supplementary code points. + * + * @see Fast + * @stable ICU 63 + */ + FAST, + /** + * Small/slower BMP data structure. + * + * @see Small + * @stable ICU 63 + */ + SMALL + } + + /** + * Selectors for the number of bits in a CodePointTrie data value. + * + *

+ * Use null for {@link #fromBinary} to accept any data value width; + * {@link #getValueWidth} will return the actual data value width. + * + * @stable ICU 63 + */ + public enum ValueWidth { + /** + * The trie stores 16 bits per data value. It returns them as unsigned values + * 0..0xffff=65535. + * + * @stable ICU 63 + */ + BITS_16, + /** + * The trie stores 32 bits per data value. + * + * @stable ICU 63 + */ + BITS_32, + /** + * The trie stores 8 bits per data value. It returns them as unsigned values + * 0..0xff=255. + * + * @stable ICU 63 + */ + BITS_8 + } + + private CodePointTrie(char[] index, Data data, int highStart, int index3NullOffset, int dataNullOffset) { + this.ascii = new int[ASCII_LIMIT]; + this.index = index; + this.data = data; + this.dataLength = data.getDataLength(); + this.highStart = highStart; + this.index3NullOffset = index3NullOffset; + this.dataNullOffset = dataNullOffset; + + for (int c = 0; c < ASCII_LIMIT; ++c) { + ascii[c] = data.getFromIndex(c); + } + + int nullValueOffset = dataNullOffset; + if (nullValueOffset >= dataLength) { + nullValueOffset = dataLength - HIGH_VALUE_NEG_DATA_OFFSET; + } + nullValue = data.getFromIndex(nullValueOffset); + } + + /** + * Creates a trie from its binary form, stored in the ByteBuffer starting at the + * current position. Advances the buffer position to just after the trie data. + * Inverse of {@link #toBinary(OutputStream)}. + * + *

+ * The data is copied from the buffer; later modification of the buffer will not + * affect the trie. + * + * @param type selects the trie type; this method throws an exception if + * the type does not match the binary data; use null to accept + * any type + * @param valueWidth selects the number of bits in a data value; this method + * throws an exception if the valueWidth does not match the + * binary data; use null to accept any data value width + * @param bytes a buffer containing the binary data of a CodePointTrie + * @return the trie + * @see MutableCodePointTrie#MutableCodePointTrie(int, int) + * @see MutableCodePointTrie#buildImmutable(CodePointTrie.Type, + * CodePointTrie.ValueWidth) + * @see #toBinary(OutputStream) + * @stable ICU 63 + */ + public static CodePointTrie fromBinary(Type type, ValueWidth valueWidth, ByteBuffer bytes) { + ByteOrder outerByteOrder = bytes.order(); + try { + // Enough data for a trie header? + if (bytes.remaining() < 16 /* sizeof(UCPTrieHeader) */) { + throw new InternalError("Buffer too short for a CodePointTrie header"); + } + + // struct UCPTrieHeader + /** "Tri3" in big-endian US-ASCII (0x54726933) */ + int signature = bytes.getInt(); + + // Check the signature. + switch (signature) { + case 0x54726933: + // The buffer is already set to the trie data byte order. + break; + case 0x33697254: + // Temporarily reverse the byte order. + boolean isBigEndian = outerByteOrder == ByteOrder.BIG_ENDIAN; + bytes.order(isBigEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); + signature = 0x54726933; + break; + default: + throw new InternalError("Buffer does not contain a serialized CodePointTrie"); + } + + // struct UCPTrieHeader continued + /** + * 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 + */ + int options = bytes.getChar(); + + /** Total length of the index tables. */ + int indexLength = bytes.getChar(); + + /** Data length bits 15..0. */ + int dataLength = bytes.getChar(); + + /** Index-3 null block offset, 0x7fff or 0xffff if none. */ + int index3NullOffset = bytes.getChar(); + + /** Data null block offset bits 15..0, 0xfffff if none. */ + int dataNullOffset = bytes.getChar(); + + /** + * First code point of the single-value range ending with U+10ffff, rounded up + * and then shifted right by SHIFT_2. + */ + int shiftedHighStart = bytes.getChar(); + // struct UCPTrieHeader end + + int typeInt = (options >> 6) & 3; + Type actualType; + switch (typeInt) { + case 0: + actualType = Type.FAST; + break; + case 1: + actualType = Type.SMALL; + break; + default: + throw new InternalError("CodePointTrie data header has an unsupported type"); + } + + int valueWidthInt = options & OPTIONS_VALUE_BITS_MASK; + ValueWidth actualValueWidth; + switch (valueWidthInt) { + case 0: + actualValueWidth = ValueWidth.BITS_16; + break; + case 1: + actualValueWidth = ValueWidth.BITS_32; + break; + case 2: + actualValueWidth = ValueWidth.BITS_8; + break; + default: + throw new InternalError("CodePointTrie data header has an unsupported value width"); + } + + if ((options & OPTIONS_RESERVED_MASK) != 0) { + throw new InternalError("CodePointTrie data header has unsupported options"); + } + + if (type == null) { + type = actualType; + } + if (valueWidth == null) { + valueWidth = actualValueWidth; + } + if (type != actualType || valueWidth != actualValueWidth) { + throw new InternalError("CodePointTrie data header has a different type or value width than required"); + } + + // Get the length values and offsets. + dataLength |= ((options & OPTIONS_DATA_LENGTH_MASK) << 4); + dataNullOffset |= ((options & OPTIONS_DATA_NULL_OFFSET_MASK) << 8); + + int highStart = shiftedHighStart << SHIFT_2; + + // Calculate the actual length, minus the header. + int actualLength = indexLength * 2; + if (valueWidth == ValueWidth.BITS_16) { + actualLength += dataLength * 2; + } else if (valueWidth == ValueWidth.BITS_32) { + actualLength += dataLength * 4; + } else { + actualLength += dataLength; + } + if (bytes.remaining() < actualLength) { + throw new InternalError("Buffer too short for the CodePointTrie data"); + } + + char[] index = ICUBinary.getChars(bytes, indexLength, 0); + switch (valueWidth) { + case BITS_16: { + char[] data16 = ICUBinary.getChars(bytes, dataLength, 0); + return type == Type.FAST ? new Fast16(index, data16, highStart, index3NullOffset, dataNullOffset) + : new Small16(index, data16, highStart, index3NullOffset, dataNullOffset); + } + case BITS_32: { + int[] data32 = ICUBinary.getInts(bytes, dataLength, 0); + return type == Type.FAST ? new Fast32(index, data32, highStart, index3NullOffset, dataNullOffset) + : new Small32(index, data32, highStart, index3NullOffset, dataNullOffset); + } + case BITS_8: { + byte[] data8 = ICUBinary.getBytes(bytes, dataLength, 0); + return type == Type.FAST ? new Fast8(index, data8, highStart, index3NullOffset, dataNullOffset) + : new Small8(index, data8, highStart, index3NullOffset, dataNullOffset); + } + default: + throw new AssertionError("should be unreachable"); + } + } finally { + bytes.order(outerByteOrder); + } + } + + /** + * Returns the trie type. + * + * @return the trie type + * @stable ICU 63 + */ + public abstract Type getType(); + + /** + * Returns the number of bits in a trie data value. + * + * @return the number of bits in a trie data value + * @stable ICU 63 + */ + public final ValueWidth getValueWidth() { + return data.getValueWidth(); + } + + /** + * {@inheritDoc} + * + * @stable ICU 63 + */ + @Override + public int get(int c) { + return data.getFromIndex(cpIndex(c)); + } + + /** + * Returns a trie value for an ASCII code point, without range checking. + * + * @param c the input code point; must be U+0000..U+007F + * @return The ASCII code point's trie value. + * @stable ICU 63 + */ + public final int asciiGet(int c) { + return ascii[c]; + } + + private static final int MAX_UNICODE = 0x10ffff; + + private static final int ASCII_LIMIT = 0x80; + + private static final int maybeFilterValue(int value, int trieNullValue, int nullValue, ValueFilter filter) { + if (value == trieNullValue) { + value = nullValue; + } else if (filter != null) { + value = filter.apply(value); + } + return value; + } + + /** + * {@inheritDoc} + * + * @stable ICU 63 + */ + @Override + public final boolean getRange(int start, ValueFilter filter, Range range) { + if (start < 0 || MAX_UNICODE < start) { + return false; + } + if (start >= highStart) { + int di = dataLength - HIGH_VALUE_NEG_DATA_OFFSET; + int value = data.getFromIndex(di); + if (filter != null) { + value = filter.apply(value); + } + range.set(start, MAX_UNICODE, value); + return true; + } + + int nullValue = this.nullValue; + if (filter != null) { + nullValue = filter.apply(nullValue); + } + Type type = getType(); + + int prevI3Block = -1; + int prevBlock = -1; + int c = start; + // Initialize to make compiler happy. Real value when haveValue is true. + int trieValue = 0, value = 0; + boolean haveValue = false; + do { + int i3Block; + int i3; + int i3BlockLength; + int dataBlockLength; + if (c <= 0xffff && (type == Type.FAST || c <= SMALL_MAX)) { + i3Block = 0; + i3 = c >> FAST_SHIFT; + i3BlockLength = type == Type.FAST ? BMP_INDEX_LENGTH : SMALL_INDEX_LENGTH; + dataBlockLength = FAST_DATA_BLOCK_LENGTH; + } else { + // Use the multi-stage index. + int i1 = c >> SHIFT_1; + if (type == Type.FAST) { + assert (0xffff < c && c < highStart); + i1 += BMP_INDEX_LENGTH - OMITTED_BMP_INDEX_1_LENGTH; + } else { + assert (c < highStart && highStart > SMALL_LIMIT); + i1 += SMALL_INDEX_LENGTH; + } + i3Block = index[index[i1] + ((c >> SHIFT_2) & INDEX_2_MASK)]; + if (i3Block == prevI3Block && (c - start) >= CP_PER_INDEX_2_ENTRY) { + // The index-3 block is the same as the previous one, and filled with value. + assert ((c & (CP_PER_INDEX_2_ENTRY - 1)) == 0); + c += CP_PER_INDEX_2_ENTRY; + continue; + } + prevI3Block = i3Block; + if (i3Block == index3NullOffset) { + // This is the index-3 null block. + if (haveValue) { + if (nullValue != value) { + range.set(start, c - 1, value); + return true; + } + } else { + trieValue = this.nullValue; + value = nullValue; + haveValue = true; + } + prevBlock = dataNullOffset; + c = (c + CP_PER_INDEX_2_ENTRY) & ~(CP_PER_INDEX_2_ENTRY - 1); + continue; + } + i3 = (c >> SHIFT_3) & INDEX_3_MASK; + i3BlockLength = INDEX_3_BLOCK_LENGTH; + dataBlockLength = SMALL_DATA_BLOCK_LENGTH; + } + // Enumerate data blocks for one index-3 block. + do { + int block; + if ((i3Block & 0x8000) == 0) { + block = index[i3Block + i3]; + } else { + // 18-bit indexes stored in groups of 9 entries per 8 indexes. + int group = (i3Block & 0x7fff) + (i3 & ~7) + (i3 >> 3); + int gi = i3 & 7; + block = (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. + assert ((c & (dataBlockLength - 1)) == 0); + c += dataBlockLength; + } else { + int dataMask = dataBlockLength - 1; + prevBlock = block; + if (block == dataNullOffset) { + // This is the data null block. + if (haveValue) { + if (nullValue != value) { + range.set(start, c - 1, value); + return true; + } + } else { + trieValue = this.nullValue; + value = nullValue; + haveValue = true; + } + c = (c + dataBlockLength) & ~dataMask; + } else { + int di = block + (c & dataMask); + int trieValue2 = data.getFromIndex(di); + if (haveValue) { + if (trieValue2 != trieValue) { + if (filter == null + || maybeFilterValue(trieValue2, this.nullValue, nullValue, filter) != value) { + range.set(start, c - 1, value); + return true; + } + trieValue = trieValue2; // may or may not help + } + } else { + trieValue = trieValue2; + value = maybeFilterValue(trieValue2, this.nullValue, nullValue, filter); + haveValue = true; + } + while ((++c & dataMask) != 0) { + trieValue2 = data.getFromIndex(++di); + if (trieValue2 != trieValue) { + if (filter == null + || maybeFilterValue(trieValue2, this.nullValue, nullValue, filter) != value) { + range.set(start, c - 1, value); + return true; + } + trieValue = trieValue2; // may or may not help + } + } + } + } + } while (++i3 < i3BlockLength); + } while (c < highStart); + assert (haveValue); + int di = dataLength - HIGH_VALUE_NEG_DATA_OFFSET; + int highValue = data.getFromIndex(di); + if (maybeFilterValue(highValue, this.nullValue, nullValue, filter) != value) { + --c; + } else { + c = MAX_UNICODE; + } + range.set(start, c, value); + return true; + } + + /** + * Writes a representation of the trie to the output stream. Inverse of + * {@link #fromBinary}. + * + * @param os the output stream + * @return the number of bytes written + * @stable ICU 63 + */ + public final int toBinary(OutputStream os) { + try { + DataOutputStream dos = new DataOutputStream(os); + + // Write the UCPTrieHeader + dos.writeInt(0x54726933); // signature="Tri3" + dos.writeChar( // options + ((dataLength & 0xf0000) >> 4) | ((dataNullOffset & 0xf0000) >> 8) | (getType().ordinal() << 6) + | getValueWidth().ordinal()); + dos.writeChar(index.length); + dos.writeChar(dataLength); + dos.writeChar(index3NullOffset); + dos.writeChar(dataNullOffset); + dos.writeChar(highStart >> SHIFT_2); // shiftedHighStart + int length = 16; // sizeof(UCPTrieHeader) + + for (char i : index) { + dos.writeChar(i); + } + length += index.length * 2; + length += data.write(dos); + return length; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** @internal */ + static final int FAST_SHIFT = 6; + + /** + * Number of entries in a data block for code points below the fast limit. + * 64=0x40 @internal + */ + static final int FAST_DATA_BLOCK_LENGTH = 1 << FAST_SHIFT; + + /** + * Mask for getting the lower bits for the in-fast-data-block offset. @internal + */ + private static final int FAST_DATA_MASK = FAST_DATA_BLOCK_LENGTH - 1; + + /** @internal */ + private static final int 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 + */ + private static final int ERROR_VALUE_NEG_DATA_OFFSET = 1; + /** + * Offset from dataLength (to be subtracted) for fetching the value returned for + * code points highStart..U+10FFFF. + * + * @internal + */ + private static final int HIGH_VALUE_NEG_DATA_OFFSET = 2; + + // ucptrie_impl.h + + /** The length of the BMP index table. 1024=0x400 */ + private static final int BMP_INDEX_LENGTH = 0x10000 >> FAST_SHIFT; + + static final int SMALL_LIMIT = 0x1000; + private static final int SMALL_INDEX_LENGTH = SMALL_LIMIT >> FAST_SHIFT; + + /** Shift size for getting the index-3 table offset. */ + static final int SHIFT_3 = 4; + + /** Shift size for getting the index-2 table offset. */ + private static final int SHIFT_2 = 5 + SHIFT_3; + + /** Shift size for getting the index-1 table offset. */ + private static final int SHIFT_1 = 5 + SHIFT_2; + + /** + * Difference between two shift sizes, for getting an index-2 offset from an + * index-3 offset. 5=9-4 + */ + static final int SHIFT_2_3 = SHIFT_2 - SHIFT_3; + + /** + * Difference between two shift sizes, for getting an index-1 offset from an + * index-2 offset. 5=14-9 + */ + static final int SHIFT_1_2 = SHIFT_1 - SHIFT_2; + + /** + * Number of index-1 entries for the BMP. (4) This part of the index-1 table is + * omitted from the serialized form. + */ + private static final int OMITTED_BMP_INDEX_1_LENGTH = 0x10000 >> SHIFT_1; + + /** Number of entries in an index-2 block. 32=0x20 */ + static final int INDEX_2_BLOCK_LENGTH = 1 << SHIFT_1_2; + + /** Mask for getting the lower bits for the in-index-2-block offset. */ + static final int INDEX_2_MASK = INDEX_2_BLOCK_LENGTH - 1; + + /** Number of code points per index-2 table entry. 512=0x200 */ + static final int CP_PER_INDEX_2_ENTRY = 1 << SHIFT_2; + + /** Number of entries in an index-3 block. 32=0x20 */ + static final int INDEX_3_BLOCK_LENGTH = 1 << SHIFT_2_3; + + /** Mask for getting the lower bits for the in-index-3-block offset. */ + private static final int INDEX_3_MASK = INDEX_3_BLOCK_LENGTH - 1; + + /** Number of entries in a small data block. 16=0x10 */ + static final int SMALL_DATA_BLOCK_LENGTH = 1 << SHIFT_3; + + /** Mask for getting the lower bits for the in-small-data-block offset. */ + static final int SMALL_DATA_MASK = SMALL_DATA_BLOCK_LENGTH - 1; + + // ucptrie_impl.h: Constants for use with UCPTrieHeader.options. + private static final int OPTIONS_DATA_LENGTH_MASK = 0xf000; + private static final int OPTIONS_DATA_NULL_OFFSET_MASK = 0xf00; + private static final int OPTIONS_RESERVED_MASK = 0x38; + private static final int 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. + */ + static final int NO_INDEX3_NULL_OFFSET = 0x7fff; + static final int NO_DATA_NULL_OFFSET = 0xfffff; + + private static abstract class Data { + abstract ValueWidth getValueWidth(); + + abstract int getDataLength(); + + abstract int getFromIndex(int index); + + abstract int write(DataOutputStream dos) throws IOException; + } + + private static final class Data16 extends Data { + char[] array; + + Data16(char[] a) { + array = a; + } + + @Override + ValueWidth getValueWidth() { + return ValueWidth.BITS_16; + } + + @Override + int getDataLength() { + return array.length; + } + + @Override + int getFromIndex(int index) { + return array[index]; + } + + @Override + int write(DataOutputStream dos) throws IOException { + for (char v : array) { + dos.writeChar(v); + } + return array.length * 2; + } + } + + private static final class Data32 extends Data { + int[] array; + + Data32(int[] a) { + array = a; + } + + @Override + ValueWidth getValueWidth() { + return ValueWidth.BITS_32; + } + + @Override + int getDataLength() { + return array.length; + } + + @Override + int getFromIndex(int index) { + return array[index]; + } + + @Override + int write(DataOutputStream dos) throws IOException { + for (int v : array) { + dos.writeInt(v); + } + return array.length * 4; + } + } + + private static final class Data8 extends Data { + byte[] array; + + Data8(byte[] a) { + array = a; + } + + @Override + ValueWidth getValueWidth() { + return ValueWidth.BITS_8; + } + + @Override + int getDataLength() { + return array.length; + } + + @Override + int getFromIndex(int index) { + return array[index] & 0xff; + } + + @Override + int write(DataOutputStream dos) throws IOException { + for (byte v : array) { + dos.writeByte(v); + } + return array.length; + } + } + + /** @internal */ + private final int[] ascii; + + /** @internal */ + private final char[] index; + + /** + * @internal + * @deprecated This API is ICU internal only. + */ + @Deprecated + protected final Data data; + /** + * @internal + * @deprecated This API is ICU internal only. + */ + @Deprecated + protected final int dataLength; + /** + * Start of the last range which ends at U+10FFFF. + * + * @internal + * @deprecated This API is ICU internal only. + */ + @Deprecated + protected final int highStart; + + /** + * 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 + */ + private final int 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 + */ + private final int dataNullOffset; + /** @internal */ + private final int nullValue; + + /** + * @internal + * @deprecated This API is ICU internal only. + */ + @Deprecated + protected final int fastIndex(int c) { + return index[c >> FAST_SHIFT] + (c & FAST_DATA_MASK); + } + + /** + * @internal + * @deprecated This API is ICU internal only. + */ + @Deprecated + protected final int smallIndex(Type type, int c) { + // Split into two methods to make this part inline-friendly. + // In C, this part is a macro. + if (c >= highStart) { + return dataLength - HIGH_VALUE_NEG_DATA_OFFSET; + } + return internalSmallIndex(type, c); + } + + private final int internalSmallIndex(Type type, int c) { + int i1 = c >> SHIFT_1; + if (type == Type.FAST) { + assert (0xffff < c && c < highStart); + i1 += BMP_INDEX_LENGTH - OMITTED_BMP_INDEX_1_LENGTH; + } else { + assert (0 <= c && c < highStart && highStart > SMALL_LIMIT); + i1 += SMALL_INDEX_LENGTH; + } + int i3Block = index[index[i1] + ((c >> SHIFT_2) & INDEX_2_MASK)]; + int i3 = (c >> SHIFT_3) & INDEX_3_MASK; + int dataBlock; + if ((i3Block & 0x8000) == 0) { + // 16-bit indexes + dataBlock = 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 = (index[i3Block++] << (2 + (2 * i3))) & 0x30000; + dataBlock |= index[i3Block + i3]; + } + return dataBlock + (c & SMALL_DATA_MASK); + } + + /** + * @internal + * @deprecated This API is ICU internal only. + */ + @Deprecated + protected abstract int cpIndex(int c); + + /** + * A CodePointTrie with {@link Type#FAST}. + * + * @stable ICU 63 + */ + public static abstract class Fast extends CodePointTrie { + private Fast(char[] index, Data data, int highStart, int index3NullOffset, int dataNullOffset) { + super(index, data, highStart, index3NullOffset, dataNullOffset); + } + + /** + * Creates a trie from its binary form. Same as + * {@link CodePointTrie#fromBinary(Type, ValueWidth, ByteBuffer)} with + * {@link Type#FAST}. + * + * @param valueWidth selects the number of bits in a data value; this method + * throws an exception if the valueWidth does not match the + * binary data; use null to accept any data value width + * @param bytes a buffer containing the binary data of a CodePointTrie + * @return the trie + * @stable ICU 63 + */ + public static Fast fromBinary(ValueWidth valueWidth, ByteBuffer bytes) { + return (Fast) CodePointTrie.fromBinary(Type.FAST, valueWidth, bytes); + } + + /** + * @return {@link Type#FAST} + * @stable ICU 63 + */ + @Override + public final Type getType() { + return Type.FAST; + } + + /** + * 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 c the input code point, must be U+0000..U+FFFF + * @return The BMP code point's trie value. + * @stable ICU 63 + */ + public abstract int bmpGet(int c); + + /** + * Returns a trie value for a supplementary code point (U+10000..U+10FFFF), + * without range checking. + * + * @param c the input code point, must be U+10000..U+10FFFF + * @return The supplementary code point's trie value. + * @stable ICU 63 + */ + public abstract int suppGet(int c); + + /** + * @internal + * @deprecated This API is ICU internal only. + */ + @Deprecated + @Override + protected final int cpIndex(int c) { + if (c >= 0) { + if (c <= 0xffff) { + return fastIndex(c); + } else if (c <= 0x10ffff) { + return smallIndex(Type.FAST, c); + } + } + return dataLength - ERROR_VALUE_NEG_DATA_OFFSET; + } + + /** + * {@inheritDoc} + * + * @stable ICU 63 + */ + @Override + public final StringIterator stringIterator(CharSequence s, int sIndex) { + return new FastStringIterator(s, sIndex); + } + + private final class FastStringIterator extends StringIterator { + private FastStringIterator(CharSequence s, int sIndex) { + super(s, sIndex); + } + + @Override + public boolean next() { + if (sIndex >= s.length()) { + return false; + } + char lead = s.charAt(sIndex++); + c = lead; + int dataIndex; + if (!Character.isSurrogate(lead)) { + dataIndex = fastIndex(c); + } else { + char trail; + if (UTF16Plus.isSurrogateLead(lead) && sIndex < s.length() + && Character.isLowSurrogate(trail = s.charAt(sIndex))) { + ++sIndex; + c = Character.toCodePoint(lead, trail); + dataIndex = smallIndex(Type.FAST, c); + } else { + dataIndex = dataLength - ERROR_VALUE_NEG_DATA_OFFSET; + } + } + value = data.getFromIndex(dataIndex); + return true; + } + + @Override + public boolean previous() { + if (sIndex <= 0) { + return false; + } + char trail = s.charAt(--sIndex); + c = trail; + int dataIndex; + if (!Character.isSurrogate(trail)) { + dataIndex = fastIndex(c); + } else { + char lead; + if (!UTF16Plus.isSurrogateLead(trail) && sIndex > 0 + && Character.isHighSurrogate(lead = s.charAt(sIndex - 1))) { + --sIndex; + c = Character.toCodePoint(lead, trail); + dataIndex = smallIndex(Type.FAST, c); + } else { + dataIndex = dataLength - ERROR_VALUE_NEG_DATA_OFFSET; + } + } + value = data.getFromIndex(dataIndex); + return true; + } + } + } + + /** + * A CodePointTrie with {@link Type#SMALL}. + * + * @stable ICU 63 + */ + public static abstract class Small extends CodePointTrie { + private Small(char[] index, Data data, int highStart, int index3NullOffset, int dataNullOffset) { + super(index, data, highStart, index3NullOffset, dataNullOffset); + } + + /** + * Creates a trie from its binary form. Same as + * {@link CodePointTrie#fromBinary(Type, ValueWidth, ByteBuffer)} with + * {@link Type#SMALL}. + * + * @param valueWidth selects the number of bits in a data value; this method + * throws an exception if the valueWidth does not match the + * binary data; use null to accept any data value width + * @param bytes a buffer containing the binary data of a CodePointTrie + * @return the trie + * @stable ICU 63 + */ + public static Small fromBinary(ValueWidth valueWidth, ByteBuffer bytes) { + return (Small) CodePointTrie.fromBinary(Type.SMALL, valueWidth, bytes); + } + + /** + * @return {@link Type#SMALL} + * @stable ICU 63 + */ + @Override + public final Type getType() { + return Type.SMALL; + } + + /** + * @internal + * @deprecated This API is ICU internal only. + */ + @Deprecated + @Override + protected final int cpIndex(int c) { + if (c >= 0) { + if (c <= SMALL_MAX) { + return fastIndex(c); + } else if (c <= 0x10ffff) { + return smallIndex(Type.SMALL, c); + } + } + return dataLength - ERROR_VALUE_NEG_DATA_OFFSET; + } + + /** + * {@inheritDoc} + * + * @stable ICU 63 + */ + @Override + public final StringIterator stringIterator(CharSequence s, int sIndex) { + return new SmallStringIterator(s, sIndex); + } + + private final class SmallStringIterator extends StringIterator { + private SmallStringIterator(CharSequence s, int sIndex) { + super(s, sIndex); + } + + @Override + public boolean next() { + if (sIndex >= s.length()) { + return false; + } + char lead = s.charAt(sIndex++); + c = lead; + int dataIndex; + if (!Character.isSurrogate(lead)) { + dataIndex = cpIndex(c); + } else { + char trail; + if (UTF16Plus.isSurrogateLead(lead) && sIndex < s.length() + && Character.isLowSurrogate(trail = s.charAt(sIndex))) { + ++sIndex; + c = Character.toCodePoint(lead, trail); + dataIndex = smallIndex(Type.SMALL, c); + } else { + dataIndex = dataLength - ERROR_VALUE_NEG_DATA_OFFSET; + } + } + value = data.getFromIndex(dataIndex); + return true; + } + + @Override + public boolean previous() { + if (sIndex <= 0) { + return false; + } + char trail = s.charAt(--sIndex); + c = trail; + int dataIndex; + if (!Character.isSurrogate(trail)) { + dataIndex = cpIndex(c); + } else { + char lead; + if (!UTF16Plus.isSurrogateLead(trail) && sIndex > 0 + && Character.isHighSurrogate(lead = s.charAt(sIndex - 1))) { + --sIndex; + c = Character.toCodePoint(lead, trail); + dataIndex = smallIndex(Type.SMALL, c); + } else { + dataIndex = dataLength - ERROR_VALUE_NEG_DATA_OFFSET; + } + } + value = data.getFromIndex(dataIndex); + return true; + } + } + } + + /** + * A CodePointTrie with {@link Type#FAST} and {@link ValueWidth#BITS_16}. + * + * @stable ICU 63 + */ + public static final class Fast16 extends Fast { + private final char[] dataArray; + + Fast16(char[] index, char[] data16, int highStart, int index3NullOffset, int dataNullOffset) { + super(index, new Data16(data16), highStart, index3NullOffset, dataNullOffset); + this.dataArray = data16; + } + + /** + * Creates a trie from its binary form. Same as + * {@link CodePointTrie#fromBinary(Type, ValueWidth, ByteBuffer)} with + * {@link Type#FAST} and {@link ValueWidth#BITS_16}. + * + * @param bytes a buffer containing the binary data of a CodePointTrie + * @return the trie + * @stable ICU 63 + */ + public static Fast16 fromBinary(ByteBuffer bytes) { + return (Fast16) CodePointTrie.fromBinary(Type.FAST, ValueWidth.BITS_16, bytes); + } + + /** + * {@inheritDoc} + * + * @stable ICU 63 + */ + @Override + public final int get(int c) { + return dataArray[cpIndex(c)]; + } + + /** + * {@inheritDoc} + * + * @stable ICU 63 + */ + @Override + public final int bmpGet(int c) { + assert 0 <= c && c <= 0xffff; + return dataArray[fastIndex(c)]; + } + + /** + * {@inheritDoc} + * + * @stable ICU 63 + */ + @Override + public final int suppGet(int c) { + assert 0x10000 <= c && c <= 0x10ffff; + return dataArray[smallIndex(Type.FAST, c)]; + } + } + + /** + * A CodePointTrie with {@link Type#FAST} and {@link ValueWidth#BITS_32}. + * + * @stable ICU 63 + */ + public static final class Fast32 extends Fast { + private final int[] dataArray; + + Fast32(char[] index, int[] data32, int highStart, int index3NullOffset, int dataNullOffset) { + super(index, new Data32(data32), highStart, index3NullOffset, dataNullOffset); + this.dataArray = data32; + } + + /** + * Creates a trie from its binary form. Same as + * {@link CodePointTrie#fromBinary(Type, ValueWidth, ByteBuffer)} with + * {@link Type#FAST} and {@link ValueWidth#BITS_32}. + * + * @param bytes a buffer containing the binary data of a CodePointTrie + * @return the trie + * @stable ICU 63 + */ + public static Fast32 fromBinary(ByteBuffer bytes) { + return (Fast32) CodePointTrie.fromBinary(Type.FAST, ValueWidth.BITS_32, bytes); + } + + /** + * {@inheritDoc} + * + * @stable ICU 63 + */ + @Override + public final int get(int c) { + return dataArray[cpIndex(c)]; + } + + /** + * {@inheritDoc} + * + * @stable ICU 63 + */ + @Override + public final int bmpGet(int c) { + assert 0 <= c && c <= 0xffff; + return dataArray[fastIndex(c)]; + } + + /** + * {@inheritDoc} + * + * @stable ICU 63 + */ + @Override + public final int suppGet(int c) { + assert 0x10000 <= c && c <= 0x10ffff; + return dataArray[smallIndex(Type.FAST, c)]; + } + } + + /** + * A CodePointTrie with {@link Type#FAST} and {@link ValueWidth#BITS_8}. + * + * @stable ICU 63 + */ + public static final class Fast8 extends Fast { + private final byte[] dataArray; + + Fast8(char[] index, byte[] data8, int highStart, int index3NullOffset, int dataNullOffset) { + super(index, new Data8(data8), highStart, index3NullOffset, dataNullOffset); + this.dataArray = data8; + } + + /** + * Creates a trie from its binary form. Same as + * {@link CodePointTrie#fromBinary(Type, ValueWidth, ByteBuffer)} with + * {@link Type#FAST} and {@link ValueWidth#BITS_8}. + * + * @param bytes a buffer containing the binary data of a CodePointTrie + * @return the trie + * @stable ICU 63 + */ + public static Fast8 fromBinary(ByteBuffer bytes) { + return (Fast8) CodePointTrie.fromBinary(Type.FAST, ValueWidth.BITS_8, bytes); + } + + /** + * {@inheritDoc} + * + * @stable ICU 63 + */ + @Override + public final int get(int c) { + return dataArray[cpIndex(c)] & 0xff; + } + + /** + * {@inheritDoc} + * + * @stable ICU 63 + */ + @Override + public final int bmpGet(int c) { + assert 0 <= c && c <= 0xffff; + return dataArray[fastIndex(c)] & 0xff; + } + + /** + * {@inheritDoc} + * + * @stable ICU 63 + */ + @Override + public final int suppGet(int c) { + assert 0x10000 <= c && c <= 0x10ffff; + return dataArray[smallIndex(Type.FAST, c)] & 0xff; + } + } + + /** + * A CodePointTrie with {@link Type#SMALL} and {@link ValueWidth#BITS_16}. + * + * @stable ICU 63 + */ + public static final class Small16 extends Small { + Small16(char[] index, char[] data16, int highStart, int index3NullOffset, int dataNullOffset) { + super(index, new Data16(data16), highStart, index3NullOffset, dataNullOffset); + } + + /** + * Creates a trie from its binary form. Same as + * {@link CodePointTrie#fromBinary(Type, ValueWidth, ByteBuffer)} with + * {@link Type#SMALL} and {@link ValueWidth#BITS_16}. + * + * @param bytes a buffer containing the binary data of a CodePointTrie + * @return the trie + * @stable ICU 63 + */ + public static Small16 fromBinary(ByteBuffer bytes) { + return (Small16) CodePointTrie.fromBinary(Type.SMALL, ValueWidth.BITS_16, bytes); + } + } + + /** + * A CodePointTrie with {@link Type#SMALL} and {@link ValueWidth#BITS_32}. + * + * @stable ICU 63 + */ + public static final class Small32 extends Small { + Small32(char[] index, int[] data32, int highStart, int index3NullOffset, int dataNullOffset) { + super(index, new Data32(data32), highStart, index3NullOffset, dataNullOffset); + } + + /** + * Creates a trie from its binary form. Same as + * {@link CodePointTrie#fromBinary(Type, ValueWidth, ByteBuffer)} with + * {@link Type#SMALL} and {@link ValueWidth#BITS_32}. + * + * @param bytes a buffer containing the binary data of a CodePointTrie + * @return the trie + * @stable ICU 63 + */ + public static Small32 fromBinary(ByteBuffer bytes) { + return (Small32) CodePointTrie.fromBinary(Type.SMALL, ValueWidth.BITS_32, bytes); + } + } + + /** + * A CodePointTrie with {@link Type#SMALL} and {@link ValueWidth#BITS_8}. + * + * @stable ICU 63 + */ + public static final class Small8 extends Small { + Small8(char[] index, byte[] data8, int highStart, int index3NullOffset, int dataNullOffset) { + super(index, new Data8(data8), highStart, index3NullOffset, dataNullOffset); + } + + /** + * Creates a trie from its binary form. Same as + * {@link CodePointTrie#fromBinary(Type, ValueWidth, ByteBuffer)} with + * {@link Type#SMALL} and {@link ValueWidth#BITS_8}. + * + * @param bytes a buffer containing the binary data of a CodePointTrie + * @return the trie + * @stable ICU 63 + */ + public static Small8 fromBinary(ByteBuffer bytes) { + return (Small8) CodePointTrie.fromBinary(Type.SMALL, ValueWidth.BITS_8, bytes); + } + } +} diff --git a/src/main/java/jdk_internal/icu/util/OutputInt.java b/src/main/java/jdk_internal/icu/util/OutputInt.java index 973e4168..15e5944e 100755 --- a/src/main/java/jdk_internal/icu/util/OutputInt.java +++ b/src/main/java/jdk_internal/icu/util/OutputInt.java @@ -1,48 +1,48 @@ -/* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ******************************************************************************* - * Copyright (C) 2014, International Business Machines Corporation and - * others. All Rights Reserved. - ******************************************************************************* - */ -package jdk_internal.icu.util; - -/** - * Simple struct-like class for int output parameters. Like - * Output<Integer> but without auto-boxing. - * - * @internal but could become public deprecated This API is ICU internal only. - */ -public class OutputInt { - - /** - * The value field. - * - * @internal deprecated This API is ICU internal only. - */ - public int value; -} +/* + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ******************************************************************************* + * Copyright (C) 2014, International Business Machines Corporation and + * others. All Rights Reserved. + ******************************************************************************* + */ +package jdk_internal.icu.util; + +/** + * Simple struct-like class for int output parameters. Like + * Output<Integer> but without auto-boxing. + * + * @internal but could become public deprecated This API is ICU internal only. + */ +public class OutputInt { + + /** + * The value field. + * + * @internal deprecated This API is ICU internal only. + */ + public int value; +} diff --git a/src/main/java/jdk_internal/icu/util/VersionInfo.java b/src/main/java/jdk_internal/icu/util/VersionInfo.java index 06724a83..55a574c6 100755 --- a/src/main/java/jdk_internal/icu/util/VersionInfo.java +++ b/src/main/java/jdk_internal/icu/util/VersionInfo.java @@ -1,191 +1,191 @@ -/* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -/* - ******************************************************************************* - * (C) Copyright IBM Corp. and others, 1996-2009 - All Rights Reserved * - * * - * The original version of this source code and documentation is copyrighted * - * and owned by IBM, These materials are provided under terms of a License * - * Agreement between IBM and Sun. This technology is protected by multiple * - * US and International patents. This notice and attribution to IBM may not * - * to removed. * - ******************************************************************************* - */ - -package jdk_internal.icu.util; - -import java.util.HashMap; - -/** - * Class to store version numbers of the form major.minor.milli.micro. - * - * @author synwee - * @stable ICU 2.6 - */ -public final class VersionInfo { - // public data members ------------------------------------------------- - - /** - * Data version string for ICU's internal data. Used for appending to data path - * (e.g. icudt43b) - * - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - public static final String ICU_DATA_VERSION_PATH = "67b"; - - // public methods ------------------------------------------------------ - - /** - * Returns an instance of VersionInfo with the argument version. - * - * @param version version String in the format of "major.minor.milli.micro" or - * "major.minor.milli" or "major.minor" or "major", where major, - * minor, milli, micro are non-negative numbers {@literal <=} - * 255. If the trailing version numbers are not specified they - * are taken as 0s. E.g. Version "3.1" is equivalent to - * "3.1.0.0". - * @return an instance of VersionInfo with the argument version. - * @exception throws an IllegalArgumentException when the argument version is - * not in the right format - * @stable ICU 2.6 - */ - public static VersionInfo getInstance(String version) { - int length = version.length(); - int array[] = { 0, 0, 0, 0 }; - int count = 0; - int index = 0; - - while (count < 4 && index < length) { - char c = version.charAt(index); - if (c == '.') { - count++; - } else { - c -= '0'; - if (c < 0 || c > 9) { - throw new IllegalArgumentException(INVALID_VERSION_NUMBER_); - } - array[count] *= 10; - array[count] += c; - } - index++; - } - if (index != length) { - throw new IllegalArgumentException( - "Invalid version number: String '" + version + "' exceeds version format"); - } - for (int i = 0; i < 4; i++) { - if (array[i] < 0 || array[i] > 255) { - throw new IllegalArgumentException(INVALID_VERSION_NUMBER_); - } - } - - return getInstance(array[0], array[1], array[2], array[3]); - } - - /** - * Returns an instance of VersionInfo with the argument version. - * - * @param major major version, non-negative number {@literal <=} 255. - * @param minor minor version, non-negative number {@literal <=} 255. - * @param milli milli version, non-negative number {@literal <=} 255. - * @param micro micro version, non-negative number {@literal <=} 255. - * @exception throws an IllegalArgumentException when either arguments are - * negative or {@literal >} 255 - * @stable ICU 2.6 - */ - public static VersionInfo getInstance(int major, int minor, int milli, int micro) { - // checks if it is in the hashmap - // else - if (major < 0 || major > 255 || minor < 0 || minor > 255 || milli < 0 || milli > 255 || micro < 0 - || micro > 255) { - throw new IllegalArgumentException(INVALID_VERSION_NUMBER_); - } - int version = getInt(major, minor, milli, micro); - Integer key = Integer.valueOf(version); - Object result = MAP_.get(key); - if (result == null) { - result = new VersionInfo(version); - MAP_.put(key, result); - } - return (VersionInfo) result; - } - - /** - * Compares other with this VersionInfo. - * - * @param other VersionInfo to be compared - * @return 0 if the argument is a VersionInfo object that has version - * information equal to this object. Less than 0 if the argument is a - * VersionInfo object that has version information greater than this - * object. Greater than 0 if the argument is a VersionInfo object that - * has version information less than this object. - * @stable ICU 2.6 - */ - public int compareTo(VersionInfo other) { - return m_version_ - other.m_version_; - } - - // private data members ---------------------------------------------- - - /** - * Version number stored as a byte for each of the major, minor, milli and micro - * numbers in the 32 bit int. Most significant for the major and the least - * significant contains the micro numbers. - */ - private int m_version_; - /** - * Map of singletons - */ - private static final HashMap MAP_ = new HashMap<>(); - /** - * Error statement string - */ - private static final String INVALID_VERSION_NUMBER_ = "Invalid version number: Version number may be negative or greater than 255"; - - // private constructor ----------------------------------------------- - - /** - * Constructor with int - * - * @param compactversion a 32 bit int with each byte representing a number - */ - private VersionInfo(int compactversion) { - m_version_ = compactversion; - } - - /** - * Gets the int from the version numbers - * - * @param major non-negative version number - * @param minor non-negativeversion number - * @param milli non-negativeversion number - * @param micro non-negativeversion number - */ - private static int getInt(int major, int minor, int milli, int micro) { - return (major << 24) | (minor << 16) | (milli << 8) | micro; - } -} +/* + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + ******************************************************************************* + * (C) Copyright IBM Corp. and others, 1996-2009 - All Rights Reserved * + * * + * The original version of this source code and documentation is copyrighted * + * and owned by IBM, These materials are provided under terms of a License * + * Agreement between IBM and Sun. This technology is protected by multiple * + * US and International patents. This notice and attribution to IBM may not * + * to removed. * + ******************************************************************************* + */ + +package jdk_internal.icu.util; + +import java.util.HashMap; + +/** + * Class to store version numbers of the form major.minor.milli.micro. + * + * @author synwee + * @stable ICU 2.6 + */ +public final class VersionInfo { + // public data members ------------------------------------------------- + + /** + * Data version string for ICU's internal data. Used for appending to data path + * (e.g. icudt43b) + * + * @internal + * @deprecated This API is ICU internal only. + */ + @Deprecated + public static final String ICU_DATA_VERSION_PATH = "67b"; + + // public methods ------------------------------------------------------ + + /** + * Returns an instance of VersionInfo with the argument version. + * + * @param version version String in the format of "major.minor.milli.micro" or + * "major.minor.milli" or "major.minor" or "major", where major, + * minor, milli, micro are non-negative numbers {@literal <=} + * 255. If the trailing version numbers are not specified they + * are taken as 0s. E.g. Version "3.1" is equivalent to + * "3.1.0.0". + * @return an instance of VersionInfo with the argument version. + * @exception throws an IllegalArgumentException when the argument version is + * not in the right format + * @stable ICU 2.6 + */ + public static VersionInfo getInstance(String version) { + int length = version.length(); + int array[] = { 0, 0, 0, 0 }; + int count = 0; + int index = 0; + + while (count < 4 && index < length) { + char c = version.charAt(index); + if (c == '.') { + count++; + } else { + c -= '0'; + if (c < 0 || c > 9) { + throw new IllegalArgumentException(INVALID_VERSION_NUMBER_); + } + array[count] *= 10; + array[count] += c; + } + index++; + } + if (index != length) { + throw new IllegalArgumentException( + "Invalid version number: String '" + version + "' exceeds version format"); + } + for (int i = 0; i < 4; i++) { + if (array[i] < 0 || array[i] > 255) { + throw new IllegalArgumentException(INVALID_VERSION_NUMBER_); + } + } + + return getInstance(array[0], array[1], array[2], array[3]); + } + + /** + * Returns an instance of VersionInfo with the argument version. + * + * @param major major version, non-negative number {@literal <=} 255. + * @param minor minor version, non-negative number {@literal <=} 255. + * @param milli milli version, non-negative number {@literal <=} 255. + * @param micro micro version, non-negative number {@literal <=} 255. + * @exception throws an IllegalArgumentException when either arguments are + * negative or {@literal >} 255 + * @stable ICU 2.6 + */ + public static VersionInfo getInstance(int major, int minor, int milli, int micro) { + // checks if it is in the hashmap + // else + if (major < 0 || major > 255 || minor < 0 || minor > 255 || milli < 0 || milli > 255 || micro < 0 + || micro > 255) { + throw new IllegalArgumentException(INVALID_VERSION_NUMBER_); + } + int version = getInt(major, minor, milli, micro); + Integer key = Integer.valueOf(version); + Object result = MAP_.get(key); + if (result == null) { + result = new VersionInfo(version); + MAP_.put(key, result); + } + return (VersionInfo) result; + } + + /** + * Compares other with this VersionInfo. + * + * @param other VersionInfo to be compared + * @return 0 if the argument is a VersionInfo object that has version + * information equal to this object. Less than 0 if the argument is a + * VersionInfo object that has version information greater than this + * object. Greater than 0 if the argument is a VersionInfo object that + * has version information less than this object. + * @stable ICU 2.6 + */ + public int compareTo(VersionInfo other) { + return m_version_ - other.m_version_; + } + + // private data members ---------------------------------------------- + + /** + * Version number stored as a byte for each of the major, minor, milli and micro + * numbers in the 32 bit int. Most significant for the major and the least + * significant contains the micro numbers. + */ + private int m_version_; + /** + * Map of singletons + */ + private static final HashMap MAP_ = new HashMap<>(); + /** + * Error statement string + */ + private static final String INVALID_VERSION_NUMBER_ = "Invalid version number: Version number may be negative or greater than 255"; + + // private constructor ----------------------------------------------- + + /** + * Constructor with int + * + * @param compactversion a 32 bit int with each byte representing a number + */ + private VersionInfo(int compactversion) { + m_version_ = compactversion; + } + + /** + * Gets the int from the version numbers + * + * @param major non-negative version number + * @param minor non-negativeversion number + * @param milli non-negativeversion number + * @param micro non-negativeversion number + */ + private static int getInt(int major, int minor, int milli, int micro) { + return (major << 24) | (minor << 16) | (milli << 8) | micro; + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/ClientUUIDLoadingCache.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/ClientUUIDLoadingCache.java index b3649e1e..ee7fe275 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/ClientUUIDLoadingCache.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/ClientUUIDLoadingCache.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 lax1dude. All Rights Reserved. + * Copyright (c) 2024-2025 lax1dude. All Rights Reserved. * * 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 @@ -22,6 +22,7 @@ import java.util.Map; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.client.StateFlags; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketGetOtherClientUUIDV4EAG; import net.minecraft.client.Minecraft; import net.minecraft.client.entity.AbstractClientPlayer; @@ -53,7 +54,7 @@ public class ClientUUIDLoadingCache { if(ret == null) { Minecraft mc = Minecraft.getMinecraft(); if(mc != null && mc.thePlayer != null && mc.thePlayer.sendQueue.getEaglerMessageProtocol().ver >= 4) { - if(ignoreNonEaglerPlayers && !player.getGameProfile().getTextures().eaglerPlayer) { + if(StateFlags.eaglerPlayerFlag && player.getGameProfile().getTextures().eaglerPlayer != (byte) 2) { ret = VANILLA_UUID; }else { ret = PENDING_UUID; @@ -86,7 +87,6 @@ public class ClientUUIDLoadingCache { private static int requestId = 0; private static long lastFlushReq = EagRuntime.steadyTimeMillis(); private static long lastFlushEvict = EagRuntime.steadyTimeMillis(); - private static boolean ignoreNonEaglerPlayers = false; public static void update() { long timestamp = EagRuntime.steadyTimeMillis(); @@ -122,19 +122,21 @@ public class ClientUUIDLoadingCache { evictedUUIDs.clear(); } - private static final EaglercraftUUID MAGIC_DISABLE_NON_EAGLER_PLAYERS = new EaglercraftUUID(0xEEEEA64771094C4EL, 0x86E55B81D17E67EBL); - public static void handleResponse(int requestId, EaglercraftUUID clientId) { WaitingLookup lookup = waitingIDs.remove(requestId); if(lookup != null) { lookup.player.clientBrandUUIDCache = clientId; waitingUUIDs.remove(lookup.uuid); }else { - if(requestId == -1 && MAGIC_DISABLE_NON_EAGLER_PLAYERS.equals(clientId)) { - ignoreNonEaglerPlayers = true; - }else { - logger.warn("Unsolicited client brand UUID lookup response #{} recieved! (Brand UUID: {})", requestId, clientId); + if (requestId == -1 && StateFlags.LEGACY_EAGLER_PLAYER_FLAG_PRESENT.equals(clientId)) { + Minecraft mc = Minecraft.getMinecraft(); + if (mc != null && (mc.thePlayer == null || mc.thePlayer.sendQueue.getEaglerMessageProtocol().ver < 5)) { + StateFlags.eaglerPlayerFlag = true; + StateFlags.eaglerPlayerFlagSupervisor = true; + return; + } } + logger.warn("Unsolicited client brand UUID lookup response #{} recieved! (Brand UUID: {})", requestId, clientId); } } @@ -146,10 +148,6 @@ public class ClientUUIDLoadingCache { } } - public static void resetFlags() { - ignoreNonEaglerPlayers = false; - } - private static class WaitingLookup { private final int reqID; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/Display.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/Display.java index a345e588..7e4431b7 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/Display.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/Display.java @@ -16,6 +16,7 @@ package net.lax1dude.eaglercraft.v1_8; +import net.lax1dude.eaglercraft.v1_8.internal.ContextLostError; import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; public class Display { @@ -83,6 +84,12 @@ public class Display { return PlatformInput.contextLost(); } + public static void checkContextLost() { + if(PlatformInput.contextLost()) { + throw new ContextLostError(); + } + } + public static boolean wasResized() { return PlatformInput.wasResized(); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/EagRuntime.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/EagRuntime.java index b4ff91b2..50dbdb66 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/EagRuntime.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/EagRuntime.java @@ -69,10 +69,10 @@ public class EagRuntime { operatingSystem = PlatformRuntime.getPlatformOS(); angleBackend = PlatformRuntime.getPlatformANGLE(); UpdateService.initialize(); - EaglerXBungeeVersion.initialize(); EaglercraftGPU.warmUpCache(); ScreenRecordingController.initialize(); PlatformRuntime.postCreate(); + Display.checkContextLost(); } public static void destroy() { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglerXBungeeVersion.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglerXBungeeVersion.java deleted file mode 100755 index b8df18a9..00000000 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglerXBungeeVersion.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2024 lax1dude. All Rights Reserved. - * - * 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. - * - */ - -package net.lax1dude.eaglercraft.v1_8; - -import org.json.JSONObject; - -public class EaglerXBungeeVersion { - - public static final String pluginFileEPK = "plugin_download.zip"; - - private static String pluginName = null; - private static String pluginVersion = null; - private static long pluginVersionLong = 0l; - private static String pluginButton = null; - private static String pluginFilename = null; - - public static void initialize() { - String pluginVersionJson = EagRuntime.getRequiredResourceString("plugin_version.json"); - JSONObject json = new JSONObject(pluginVersionJson); - pluginName = json.getString("pluginName"); - pluginVersion = json.getString("pluginVersion"); - pluginVersionLong = getVersionAsLong(pluginVersion); - pluginButton = json.getString("pluginButton"); - pluginFilename = json.getString("pluginFilename"); - } - - public static String getPluginName() { - return pluginName; - } - - public static String getPluginVersion() { - return pluginVersion; - } - - public static long getPluginVersionLong() { - return pluginVersionLong; - } - - public static String getPluginButton() { - return pluginButton; - } - - public static String getPluginFilename() { - return pluginFilename; - } - - public static long getVersionAsLong(String vers) { - try { - String[] verz = vers.split("\\."); - long ret = 0; - long div = 1000000000000l; - for(int i = 0; i < verz.length; ++i) { - ret += div * Long.parseLong(verz[i]); - div /= 10000l; - } - return ret; - }catch(Throwable t) { - return -1l; - } - } - - public static byte[] getPluginDownload() { - return EagRuntime.getRequiredResourceBytes(pluginFileEPK); - } - - public static void startPluginDownload() { - EagRuntime.downloadFileWithName(pluginFilename, getPluginDownload()); - } - - public static boolean isUpdateToPluginAvailable(String brand, String vers) { - if(pluginVersionLong == -1l || !pluginName.equals(brand)) { - return false; - } - long verz = getVersionAsLong(vers); - return verz != -1l && verz < pluginVersionLong; - } -} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java index f2e5adf9..2c31bae9 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java @@ -10,7 +10,7 @@ public class EaglercraftVersion { /// Customize these to fit your fork: public static final String projectForkName = "EaglercraftX"; - public static final String projectForkVersion = "u50"; + public static final String projectForkVersion = "u51"; public static final String projectForkVendor = "lax1dude"; public static final String projectForkURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8"; @@ -20,20 +20,20 @@ public class EaglercraftVersion { public static final String projectOriginName = "EaglercraftX"; public static final String projectOriginAuthor = "lax1dude"; public static final String projectOriginRevision = "1.8"; - public static final String projectOriginVersion = "u50"; + public static final String projectOriginVersion = "u51"; public static final String projectOriginURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8"; // rest in peace // EPK Version Identifier - public static final String EPKVersionIdentifier = "u50"; // Set to null to disable EPK version check + public static final String EPKVersionIdentifier = "u51"; // Set to null to disable EPK version check // Updating configuration public static final boolean enableUpdateService = true; public static final String updateBundlePackageName = "net.lax1dude.eaglercraft.v1_8.client"; - public static final int updateBundlePackageVersionInt = 50; + public static final int updateBundlePackageVersionInt = 51; public static final String updateLatestLocalStorageKey = "latestUpdate_" + updateBundlePackageName; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/PauseMenuCustomizeState.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/PauseMenuCustomizeState.java index a03ba120..c9669b81 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/PauseMenuCustomizeState.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/PauseMenuCustomizeState.java @@ -150,8 +150,7 @@ public class PauseMenuCustomizeState { case DISCORD_MODE_NONE: default: discordButtonText = null; - serverInfoURL = null; - serverInfoHash = null; + discordInviteURL = null; break; case DISCORD_MODE_INVITE_URL: discordButtonText = packet.discordButtonText; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/HardwareFingerprint.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/HardwareFingerprint.java index 173e630b..8f876933 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/HardwareFingerprint.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/cookie/HardwareFingerprint.java @@ -43,6 +43,7 @@ import java.util.List; import com.google.common.collect.Lists; import net.lax1dude.eaglercraft.v1_8.Base64; +import net.lax1dude.eaglercraft.v1_8.Display; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; import net.lax1dude.eaglercraft.v1_8.crypto.GeneralDigest; @@ -108,6 +109,7 @@ public class HardwareFingerprint { _wglShaderSource(vert, GLSLHeader.getVertexHeaderCompat(vshLocalSrc, DrawUtils.vertexShaderPrecision)); _wglCompileShader(vert); if(_wglGetShaderi(vert, GL_COMPILE_STATUS) != GL_TRUE) { + Display.checkContextLost(); _wglDeleteShader(vert); GlStateManager.deleteTexture(helperTexture); return new byte[0]; @@ -118,6 +120,7 @@ public class HardwareFingerprint { _wglShaderSource(frag, GLSLHeader.getFragmentHeaderCompat(EagRuntime.getRequiredResourceString("/assets/eagler/glsl/hw_fingerprint.fsh"), shaderPrecision)); _wglCompileShader(frag); if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) { + Display.checkContextLost(); _wglDeleteShader(vert); _wglDeleteShader(frag); GlStateManager.deleteTexture(helperTexture); @@ -142,6 +145,7 @@ public class HardwareFingerprint { _wglDeleteShader(frag); if(_wglGetProgrami(program, GL_LINK_STATUS) != GL_TRUE) { + Display.checkContextLost(); _wglDeleteProgram(program); GlStateManager.deleteTexture(helperTexture); return new byte[0]; diff --git a/src/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/wasm_gc_teavm/TextDecoder.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/ContextLostError.java similarity index 64% rename from src/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/wasm_gc_teavm/TextDecoder.java rename to src/main/java/net/lax1dude/eaglercraft/v1_8/internal/ContextLostError.java index cc2cabaf..a8f07b91 100755 --- a/src/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/wasm_gc_teavm/TextDecoder.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/ContextLostError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 lax1dude. All Rights Reserved. + * Copyright (c) 2025 lax1dude. All Rights Reserved. * * 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 @@ -14,19 +14,12 @@ * */ -package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm; +package net.lax1dude.eaglercraft.v1_8.internal; -import org.teavm.jso.JSClass; -import org.teavm.jso.JSObject; -import org.teavm.jso.core.JSString; -import org.teavm.jso.typedarrays.Uint8Array; +public class ContextLostError extends Error { -@JSClass -public class TextDecoder implements JSObject { - - public TextDecoder(String encoding) { + public ContextLostError() { + super("WebGL context lost! Please refresh the page to continue"); } - public native JSString decode(Uint8Array buffer); - -} \ No newline at end of file +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IVertexArrayGL.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IVertexArrayGL.java index 9f569c0b..252b3060 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IVertexArrayGL.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IVertexArrayGL.java @@ -18,4 +18,10 @@ package net.lax1dude.eaglercraft.v1_8.internal; public interface IVertexArrayGL extends IObjectGL { + int getBits(); + + void setBit(int bit); + + void unsetBit(int bit); + } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/MainMenuSkyboxTexture.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/MainMenuSkyboxTexture.java index 014c97e4..528f4e9b 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/MainMenuSkyboxTexture.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/MainMenuSkyboxTexture.java @@ -55,10 +55,10 @@ public class MainMenuSkyboxTexture extends AbstractTexture { GlStateManager.bindTexture(tex); _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(tex), 0); + _wglDrawBuffers(_GL_COLOR_ATTACHMENT0); }else { _wglBindFramebuffer(_GL_FRAMEBUFFER, framebuffer); } - _wglDrawBuffers(new int[] { _GL_COLOR_ATTACHMENT0 }); } public void deleteGlTexture() { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/mojang/authlib/GameProfile.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/mojang/authlib/GameProfile.java index f20303c8..73890bf4 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/mojang/authlib/GameProfile.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/mojang/authlib/GameProfile.java @@ -78,8 +78,10 @@ public class GameProfile { } public String toString() { - return (new ToStringBuilder(this)).append("id", this.id).append("name", this.name) - .append("legacy", false).toString(); + return "GameProfile{id=" + this.id + ", name=" + this.name + ", legacy=false}"; + //TODO: uncomment once JS runtime is updated to newer TeaVM version + //return (new ToStringBuilder(this)).append("id", this.id).append("name", this.name) + // .append("legacy", false).toString(); } public boolean isLegacy() { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/mojang/authlib/TexturesProperty.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/mojang/authlib/TexturesProperty.java index 9a9fc2d9..9f635175 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/mojang/authlib/TexturesProperty.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/mojang/authlib/TexturesProperty.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 lax1dude. All Rights Reserved. + * Copyright (c) 2022-2025 lax1dude. All Rights Reserved. * * 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 @@ -22,48 +22,66 @@ import org.json.JSONObject; import net.lax1dude.eaglercraft.v1_8.ArrayUtils; import net.lax1dude.eaglercraft.v1_8.Base64; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.profile.SkinModel; +import net.lax1dude.eaglercraft.v1_8.profile.SkinPackets; public class TexturesProperty { public final String skin; - public final String model; + public final SkinModel model; public final String cape; - public final boolean eaglerPlayer; + public final byte eaglerPlayer; - public static final TexturesProperty defaultNull = new TexturesProperty(null, "default", null, false); + private EaglercraftUUID skinTextureUUID; - private TexturesProperty(String skin, String model, String cape, boolean eaglerPlayer) { + public static final TexturesProperty[] defaultNull = new TexturesProperty[] { + new TexturesProperty(null, SkinModel.STEVE, null, (byte) 0), + new TexturesProperty(null, SkinModel.STEVE, null, (byte) 1), + new TexturesProperty(null, SkinModel.STEVE, null, (byte) 2) + }; + + private TexturesProperty(String skin, SkinModel model, String cape, byte eaglerPlayer) { this.skin = skin; this.model = model; this.cape = cape; this.eaglerPlayer = eaglerPlayer; } + public EaglercraftUUID loadSkinTextureUUID() { + if(skinTextureUUID == null && skin != null) { + skinTextureUUID = SkinPackets.createEaglerURLSkinUUID(skin); + } + return skinTextureUUID; + } + public static TexturesProperty parseProfile(GameProfile profile) { + String str = null; + byte isEagler = 0; + Property prop; Collection etr = profile.getProperties().get("textures"); if(!etr.isEmpty()) { - Property prop = etr.iterator().next(); - String str; + prop = etr.iterator().next(); try { str = ArrayUtils.asciiString(Base64.decodeBase64(prop.getValue())); }catch(Throwable t) { - return defaultNull; - } - boolean isEagler = false; - etr = profile.getProperties().get("isEaglerPlayer"); - if(!etr.isEmpty()) { - prop = etr.iterator().next(); - isEagler = prop.getValue().equalsIgnoreCase("true"); } + } + etr = profile.getProperties().get("isEaglerPlayer"); + if(!etr.isEmpty()) { + prop = etr.iterator().next(); + isEagler = prop.getValue().equalsIgnoreCase("true") ? (byte) 2 : (byte) 1; + } + if(str != null) { return parseTextures(str, isEagler); }else { - return defaultNull; + return defaultNull[isEagler]; } } - public static TexturesProperty parseTextures(String string, boolean isEagler) { + public static TexturesProperty parseTextures(String string, byte isEagler) { String skin = null; - String model = "default"; + SkinModel model = SkinModel.STEVE; String cape = null; try { JSONObject json = new JSONObject(string); @@ -74,7 +92,10 @@ public class TexturesProperty { skin = skinObj.optString("url"); JSONObject meta = skinObj.optJSONObject("metadata"); if(meta != null) { - model = meta.optString("model", model); + String modelStr = meta.optString("model"); + if(modelStr != null && modelStr.equalsIgnoreCase("slim")) { + model = SkinModel.STEVE; + } } } JSONObject capeObj = json.optJSONObject("CAPE"); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/ServerNotificationManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/ServerNotificationManager.java index 78c29945..ac1c8449 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/ServerNotificationManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/notifications/ServerNotificationManager.java @@ -39,7 +39,6 @@ import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadg import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifIconsRegisterV4EAG; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifIconsReleaseV4EAG; import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.PacketImageData; -import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.util.IChatComponent; @@ -56,8 +55,8 @@ public class ServerNotificationManager { private final TextureManager textureMgr; protected int unreadCounter = 0; - public ServerNotificationManager() { - this.textureMgr = Minecraft.getMinecraft().getTextureManager(); + public ServerNotificationManager(TextureManager textureMgr) { + this.textureMgr = textureMgr; } public void processPacketAddIcons(SPacketNotifIconsRegisterV4EAG packet) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/DrawUtils.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/DrawUtils.java index 2ad88219..ebc2874f 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/DrawUtils.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/DrawUtils.java @@ -21,6 +21,7 @@ import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; import java.util.List; +import net.lax1dude.eaglercraft.v1_8.Display; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.internal.IVertexArrayGL; import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL; @@ -78,6 +79,7 @@ public class DrawUtils { _wglCompileShader(vshLocal); if(_wglGetShaderi(vshLocal, GL_COMPILE_STATUS) != GL_TRUE) { + Display.checkContextLost(); EaglercraftGPU.logger.error("Failed to compile GL_VERTEX_SHADER \"" + vertexShaderPath + "\"!"); String log = _wglGetShaderInfoLog(vshLocal); if(log != null) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java index 064d5e83..19330767 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java @@ -47,6 +47,11 @@ public class EaglercraftGPU { return _wglGenBuffers(); } + @Override + protected void invalidate(IBufferGL object) { + // Don't bother + } + @Override protected void destroy(IBufferGL object) { _wglDeleteBuffers(object); @@ -61,6 +66,11 @@ public class EaglercraftGPU { return _wglGenBuffers(); } + @Override + protected void invalidate(IBufferGL object) { + // Don't bother + } + @Override protected void destroy(IBufferGL object) { _wglDeleteBuffers(object); @@ -68,13 +78,32 @@ public class EaglercraftGPU { }; - static final GLObjectRecycler VAORecycler = new GLObjectRecycler(128) { + static final GLObjectRecycler VAORecycler = new GLObjectRecycler(256) { @Override protected IVertexArrayGL create() { return _wglGenVertexArrays(); } + @Override + protected void invalidate(IVertexArrayGL object) { + int i; + int bits = object.getBits(); + if(bits != 0) { + IVertexArrayGL old = currentVertexArray; + if (old != object) { + _wglBindVertexArray(object); + } + do { + i = Integer.numberOfTrailingZeros(bits); + _wglDisableVertexAttribArray(i); + } while((bits &= ~((i << 1) - 1)) != 0); + if (old != object) { + _wglBindVertexArray(old); + } + } + } + @Override protected void destroy(IVertexArrayGL object) { _wglDeleteVertexArrays(object); @@ -186,7 +215,7 @@ public class EaglercraftGPU { dp.bindQuad32 = false; } if(dp.vertexBuffer == null) { - dp.vertexBuffer = _wglGenBuffers(); + dp.vertexBuffer = createGLArrayBuffer(); } bindVAOGLArrayBufferNow(dp.vertexBuffer); @@ -250,7 +279,7 @@ public class EaglercraftGPU { bindGLVertexArray(dp.vertexArray); if(dp.mode == GL_QUADS) { int cnt = dp.count; - if(cnt > 0xFFFF) { + if(cnt > quad16MaxVertices) { if(!dp.bindQuad32) { dp.bindQuad16 = false; dp.bindQuad32 = true; @@ -258,16 +287,14 @@ public class EaglercraftGPU { }else { attachQuad32EmulationBuffer(cnt, false); } - p.drawElements(GL_TRIANGLES, cnt + (cnt >> 1), GL_UNSIGNED_INT, 0); + p.drawElements(GL_TRIANGLES, (cnt >> 2) * 6, GL_UNSIGNED_INT, 0); }else { if(!dp.bindQuad16) { dp.bindQuad16 = true; dp.bindQuad32 = false; - attachQuad16EmulationBuffer(cnt, true); - }else { - attachQuad16EmulationBuffer(cnt, false); + attachQuad16EmulationBuffer(true); } - p.drawElements(GL_TRIANGLES, cnt + (cnt >> 1), GL_UNSIGNED_SHORT, 0); + p.drawElements(GL_TRIANGLES, (cnt >> 2) * 6, GL_UNSIGNED_SHORT, 0); } }else { p.drawArrays(dp.mode, 0, dp.count); @@ -436,7 +463,7 @@ public class EaglercraftGPU { } public static void destroyGLArrayBuffer(IBufferGL buffer) { - arrayBufferRecycler.destroy(buffer); + arrayBufferRecycler.destroyObject(buffer); } public static IBufferGL createGLElementArrayBuffer() { @@ -444,7 +471,7 @@ public class EaglercraftGPU { } public static void destroyGLElementArrayBuffer(IBufferGL buffer) { - elementArrayBufferRecycler.destroy(buffer); + elementArrayBufferRecycler.destroyObject(buffer); } public static boolean areVAOsEmulated() { @@ -461,7 +488,7 @@ public class EaglercraftGPU { public static void destroyGLVertexArray(IVertexArrayGL buffer) { if(!emulatedVAOs) { - VAORecycler.destroy(buffer); + VAORecycler.destroyObject(buffer); } } @@ -694,7 +721,7 @@ public class EaglercraftGPU { public static final int CLEAR_BINDING_TEXTURE = 1; public static final int CLEAR_BINDING_TEXTURE0 = 2; public static final int CLEAR_BINDING_ACTIVE_TEXTURE = 4; - public static final int CLEAR_BINDING_BUFFER_ARRAY = 8; + public static final int CLEAR_BINDING_VERTEX_ARRAY = 8; public static final int CLEAR_BINDING_ARRAY_BUFFER = 16; public static final int CLEAR_BINDING_SHADER_PROGRAM = 32; @@ -712,7 +739,7 @@ public class EaglercraftGPU { GlStateManager.activeTexture = 0; _wglActiveTexture(GL_TEXTURE0); } - if((mask & CLEAR_BINDING_BUFFER_ARRAY) != 0) { + if((mask & CLEAR_BINDING_VERTEX_ARRAY) != 0) { currentVertexArray = null; } if((mask & CLEAR_BINDING_ARRAY_BUFFER) != 0) { @@ -757,11 +784,11 @@ public class EaglercraftGPU { private static long lastRecyclerFlush = 0l; public static void optimize() { - FixedFunctionPipeline.optimize(); long millis = EagRuntime.steadyTimeMillis(); if(millis - lastRecyclerFlush > 120000l) { lastRecyclerFlush = millis; arrayBufferRecycler.compact(); + elementArrayBufferRecycler.compact(); VAORecycler.compact(); } } @@ -778,34 +805,21 @@ public class EaglercraftGPU { lastRender.update().drawDirectArrays(lastMode, 0, lastCount); } + public static final int quad16MaxVertices = 65536; + private static IBufferGL quad16EmulationBuffer = null; - private static int quad16EmulationBufferSize = 0; private static IBufferGL quad32EmulationBuffer = null; private static int quad32EmulationBufferSize = 0; - public static void attachQuad16EmulationBuffer(int vertexCount, boolean bind) { + public static void attachQuad16EmulationBuffer(boolean bind) { IBufferGL buf = quad16EmulationBuffer; if(buf == null) { quad16EmulationBuffer = buf = _wglGenBuffers(); - int newSize = quad16EmulationBufferSize = (vertexCount & 0xFFFFF000) + 0x2000; - if(newSize > 0xFFFF) { - newSize = 0xFFFF; - } EaglercraftGPU.bindVAOGLElementArrayBufferNow(buf); - resizeQuad16EmulationBuffer(newSize >> 2); - }else { - int cnt = quad16EmulationBufferSize; - if(cnt < vertexCount) { - int newSize = quad16EmulationBufferSize = (vertexCount & 0xFFFFF000) + 0x2000; - if(newSize > 0xFFFF) { - newSize = 0xFFFF; - } - EaglercraftGPU.bindVAOGLElementArrayBufferNow(buf); - resizeQuad16EmulationBuffer(newSize >> 2); - }else if(bind) { - EaglercraftGPU.bindVAOGLElementArrayBuffer(buf); - } + resizeQuad16EmulationBuffer(quad16MaxVertices >> 2); + }else if(bind) { + EaglercraftGPU.bindVAOGLElementArrayBuffer(buf); } } @@ -813,13 +827,13 @@ public class EaglercraftGPU { IBufferGL buf = quad32EmulationBuffer; if(buf == null) { quad32EmulationBuffer = buf = _wglGenBuffers(); - int newSize = quad32EmulationBufferSize = (vertexCount & 0xFFFFC000) + 0x8000; + int newSize = quad32EmulationBufferSize = (vertexCount + 0xFFFF) & 0xFFFF0000; EaglercraftGPU.bindVAOGLElementArrayBufferNow(buf); resizeQuad32EmulationBuffer(newSize >> 2); }else { int cnt = quad32EmulationBufferSize; if(cnt < vertexCount) { - int newSize = quad32EmulationBufferSize = (vertexCount & 0xFFFFC000) + 0x8000; + int newSize = quad32EmulationBufferSize = (vertexCount + 0xFFFF) & 0xFFFF0000; EaglercraftGPU.bindVAOGLElementArrayBufferNow(buf); resizeQuad32EmulationBuffer(newSize >> 2); }else if(bind) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EffectPipelineFXAA.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EffectPipelineFXAA.java index 06753e60..b5ec7685 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EffectPipelineFXAA.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EffectPipelineFXAA.java @@ -26,6 +26,9 @@ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import net.lax1dude.eaglercraft.v1_8.Display; + import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; import net.lax1dude.eaglercraft.v1_8.EagRuntime; @@ -41,8 +44,9 @@ public class EffectPipelineFXAA { private static final int _GL_RENDERBUFFER = 0x8D41; private static final int _GL_COLOR_ATTACHMENT0 = 0x8CE0; private static final int _GL_DEPTH_ATTACHMENT = 0x8D00; - private static final int _GL_DEPTH_COMPONENT16 = 0x81A5; private static final int _GL_DEPTH_COMPONENT32F = 0x8CAC; + private static final int _GL_DEPTH_STENCIL_ATTACHMENT = 0x821A; + private static final int _GL_DEPTH_STENCIL = 0x84F9; private static IProgramGL shaderProgram = null; private static IUniformGL u_screenSize2f = null; @@ -63,6 +67,7 @@ public class EffectPipelineFXAA { _wglCompileShader(frag); if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) { + Display.checkContextLost(); logger.error("Failed to compile GL_FRAGMENT_SHADER \"" + fragmentShaderPath + "\" for EffectPipelineFXAA!"); String log = _wglGetShaderInfoLog(frag); if(log != null) { @@ -91,6 +96,7 @@ public class EffectPipelineFXAA { _wglDeleteShader(frag); if(_wglGetProgrami(shaderProgram, GL_LINK_STATUS) != GL_TRUE) { + Display.checkContextLost(); logger.error("Failed to link shader program for EffectPipelineFXAA!"); String log = _wglGetProgramInfoLog(shaderProgram); if(log != null) { @@ -121,8 +127,11 @@ public class EffectPipelineFXAA { _wglBindRenderbuffer(_GL_RENDERBUFFER, framebufferDepth); _wglBindFramebuffer(_GL_FRAMEBUFFER, framebuffer); - _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(framebufferColor), 0); - _wglFramebufferRenderbuffer(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, _GL_RENDERBUFFER, framebufferDepth); + _wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + EaglercraftGPU.getNativeTexture(framebufferColor), 0); + _wglFramebufferRenderbuffer(_GL_FRAMEBUFFER, + EaglercraftGPU.checkOpenGLESVersion() == 200 ? _GL_DEPTH_STENCIL_ATTACHMENT : _GL_DEPTH_ATTACHMENT, + _GL_RENDERBUFFER, framebufferDepth); _wglBindFramebuffer(_GL_FRAMEBUFFER, null); } @@ -136,7 +145,8 @@ public class EffectPipelineFXAA { EaglercraftGPU.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); _wglBindRenderbuffer(_GL_RENDERBUFFER, framebufferDepth); - _wglRenderbufferStorage(_GL_RENDERBUFFER, EaglercraftGPU.checkOpenGLESVersion() == 200 ? _GL_DEPTH_COMPONENT16 : _GL_DEPTH_COMPONENT32F, width, height); + _wglRenderbufferStorage(_GL_RENDERBUFFER, EaglercraftGPU.checkOpenGLESVersion() == 200 ? _GL_DEPTH_STENCIL + : _GL_DEPTH_COMPONENT32F, width, height); } _wglBindFramebuffer(_GL_FRAMEBUFFER, framebuffer); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionPipeline.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionPipeline.java index 40885ee2..60c4b64f 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionPipeline.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionPipeline.java @@ -24,7 +24,7 @@ import java.util.List; import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; - +import net.lax1dude.eaglercraft.v1_8.Display; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.internal.IVertexArrayGL; import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; @@ -153,7 +153,7 @@ public class FixedFunctionPipeline { EaglercraftGPU.bindGLShaderProgram(shaderProgram); if(mode == GL_QUADS) { StreamBufferInstance sb = currentVertexArray; - if(count > 0xFFFF) { + if(count > EaglercraftGPU.quad16MaxVertices) { if(!sb.bindQuad32) { sb.bindQuad16 = false; sb.bindQuad32 = true; @@ -161,18 +161,14 @@ public class FixedFunctionPipeline { }else { EaglercraftGPU.attachQuad32EmulationBuffer(count, false); } - EaglercraftGPU.drawElements(GL_TRIANGLES, count + (count >> 1), - GL_UNSIGNED_INT, 0); + EaglercraftGPU.drawElements(GL_TRIANGLES, (count >> 2) * 6, GL_UNSIGNED_INT, 0); }else { if(!sb.bindQuad16) { sb.bindQuad16 = true; sb.bindQuad32 = false; - EaglercraftGPU.attachQuad16EmulationBuffer(count, true); - }else { - EaglercraftGPU.attachQuad16EmulationBuffer(count, false); + EaglercraftGPU.attachQuad16EmulationBuffer(true); } - EaglercraftGPU.drawElements(GL_TRIANGLES, count + (count >> 1), - GL_UNSIGNED_SHORT, 0); + EaglercraftGPU.drawElements(GL_TRIANGLES, (count >> 2) * 6, GL_UNSIGNED_SHORT, 0); } }else { EaglercraftGPU.drawArrays(mode, offset, count); @@ -291,6 +287,7 @@ public class FixedFunctionPipeline { _wglCompileShader(vsh); if(_wglGetShaderi(vsh, GL_COMPILE_STATUS) != GL_TRUE) { + Display.checkContextLost(); LOGGER.error("Failed to compile GL_VERTEX_SHADER for state {} !", (visualizeBits(coreBits) + (enableExt && extBits != 0 ? " ext " + visualizeBits(extBits) : ""))); String log = _wglGetShaderInfoLog(vsh); if(log != null) { @@ -309,6 +306,7 @@ public class FixedFunctionPipeline { _wglCompileShader(fsh); if(_wglGetShaderi(fsh, GL_COMPILE_STATUS) != GL_TRUE) { + Display.checkContextLost(); LOGGER.error("Failed to compile GL_FRAGMENT_SHADER for state {} !", (visualizeBits(coreBits) + (enableExt && extBits != 0 ? " ext " + visualizeBits(extBits) : ""))); String log = _wglGetShaderInfoLog(fsh); if(log != null) { @@ -563,6 +561,7 @@ public class FixedFunctionPipeline { _wglLinkProgram(compiledProg); if(_wglGetProgrami(compiledProg, GL_LINK_STATUS) != GL_TRUE) { + Display.checkContextLost(); LOGGER.error("Program could not be linked for state {} !", (visualizeBits(bits) + (extensionProvider != null && extBits != 0 ? " ext " + visualizeBits(extBits) : ""))); String log = _wglGetProgramInfoLog(compiledProg); if(log != null) { @@ -574,8 +573,7 @@ public class FixedFunctionPipeline { throw new IllegalStateException("Program could not be linked!"); } - streamBuffer = new StreamBuffer(FixedFunctionShader.initialSize, FixedFunctionShader.initialCount, - FixedFunctionShader.maxCount, (vertexArray, vertexBuffer) -> { + streamBuffer = new StreamBuffer((vertexArray, vertexBuffer) -> { EaglercraftGPU.bindGLVertexArray(vertexArray); EaglercraftGPU.bindVAOGLArrayBuffer(vertexBuffer); @@ -1063,12 +1061,6 @@ public class FixedFunctionPipeline { return this; } - static void optimize() { - for(int i = 0, l = pipelineListTracker.size(); i < l; ++i) { - pipelineListTracker.get(i).streamBuffer.optimize(); - } - } - public static void flushCache() { shaderSourceCacheVSH = null; shaderSourceCacheFSH = null; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionShader.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionShader.java index 61e55e4d..f085c242 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionShader.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionShader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. + * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. * * 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 @@ -18,10 +18,6 @@ package net.lax1dude.eaglercraft.v1_8.opengl; public class FixedFunctionShader { - public static final int initialSize = 0x8000; - public static final int initialCount = 3; - public static final int maxCount = 8; - public class FixedFunctionState { public static final int fixedFunctionStatesCount = 12; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GLObjectRecycler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GLObjectRecycler.java index 214a65d5..a76d28c3 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GLObjectRecycler.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/GLObjectRecycler.java @@ -40,6 +40,7 @@ public abstract class GLObjectRecycler { } public void destroyObject(T obj) { + invalidate(obj); deletedObjects.addLast(obj); } @@ -51,6 +52,8 @@ public abstract class GLObjectRecycler { protected abstract T create(); + protected abstract void invalidate(T object); + protected abstract void destroy(T object); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/InstancedFontRenderer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/InstancedFontRenderer.java index dd830d5d..420dcb34 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/InstancedFontRenderer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/InstancedFontRenderer.java @@ -19,6 +19,7 @@ package net.lax1dude.eaglercraft.v1_8.opengl; import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; +import net.lax1dude.eaglercraft.v1_8.Display; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.internal.IVertexArrayGL; import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL; @@ -91,6 +92,7 @@ public class InstancedFontRenderer { _wglCompileShader(vert); if(_wglGetShaderi(vert, GL_COMPILE_STATUS) != GL_TRUE) { + Display.checkContextLost(); logger.error("Failed to compile GL_VERTEX_SHADER \"" + vertexShaderPath + "\" for InstancedFontRenderer!"); String log = _wglGetShaderInfoLog(vert); if(log != null) { @@ -106,6 +108,7 @@ public class InstancedFontRenderer { _wglCompileShader(frag); if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) { + Display.checkContextLost(); logger.error("Failed to compile GL_FRAGMENT_SHADER \"" + fragmentShaderPath + "\" for InstancedFontRenderer!"); String log = _wglGetShaderInfoLog(frag); if(log != null) { @@ -135,6 +138,7 @@ public class InstancedFontRenderer { _wglDeleteShader(frag); if(_wglGetProgrami(shaderProgram, GL_LINK_STATUS) != GL_TRUE) { + Display.checkContextLost(); logger.error("Failed to link shader program for InstancedFontRenderer!"); String log = _wglGetProgramInfoLog(shaderProgram); if(log != null) { @@ -203,7 +207,7 @@ public class InstancedFontRenderer { EaglercraftGPU.vertexAttribDivisor(0, 0); EaglercraftGPU.bindVAOGLArrayBufferNow(instancesBuffer); - _wglBufferData(GL_ARRAY_BUFFER, fontDataBuffer.remaining(), GL_STREAM_DRAW); + _wglBufferData(GL_ARRAY_BUFFER, fontDataBuffer.capacity(), GL_STREAM_DRAW); EaglercraftGPU.enableVertexAttribArray(1); EaglercraftGPU.vertexAttribPointer(1, 2, GL_SHORT, false, 10, 0); @@ -377,6 +381,7 @@ public class InstancedFontRenderer { int l = fontDataBuffer.limit(); fontDataBuffer.flip(); + _wglBufferData(GL_ARRAY_BUFFER, fontDataBuffer.capacity(), GL_STREAM_DRAW); _wglBufferSubData(GL_ARRAY_BUFFER, 0, fontDataBuffer); fontDataBuffer.position(p); @@ -390,6 +395,7 @@ public class InstancedFontRenderer { int l = fontBoldDataBuffer.limit(); fontBoldDataBuffer.flip(); + _wglBufferData(GL_ARRAY_BUFFER, fontBoldDataBuffer.capacity(), GL_STREAM_DRAW); _wglBufferSubData(GL_ARRAY_BUFFER, 0, fontBoldDataBuffer); fontBoldDataBuffer.position(p); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/InstancedParticleRenderer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/InstancedParticleRenderer.java index 48720f95..4f5fd333 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/InstancedParticleRenderer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/InstancedParticleRenderer.java @@ -19,6 +19,7 @@ package net.lax1dude.eaglercraft.v1_8.opengl; import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; +import net.lax1dude.eaglercraft.v1_8.Display; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.internal.IVertexArrayGL; import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL; @@ -92,6 +93,7 @@ public class InstancedParticleRenderer { _wglCompileShader(vert); if(_wglGetShaderi(vert, GL_COMPILE_STATUS) != GL_TRUE) { + Display.checkContextLost(); logger.error("Failed to compile GL_VERTEX_SHADER \"" + vertexShaderPath + "\" for InstancedParticleRenderer!"); String log = _wglGetShaderInfoLog(vert); if(log != null) { @@ -107,6 +109,7 @@ public class InstancedParticleRenderer { _wglCompileShader(frag); if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) { + Display.checkContextLost(); logger.error("Failed to compile GL_FRAGMENT_SHADER \"" + fragmentShaderPath + "\" for InstancedParticleRenderer!"); String log = _wglGetShaderInfoLog(frag); if(log != null) { @@ -136,6 +139,7 @@ public class InstancedParticleRenderer { _wglDeleteShader(frag); if(_wglGetProgrami(shaderProgram, GL_LINK_STATUS) != GL_TRUE) { + Display.checkContextLost(); logger.error("Failed to link shader program for InstancedParticleRenderer!"); String log = _wglGetProgramInfoLog(shaderProgram); if(log != null) { @@ -184,7 +188,7 @@ public class InstancedParticleRenderer { EaglercraftGPU.vertexAttribDivisor(0, 0); EaglercraftGPU.bindVAOGLArrayBufferNow(instancesBuffer); - _wglBufferData(GL_ARRAY_BUFFER, particleBuffer.remaining(), GL_STREAM_DRAW); + _wglBufferData(GL_ARRAY_BUFFER, particleBuffer.capacity(), GL_STREAM_DRAW); EaglercraftGPU.enableVertexAttribArray(1); EaglercraftGPU.vertexAttribPointer(1, 3, GL_FLOAT, false, 24, 0); @@ -311,6 +315,7 @@ public class InstancedParticleRenderer { int l = particleBuffer.limit(); particleBuffer.flip(); + _wglBufferData(GL_ARRAY_BUFFER, particleBuffer.capacity(), GL_STREAM_DRAW); _wglBufferSubData(GL_ARRAY_BUFFER, 0, particleBuffer); particleBuffer.position(p); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SoftGLVertexArray.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SoftGLVertexArray.java index 203384fe..278da048 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SoftGLVertexArray.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SoftGLVertexArray.java @@ -223,4 +223,17 @@ class SoftGLVertexArray implements IVertexArrayGL { } + @Override + public int getBits() { + return enabled; + } + + @Override + public void setBit(int bit) { + } + + @Override + public void unsetBit(int bit) { + } + } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SpriteLevelMixer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SpriteLevelMixer.java index c8bac921..2c2e986e 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SpriteLevelMixer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/SpriteLevelMixer.java @@ -22,6 +22,7 @@ import java.util.List; import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; +import net.lax1dude.eaglercraft.v1_8.Display; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; @@ -78,6 +79,7 @@ public class SpriteLevelMixer { _wglCompileShader(frag); if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) { + Display.checkContextLost(); LOGGER.error("Failed to compile GL_FRAGMENT_SHADER \"" + fragmentShaderPath + "\" for SpriteLevelMixer!"); String log = _wglGetShaderInfoLog(frag); if(log != null) { @@ -106,6 +108,7 @@ public class SpriteLevelMixer { _wglDeleteShader(frag); if(_wglGetProgrami(shaderProgram, GL_LINK_STATUS) != GL_TRUE) { + Display.checkContextLost(); LOGGER.error("Failed to link shader program for SpriteLevelMixer!"); String log = _wglGetProgramInfoLog(shaderProgram); if(log != null) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/StreamBuffer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/StreamBuffer.java index 2f1a2078..b550aa53 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/StreamBuffer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/StreamBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. + * Copyright (c) 2023-2025 lax1dude. All Rights Reserved. * * 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 @@ -24,11 +24,7 @@ import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; public class StreamBuffer { - public static final int poolSize = 16; - - public final int initialSize; - public final int initialCount; - public final int maxCount; + public static final int poolSize = 4; protected static final PoolInstance[] pool = new PoolInstance[poolSize]; protected static int poolBufferID = 0; @@ -55,21 +51,23 @@ public class StreamBuffer { } private static void resizeInstance(PoolInstance instance, int requiredMemory) { - if(instance.vertexBuffer == null) { - instance.vertexBuffer = _wglGenBuffers(); + IBufferGL buffer = instance.vertexBuffer; + if (buffer == null) { + buffer = _wglGenBuffers(); + instance.vertexBuffer = buffer; } - if(instance.vertexBufferSize < requiredMemory) { - int newSize = (requiredMemory & 0xFFFFF000) + 0x2000; - EaglercraftGPU.bindGLArrayBuffer(instance.vertexBuffer); - _wglBufferData(GL_ARRAY_BUFFER, newSize, GL_STREAM_DRAW); + int newSize = instance.vertexBufferSize; + if (newSize < requiredMemory) { + newSize = (requiredMemory + 0xFFFF) & 0xFFFF0000; instance.vertexBufferSize = newSize; } + EaglercraftGPU.bindGLArrayBuffer(buffer); + _wglBufferData(GL_ARRAY_BUFFER, newSize, GL_STREAM_DRAW); } protected StreamBufferInstance[] buffers; protected int currentBufferId = 0; - protected int overflowCounter = 0; protected final IStreamBufferInitializer initializer; @@ -95,19 +93,20 @@ public class StreamBuffer { void initialize(IVertexArrayGL vertexArray, IBufferGL vertexBuffer); } - public StreamBuffer(int initialSize, int initialCount, int maxCount, IStreamBufferInitializer initializer) { - if(maxCount > poolSize) { - maxCount = poolSize; + public StreamBuffer(IStreamBufferInitializer initializer) { + this(poolSize, initializer); + } + + public StreamBuffer(int count, IStreamBufferInitializer initializer) { + if(count > poolSize) { + count = poolSize; } - this.buffers = new StreamBufferInstance[initialCount]; + this.buffers = new StreamBufferInstance[count]; for(int i = 0; i < this.buffers.length; ++i) { StreamBufferInstance j = new StreamBufferInstance(); j.poolInstance = fillPoolInstance(); this.buffers[i] = j; } - this.initialSize = initialSize; - this.initialCount = initialCount; - this.maxCount = maxCount; this.initializer = initializer; } @@ -121,67 +120,6 @@ public class StreamBuffer { return next; } - public void optimize() { - overflowCounter += currentBufferId - buffers.length; - if(overflowCounter < -25) { - int newCount = buffers.length - 1 + ((overflowCounter + 25) / 5); - if(newCount < initialCount) { - newCount = initialCount; - } - if(newCount < buffers.length) { - StreamBufferInstance[] newArray = new StreamBufferInstance[newCount]; - for(int i = 0; i < buffers.length; ++i) { - if(i < newArray.length) { - newArray[i] = buffers[i]; - }else { - if(buffers[i].vertexArray != null) { - EaglercraftGPU.destroyGLVertexArray(buffers[i].vertexArray); - } - } - } - buffers = newArray; - refill(); - } - overflowCounter = 0; - }else if(overflowCounter > 15) { - int newCount = buffers.length + 1 + ((overflowCounter - 15) / 5); - if(newCount > maxCount) { - newCount = maxCount; - } - if(newCount > buffers.length) { - StreamBufferInstance[] newArray = new StreamBufferInstance[newCount]; - for(int i = 0; i < newArray.length; ++i) { - if(i < buffers.length) { - newArray[i] = buffers[i]; - }else { - newArray[i] = new StreamBufferInstance(); - } - } - buffers = newArray; - refill(); - } - overflowCounter = 0; - } - currentBufferId = 0; - } - - private void refill() { - for(int i = 0; i < buffers.length; ++i) { - PoolInstance j = fillPoolInstance(); - StreamBufferInstance k = buffers[i]; - if(j != k.poolInstance) { - PoolInstance l = k.poolInstance; - k.poolInstance = j; - if(k.vertexArray != null) { - if(j.vertexBuffer == null) { - resizeInstance(j, l.vertexBufferSize); - } - initializer.initialize(k.vertexArray, j.vertexBuffer); - } - } - } - } - public void destroy() { for(int i = 0; i < buffers.length; ++i) { StreamBufferInstance next = buffers[i]; @@ -189,12 +127,6 @@ public class StreamBuffer { EaglercraftGPU.destroyGLVertexArray(next.vertexArray); } } - buffers = new StreamBufferInstance[initialCount]; - for(int i = 0; i < initialCount; ++i) { - StreamBufferInstance j = new StreamBufferInstance(); - j.poolInstance = fillPoolInstance(); - buffers[i] = j; - } } public static void destroyPool() { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/TextureCopyUtil.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/TextureCopyUtil.java index 3de955aa..887ca02c 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/TextureCopyUtil.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/TextureCopyUtil.java @@ -21,6 +21,7 @@ import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; import java.util.List; +import net.lax1dude.eaglercraft.v1_8.Display; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; @@ -100,6 +101,7 @@ public class TextureCopyUtil { _wglCompileShader(vshShader); if(_wglGetShaderi(vshShader, GL_COMPILE_STATUS) != GL_TRUE) { + Display.checkContextLost(); LOGGER.error("Failed to compile GL_VERTEX_SHADER \"" + vertexShaderPath + "\" for TextureCopyUtil!"); String log = _wglGetShaderInfoLog(vshShader); if(log != null) { @@ -126,6 +128,7 @@ public class TextureCopyUtil { _wglCompileShader(frag); if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) { + Display.checkContextLost(); LOGGER.error("Failed to compile GL_FRAGMENT_SHADER \"" + fragmentShaderPath + "\" for TextureCopyUtil!"); String log = _wglGetShaderInfoLog(frag); if(log != null) { @@ -154,6 +157,7 @@ public class TextureCopyUtil { _wglDeleteShader(frag); if(_wglGetProgrami(shaderProgram, GL_LINK_STATUS) != GL_TRUE) { + Display.checkContextLost(); LOGGER.error("Failed to link shader program for TextureCopyUtil!"); String log = _wglGetProgramInfoLog(shaderProgram); if(log != null) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java index b9d053d4..e8cbd087 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java @@ -1483,6 +1483,7 @@ public class EaglerDeferredPipeline { GlStateManager.globalEnableBlend(); GlStateManager.enableBlend(); GlStateManager.depthMask(false); + GlStateManager.cullFace(GL_FRONT); GlStateManager.tryBlendFuncSeparate(GL_ZERO, GL_SRC_COLOR, GL_ZERO, GL_ZERO); GlStateManager.enablePolygonOffset(); GlStateManager.doPolygonOffset(0.25f, 1.0f); @@ -1497,6 +1498,7 @@ public class EaglerDeferredPipeline { GlStateManager.disableBlend(); GlStateManager.globalDisableBlend(); GlStateManager.depthMask(true); + GlStateManager.cullFace(GL_BACK); GlStateManager.disablePolygonOffset(); GlStateManager.colorMask(false, false, false, false); DeferredStateManager.checkGLError("Post: endDrawColoredShadows()"); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/ForwardAcceleratedEffectRenderer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/ForwardAcceleratedEffectRenderer.java index 963abb8c..2994a9c2 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/ForwardAcceleratedEffectRenderer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/ForwardAcceleratedEffectRenderer.java @@ -92,7 +92,7 @@ public class ForwardAcceleratedEffectRenderer extends AbstractAcceleratedEffectR _wglVertexAttribDivisor(0, 0); EaglercraftGPU.bindGLArrayBuffer(instancesBuffer); - _wglBufferData(GL_ARRAY_BUFFER, particleBuffer.remaining(), GL_STREAM_DRAW); + _wglBufferData(GL_ARRAY_BUFFER, particleBuffer.capacity(), GL_STREAM_DRAW); _wglEnableVertexAttribArray(1); _wglVertexAttribPointer(1, 3, GL_FLOAT, false, 24, 0); @@ -149,6 +149,7 @@ public class ForwardAcceleratedEffectRenderer extends AbstractAcceleratedEffectR int l = particleBuffer.limit(); particleBuffer.flip(); + _wglBufferData(GL_ARRAY_BUFFER, particleBuffer.capacity(), GL_STREAM_DRAW); _wglBufferSubData(GL_ARRAY_BUFFER, 0, particleBuffer); particleBuffer.position(p); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/GBufferAcceleratedEffectRenderer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/GBufferAcceleratedEffectRenderer.java index ca56dfba..3cee4bd9 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/GBufferAcceleratedEffectRenderer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/GBufferAcceleratedEffectRenderer.java @@ -92,7 +92,7 @@ public class GBufferAcceleratedEffectRenderer extends AbstractAcceleratedEffectR _wglVertexAttribDivisor(0, 0); EaglercraftGPU.bindGLArrayBuffer(instancesBuffer); - _wglBufferData(GL_ARRAY_BUFFER, particleBuffer.remaining(), GL_STREAM_DRAW); + _wglBufferData(GL_ARRAY_BUFFER, particleBuffer.capacity(), GL_STREAM_DRAW); _wglEnableVertexAttribArray(1); _wglVertexAttribPointer(1, 3, GL_FLOAT, false, 24, 0); @@ -148,6 +148,7 @@ public class GBufferAcceleratedEffectRenderer extends AbstractAcceleratedEffectR int l = particleBuffer.limit(); particleBuffer.flip(); + _wglBufferData(GL_ARRAY_BUFFER, particleBuffer.capacity(), GL_STREAM_DRAW); _wglBufferSubData(GL_ARRAY_BUFFER, 0, particleBuffer); particleBuffer.position(p); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/LensFlareMeshRenderer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/LensFlareMeshRenderer.java index 738b5f9b..695a25d8 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/LensFlareMeshRenderer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/LensFlareMeshRenderer.java @@ -89,7 +89,7 @@ public class LensFlareMeshRenderer { streaksVertexArray = _wglGenVertexArrays(); EaglercraftGPU.bindGLVertexArray(streaksVertexArray); - EaglercraftGPU.attachQuad16EmulationBuffer(16, true); + EaglercraftGPU.attachQuad16EmulationBuffer(true); _wglEnableVertexAttribArray(0); _wglVertexAttribPointer(0, 2, GL_FLOAT, false, 16, 0); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderCompiler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderCompiler.java index 99a4fbfa..71fdb2b9 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderCompiler.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderCompiler.java @@ -22,6 +22,7 @@ import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; import java.util.Arrays; import java.util.List; +import net.lax1dude.eaglercraft.v1_8.Display; import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; @@ -69,6 +70,7 @@ public class ShaderCompiler { _wglCompileShader(ret); if(_wglGetShaderi(ret, GL_COMPILE_STATUS) != GL_TRUE) { + Display.checkContextLost(); logger.error("Failed to compile {} \"{}\" of program \"{}\"!", getStageName(stage), filename, name); String log = _wglGetShaderInfoLog(ret); if(log != null) { @@ -95,6 +97,7 @@ public class ShaderCompiler { _wglDetachShader(ret, frag); if(_wglGetProgrami(ret, GL_LINK_STATUS) != GL_TRUE) { + Display.checkContextLost(); logger.error("Failed to link program \"{}\"!", name); String log = _wglGetProgramInfoLog(ret); if(log != null) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/dynamiclights/DynamicLightsAcceleratedEffectRenderer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/dynamiclights/DynamicLightsAcceleratedEffectRenderer.java index e66c88c4..b1a63dc7 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/dynamiclights/DynamicLightsAcceleratedEffectRenderer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/dynamiclights/DynamicLightsAcceleratedEffectRenderer.java @@ -97,7 +97,7 @@ public class DynamicLightsAcceleratedEffectRenderer extends AbstractAcceleratedE _wglVertexAttribDivisor(0, 0); EaglercraftGPU.bindGLArrayBuffer(instancesBuffer); - _wglBufferData(GL_ARRAY_BUFFER, particleBuffer.remaining(), GL_STREAM_DRAW); + _wglBufferData(GL_ARRAY_BUFFER, particleBuffer.capacity(), GL_STREAM_DRAW); _wglEnableVertexAttribArray(1); _wglVertexAttribPointer(1, 3, GL_FLOAT, false, 24, 0); @@ -155,6 +155,7 @@ public class DynamicLightsAcceleratedEffectRenderer extends AbstractAcceleratedE int l = particleBuffer.limit(); particleBuffer.flip(); + _wglBufferData(GL_ARRAY_BUFFER, particleBuffer.capacity(), GL_STREAM_DRAW); _wglBufferSubData(GL_ARRAY_BUFFER, 0, particleBuffer); particleBuffer.position(p); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerProfile.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerProfile.java index a3d1f739..6c2372c1 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerProfile.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerProfile.java @@ -210,7 +210,6 @@ public class EaglerProfile { public static void handleForceSkinPreset(int preset) { isServerSkinOverride = true; overridePresetSkinId = preset; - ServerSkinCache.needReloadClientSkin = true; } public static void handleForceSkinCustom(int modelID, byte[] datav3) { @@ -229,13 +228,11 @@ public class EaglerProfile { }else { overrideCustomSkin.copyPixelsIn(datav3); } - ServerSkinCache.needReloadClientSkin = true; } public static void handleForceCapePreset(int preset) { isServerCapeOverride = true; overridePresetCapeId = preset; - ServerCapeCache.needReloadClientCape = true; } public static void handleForceCapeCustom(byte[] custom) { @@ -252,7 +249,6 @@ public class EaglerProfile { }else { overrideCustomCape.copyPixelsIn(pixels32x32); } - ServerCapeCache.needReloadClientCape = true; } public static void clearServerSkinOverride() { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiAuthenticationScreen.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiAuthenticationScreen.java index 5ba46f42..e699d47f 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiAuthenticationScreen.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiAuthenticationScreen.java @@ -19,7 +19,7 @@ package net.lax1dude.eaglercraft.v1_8.profile; import net.lax1dude.eaglercraft.v1_8.Keyboard; import net.lax1dude.eaglercraft.v1_8.internal.KeyboardConstants; import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; -import net.lax1dude.eaglercraft.v1_8.socket.ConnectionHandshake; +import net.lax1dude.eaglercraft.v1_8.socket.GuiHandshakeApprove; import net.lax1dude.eaglercraft.v1_8.socket.HandshakePacketTypes; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; @@ -64,7 +64,7 @@ public class GuiAuthenticationScreen extends GuiScreen { public void initGui() { if(authTypeForWarning != Integer.MAX_VALUE) { - GuiScreen scr = ConnectionHandshake.displayAuthProtocolConfirm(authTypeForWarning, parent, this); + GuiScreen scr = GuiHandshakeApprove.displayAuthProtocolConfirm(authTypeForWarning, parent, this); authTypeForWarning = Integer.MAX_VALUE; if(scr != null) { mc.displayGuiScreen(scr); @@ -90,7 +90,7 @@ public class GuiAuthenticationScreen extends GuiScreen { protected void actionPerformed(GuiButton parGuiButton) { if(parGuiButton.id == 1) { - this.mc.displayGuiScreen(new GuiConnecting(retAfterAuthScreen, password.getText())); + this.mc.displayGuiScreen(new GuiConnecting(retAfterAuthScreen, password.getText(), allowPlaintext)); }else { this.mc.displayGuiScreen(parent); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerCapeCache.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerCapeCache.java deleted file mode 100755 index ed9b3b09..00000000 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerCapeCache.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. - * - * 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. - * - */ - -package net.lax1dude.eaglercraft.v1_8.profile; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketGetOtherCapeEAG; -import net.minecraft.client.network.NetHandlerPlayClient; -import net.minecraft.client.renderer.texture.TextureManager; -import net.minecraft.util.ResourceLocation; - -public class ServerCapeCache { - - private static final Logger logger = LogManager.getLogger("ServerCapeCache"); - - public class CapeCacheEntry { - - protected final boolean isPresetCape; - protected final int presetCapeId; - protected final CacheCustomCape customCape; - - protected long lastCacheHit = EagRuntime.steadyTimeMillis(); - - protected CapeCacheEntry(EaglerSkinTexture textureInstance, ResourceLocation resourceLocation) { - this.isPresetCape = false; - this.presetCapeId = -1; - this.customCape = new CacheCustomCape(textureInstance, resourceLocation); - ServerCapeCache.this.textureManager.loadTexture(resourceLocation, textureInstance); - } - - /** - * Use only for the constant for the client player - */ - protected CapeCacheEntry(ResourceLocation resourceLocation) { - this.isPresetCape = false; - this.presetCapeId = -1; - this.customCape = new CacheCustomCape(null, resourceLocation); - } - - protected CapeCacheEntry(int presetSkinId) { - this.isPresetCape = true; - this.presetCapeId = presetSkinId; - this.customCape = null; - } - - public ResourceLocation getResourceLocation() { - if(isPresetCape) { - return DefaultCapes.getCapeFromId(presetCapeId).location; - }else { - if(customCape != null) { - return customCape.resourceLocation; - }else { - return null; - } - } - } - - protected void free() { - if(!isPresetCape && customCape.resourceLocation != null) { - ServerCapeCache.this.textureManager.deleteTexture(customCape.resourceLocation); - } - } - - } - - protected static class CacheCustomCape { - - protected final EaglerSkinTexture textureInstance; - protected final ResourceLocation resourceLocation; - - protected CacheCustomCape(EaglerSkinTexture textureInstance, ResourceLocation resourceLocation) { - this.textureInstance = textureInstance; - this.resourceLocation = resourceLocation; - } - - } - - private final CapeCacheEntry defaultCacheEntry = new CapeCacheEntry(0); - private final Map capesCache = new HashMap<>(); - private final Map waitingCapes = new HashMap<>(); - private final Map evictedCapes = new HashMap<>(); - - private final NetHandlerPlayClient netHandler; - protected final TextureManager textureManager; - - private final EaglercraftUUID clientPlayerId; - private CapeCacheEntry clientPlayerCacheEntry; - - private long lastFlush = EagRuntime.steadyTimeMillis(); - private long lastFlushReq = EagRuntime.steadyTimeMillis(); - private long lastFlushEvict = EagRuntime.steadyTimeMillis(); - - private static int texId = 0; - public static boolean needReloadClientCape = false; - - public ServerCapeCache(NetHandlerPlayClient netHandler, TextureManager textureManager) { - this.netHandler = netHandler; - this.textureManager = textureManager; - this.clientPlayerId = EaglerProfile.getPlayerUUID(); - reloadClientPlayerCape(); - } - - public void reloadClientPlayerCape() { - needReloadClientCape = false; - this.clientPlayerCacheEntry = new CapeCacheEntry(EaglerProfile.getActiveCapeResourceLocation()); - } - - public CapeCacheEntry getClientPlayerCape() { - return clientPlayerCacheEntry; - } - - public CapeCacheEntry getCape(EaglercraftUUID player) { - if(player.equals(clientPlayerId)) { - return clientPlayerCacheEntry; - } - CapeCacheEntry etr = capesCache.get(player); - if(etr == null) { - if(!waitingCapes.containsKey(player) && !evictedCapes.containsKey(player)) { - waitingCapes.put(player, EagRuntime.steadyTimeMillis()); - netHandler.sendEaglerMessage(new CPacketGetOtherCapeEAG(player.msb, player.lsb)); - } - return defaultCacheEntry; - }else { - etr.lastCacheHit = EagRuntime.steadyTimeMillis(); - return etr; - } - } - - public void cacheCapePreset(EaglercraftUUID player, int presetId) { - if(waitingCapes.remove(player) != null) { - CapeCacheEntry etr = capesCache.remove(player); - if(etr != null) { - etr.free(); - } - capesCache.put(player, new CapeCacheEntry(presetId)); - }else { - logger.error("Unsolicited cape response recieved for \"{}\"! (preset {})", player, presetId); - } - } - - public void cacheCapeCustom(EaglercraftUUID player, byte[] pixels) { - if(waitingCapes.remove(player) != null) { - CapeCacheEntry etr = capesCache.remove(player); - if(etr != null) { - etr.free(); - } - byte[] pixels32x32 = new byte[4096]; - SkinConverter.convertCape23x17RGBto32x32RGBA(pixels, pixels32x32); - try { - etr = new CapeCacheEntry(new EaglerSkinTexture(pixels32x32, 32, 32), - new ResourceLocation("eagler:capes/multiplayer/tex_" + texId++)); - }catch(Throwable t) { - etr = new CapeCacheEntry(0); - logger.error("Could not process custom skin packet for \"{}\"!", player); - logger.error(t); - } - capesCache.put(player, etr); - }else { - logger.error("Unsolicited skin response recieved for \"{}\"!", player); - } - } - - public void flush() { - long millis = EagRuntime.steadyTimeMillis(); - if(millis - lastFlushReq > 5000l) { - lastFlushReq = millis; - if(!waitingCapes.isEmpty()) { - Iterator waitingItr = waitingCapes.values().iterator(); - while(waitingItr.hasNext()) { - if(millis - waitingItr.next().longValue() > 30000l) { - waitingItr.remove(); - } - } - } - } - if(millis - lastFlushEvict > 1000l) { - lastFlushEvict = millis; - if(!evictedCapes.isEmpty()) { - Iterator evictItr = evictedCapes.values().iterator(); - while(evictItr.hasNext()) { - if(millis - evictItr.next().longValue() > 3000l) { - evictItr.remove(); - } - } - } - } - if(millis - lastFlush > 60000l) { - lastFlush = millis; - if(!capesCache.isEmpty()) { - Iterator entryItr = capesCache.values().iterator(); - while(entryItr.hasNext()) { - CapeCacheEntry etr = entryItr.next(); - if(millis - etr.lastCacheHit > 900000l) { // 15 minutes - entryItr.remove(); - etr.free(); - } - } - } - } - if(needReloadClientCape) { - reloadClientPlayerCape(); - } - } - - public void destroy() { - Iterator entryItr = capesCache.values().iterator(); - while(entryItr.hasNext()) { - entryItr.next().free(); - } - capesCache.clear(); - waitingCapes.clear(); - evictedCapes.clear(); - } - - public void evictCape(EaglercraftUUID uuid) { - evictedCapes.put(uuid, Long.valueOf(EagRuntime.steadyTimeMillis())); - CapeCacheEntry etr = capesCache.remove(uuid); - if(etr != null) { - etr.free(); - } - } - - public void handleInvalidate(EaglercraftUUID uuid) { - CapeCacheEntry etr = capesCache.remove(uuid); - if(etr != null) { - etr.free(); - } - } - -} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerSkinCache.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerSkinCache.java deleted file mode 100755 index 3dc27358..00000000 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerSkinCache.java +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. - * - * 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. - * - */ - -package net.lax1dude.eaglercraft.v1_8.profile; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; -import net.lax1dude.eaglercraft.v1_8.mojang.authlib.TexturesProperty; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketGetOtherSkinEAG; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketGetSkinByURLEAG; -import net.minecraft.client.network.NetHandlerPlayClient; -import net.minecraft.client.renderer.texture.TextureManager; -import net.minecraft.util.ResourceLocation; - -public class ServerSkinCache { - - private static final Logger logger = LogManager.getLogger("ServerSkinCache"); - - public class SkinCacheEntry { - - protected final boolean isPresetSkin; - protected final int presetSkinId; - protected final CacheCustomSkin customSkin; - - protected long lastCacheHit = EagRuntime.steadyTimeMillis(); - - protected SkinCacheEntry(EaglerSkinTexture textureInstance, ResourceLocation resourceLocation, SkinModel model) { - this.isPresetSkin = false; - this.presetSkinId = -1; - this.customSkin = new CacheCustomSkin(textureInstance, resourceLocation, model); - ServerSkinCache.this.textureManager.loadTexture(resourceLocation, textureInstance); - } - - /** - * Use only for the constant for the client player - */ - protected SkinCacheEntry(ResourceLocation resourceLocation, SkinModel model) { - this.isPresetSkin = false; - this.presetSkinId = -1; - this.customSkin = new CacheCustomSkin(null, resourceLocation, model); - } - - protected SkinCacheEntry(int presetSkinId) { - this.isPresetSkin = true; - this.presetSkinId = presetSkinId; - this.customSkin = null; - } - - public ResourceLocation getResourceLocation() { - if(isPresetSkin) { - return DefaultSkins.getSkinFromId(presetSkinId).location; - }else { - if(customSkin != null) { - return customSkin.resourceLocation; - }else { - return DefaultSkins.DEFAULT_STEVE.location; - } - } - } - - public SkinModel getSkinModel() { - if(isPresetSkin) { - return DefaultSkins.getSkinFromId(presetSkinId).model; - }else { - if(customSkin != null) { - return customSkin.model; - }else { - return DefaultSkins.DEFAULT_STEVE.model; - } - } - } - - protected void free() { - if(!isPresetSkin) { - ServerSkinCache.this.textureManager.deleteTexture(customSkin.resourceLocation); - } - } - - } - - protected static class CacheCustomSkin { - - protected final EaglerSkinTexture textureInstance; - protected final ResourceLocation resourceLocation; - protected final SkinModel model; - - protected CacheCustomSkin(EaglerSkinTexture textureInstance, ResourceLocation resourceLocation, SkinModel model) { - this.textureInstance = textureInstance; - this.resourceLocation = resourceLocation; - this.model = model; - } - - } - - protected static class WaitingSkin { - - protected final long timeout; - protected final SkinModel model; - - protected WaitingSkin(long timeout, SkinModel model) { - this.timeout = timeout; - this.model = model; - } - - } - - private final SkinCacheEntry defaultCacheEntry = new SkinCacheEntry(0); - private final SkinCacheEntry defaultSlimCacheEntry = new SkinCacheEntry(1); - private final Map skinsCache = new HashMap<>(); - private final Map waitingSkins = new HashMap<>(); - private final Map evictedSkins = new HashMap<>(); - - private final NetHandlerPlayClient netHandler; - protected final TextureManager textureManager; - - private final EaglercraftUUID clientPlayerId; - private SkinCacheEntry clientPlayerCacheEntry; - - private long lastFlush = EagRuntime.steadyTimeMillis(); - private long lastFlushReq = EagRuntime.steadyTimeMillis(); - private long lastFlushEvict = EagRuntime.steadyTimeMillis(); - - private static int texId = 0; - public static boolean needReloadClientSkin = false; - - public ServerSkinCache(NetHandlerPlayClient netHandler, TextureManager textureManager) { - this.netHandler = netHandler; - this.textureManager = textureManager; - this.clientPlayerId = EaglerProfile.getPlayerUUID(); - reloadClientPlayerSkin(); - } - - public void reloadClientPlayerSkin() { - needReloadClientSkin = false; - this.clientPlayerCacheEntry = new SkinCacheEntry(EaglerProfile.getActiveSkinResourceLocation(), EaglerProfile.getActiveSkinModel()); - } - - public SkinCacheEntry getClientPlayerSkin() { - return clientPlayerCacheEntry; - } - - public SkinCacheEntry getSkin(GameProfile player) { - EaglercraftUUID uuid = player.getId(); - if(uuid != null && uuid.equals(clientPlayerId)) { - return clientPlayerCacheEntry; - } - TexturesProperty props = player.getTextures(); - if(props.eaglerPlayer || props.skin == null) { - if(uuid != null) { - return _getSkin(uuid); - }else { - if("slim".equalsIgnoreCase(props.model)) { - return defaultSlimCacheEntry; - }else { - return defaultCacheEntry; - } - } - }else { - return getSkin(props.skin, SkinModel.getModelFromId(props.model)); - } - } - - public SkinCacheEntry getSkin(EaglercraftUUID player) { - if(player.equals(clientPlayerId)) { - return clientPlayerCacheEntry; - } - return _getSkin(player); - } - - private SkinCacheEntry _getSkin(EaglercraftUUID player) { - SkinCacheEntry etr = skinsCache.get(player); - if(etr == null) { - if(!waitingSkins.containsKey(player) && !evictedSkins.containsKey(player)) { - waitingSkins.put(player, new WaitingSkin(EagRuntime.steadyTimeMillis(), null)); - netHandler.sendEaglerMessage(new CPacketGetOtherSkinEAG(player.msb, player.lsb)); - } - return defaultCacheEntry; - }else { - etr.lastCacheHit = EagRuntime.steadyTimeMillis(); - return etr; - } - } - - public SkinCacheEntry getSkin(String url, SkinModel skinModelResponse) { - if(url.length() > 0x7F00) { - return skinModelResponse == SkinModel.ALEX ? defaultSlimCacheEntry : defaultCacheEntry; - } - EaglercraftUUID generatedUUID = SkinPackets.createEaglerURLSkinUUID(url); - SkinCacheEntry etr = skinsCache.get(generatedUUID); - if(etr != null) { - etr.lastCacheHit = EagRuntime.steadyTimeMillis(); - return etr; - }else { - if(!waitingSkins.containsKey(generatedUUID) && !evictedSkins.containsKey(generatedUUID)) { - waitingSkins.put(generatedUUID, new WaitingSkin(EagRuntime.steadyTimeMillis(), skinModelResponse)); - netHandler.sendEaglerMessage(new CPacketGetSkinByURLEAG(generatedUUID.msb, generatedUUID.lsb, url)); - } - } - return skinModelResponse == SkinModel.ALEX ? defaultSlimCacheEntry : defaultCacheEntry; - } - - public void cacheSkinPreset(EaglercraftUUID player, int presetId) { - if(waitingSkins.remove(player) != null) { - SkinCacheEntry etr = skinsCache.remove(player); - if(etr != null) { - etr.free(); - } - skinsCache.put(player, new SkinCacheEntry(presetId)); - }else { - logger.error("Unsolicited skin response recieved for \"{}\"! (preset {})", player, presetId); - } - } - - public void cacheSkinCustom(EaglercraftUUID player, byte[] pixels, SkinModel model) { - WaitingSkin waitingSkin; - if((waitingSkin = waitingSkins.remove(player)) != null) { - SkinCacheEntry etr = skinsCache.remove(player); - if(etr != null) { - etr.free(); - } - if(waitingSkin.model != null) { - model = waitingSkin.model; - }else if(model == null) { - model = (player.hashCode() & 1) != 0 ? SkinModel.ALEX : SkinModel.STEVE; - } - try { - etr = new SkinCacheEntry(new EaglerSkinTexture(pixels, model.width, model.height), - new ResourceLocation("eagler:skins/multiplayer/tex_" + texId++), model); - }catch(Throwable t) { - etr = new SkinCacheEntry(0); - logger.error("Could not process custom skin packet for \"{}\"!", player); - logger.error(t); - } - skinsCache.put(player, etr); - }else { - logger.error("Unsolicited skin response recieved for \"{}\"! (custom {}x{})", player, model.width, model.height); - } - } - - public SkinModel getRequestedSkinType(EaglercraftUUID waiting) { - WaitingSkin waitingSkin; - if((waitingSkin = waitingSkins.get(waiting)) != null) { - return waitingSkin.model; - }else { - return null; - } - } - - public void flush() { - long millis = EagRuntime.steadyTimeMillis(); - if(millis - lastFlushReq > 5000l) { - lastFlushReq = millis; - if(!waitingSkins.isEmpty()) { - Iterator waitingItr = waitingSkins.values().iterator(); - while(waitingItr.hasNext()) { - if(millis - waitingItr.next().timeout > 20000l) { - waitingItr.remove(); - } - } - } - } - if(millis - lastFlushEvict > 1000l) { - lastFlushEvict = millis; - if(!evictedSkins.isEmpty()) { - Iterator evictItr = evictedSkins.values().iterator(); - while(evictItr.hasNext()) { - if(millis - evictItr.next().longValue() > 3000l) { - evictItr.remove(); - } - } - } - } - if(millis - lastFlush > 60000l) { - lastFlush = millis; - if(!skinsCache.isEmpty()) { - Iterator entryItr = skinsCache.values().iterator(); - while(entryItr.hasNext()) { - SkinCacheEntry etr = entryItr.next(); - if(millis - etr.lastCacheHit > 900000l) { // 15 minutes - entryItr.remove(); - etr.free(); - } - } - } - } - if(needReloadClientSkin) { - reloadClientPlayerSkin(); - } - } - - public void destroy() { - Iterator entryItr = skinsCache.values().iterator(); - while(entryItr.hasNext()) { - entryItr.next().free(); - } - skinsCache.clear(); - waitingSkins.clear(); - evictedSkins.clear(); - } - - public void evictSkin(EaglercraftUUID uuid) { - evictedSkins.put(uuid, Long.valueOf(EagRuntime.steadyTimeMillis())); - SkinCacheEntry etr = skinsCache.remove(uuid); - if(etr != null) { - etr.free(); - } - } - - public void handleInvalidate(EaglercraftUUID uuid) { - SkinCacheEntry etr = skinsCache.remove(uuid); - if(etr != null) { - etr.free(); - } - } - -} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinModel.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinModel.java index 25b3a0ec..70d3fc09 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinModel.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinModel.java @@ -72,6 +72,14 @@ public enum SkinModel { return STEVE; } } + + public static SkinModel getSanitizedModelFromId(int id) { + SkinModel ret = getModelFromId(id & 0x7F); + if((id & 0x80) != 0 && ret.sanitize) { + ret = STEVE; + } + return ret; + } static { SkinModel[] arr = values(); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/skin_cache/ForeignTextureEntry.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/skin_cache/ForeignTextureEntry.java new file mode 100755 index 00000000..8c156ed7 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/skin_cache/ForeignTextureEntry.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.skin_cache; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.profile.DefaultSkins; +import net.lax1dude.eaglercraft.v1_8.profile.EaglerSkinTexture; +import net.lax1dude.eaglercraft.v1_8.profile.SkinModel; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.util.ResourceLocation; + +class ForeignTextureEntry extends SkinData { + + protected final EaglercraftUUID uuid; + protected final String url; + + protected int state; + + protected long lastHit; + + protected ResourceLocation skinLocation; + protected EaglerSkinTexture skinTexture; + protected SkinModel skinModel; + + protected SkinData inverseModel; + + public ForeignTextureEntry(EaglercraftUUID uuid, String url, SkinModel skinModel) { + this.uuid = uuid; + this.url = url; + this.skinModel = skinModel; + } + + @Override + public ResourceLocation getLocation() { + return skinLocation; + } + + @Override + public SkinModel getModel() { + return skinModel; + } + + protected SkinData withModel(SkinModel model) { + if(model != null && skinModel != model) { + if(inverseModel == null) { + return inverseModel = new SkinData() { + @Override + public ResourceLocation getLocation() { + return skinLocation; + } + @Override + public SkinModel getModel() { + return model; + } + }; + } + return inverseModel; + }else { + return this; + } + } + + protected void handleSkinResultPreset(int skinID) { + DefaultSkins skin = DefaultSkins.getSkinFromId(skinID); + skinLocation = skin.location; + skinModel = skin.model; + state |= ServerTextureCacheOld.STATE_S_LOADED; + } + + protected void handleSkinResultCustomV4(byte[] customSkin, int modelID) { + handleSkinResultCustomV3(SkinPacketVersionCache.convertToV3Raw(customSkin), modelID); + } + + protected void handleSkinResultCustomV3(byte[] customSkin, int modelID) { + if(modelID != 0xFF) { + skinModel = SkinModel.getSanitizedModelFromId(modelID); + }else if(skinModel == null) { + skinModel = SkinModel.STEVE; + } + skinTexture = new EaglerSkinTexture(customSkin, skinModel.width, skinModel.height); + state |= ServerTextureCacheOld.STATE_S_COMPLETE; + } + + protected void loadSkin(TextureManager textureManager) { + skinLocation = new ResourceLocation("eagler:multiplayer/tex_" + ServerTextureCacheOld.texId++); + textureManager.loadTexture(skinLocation, skinTexture); + state |= ServerTextureCacheOld.STATE_S_LOADED; + } + + protected void release(TextureManager textureManager) { + if(skinTexture != null && (state & ServerTextureCacheOld.STATE_S_LOADED) != 0) { + textureManager.deleteTexture(skinLocation); + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/skin_cache/PlayerTextureEntry.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/skin_cache/PlayerTextureEntry.java new file mode 100755 index 00000000..677a73f7 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/skin_cache/PlayerTextureEntry.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.skin_cache; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.profile.DefaultCapes; +import net.lax1dude.eaglercraft.v1_8.profile.DefaultSkins; +import net.lax1dude.eaglercraft.v1_8.profile.EaglerSkinTexture; +import net.lax1dude.eaglercraft.v1_8.profile.SkinConverter; +import net.lax1dude.eaglercraft.v1_8.profile.SkinModel; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.util.ResourceLocation; + +class PlayerTextureEntry extends SkinData { + + protected final EaglercraftUUID uuid; + + protected int state; + + protected long lastHit; + + protected ResourceLocation skinLocation; + protected EaglerSkinTexture skinTexture; + protected SkinModel skinModel; + + protected ResourceLocation capeLocation; + protected EaglerSkinTexture capeTexture; + + public PlayerTextureEntry(EaglercraftUUID uuid) { + this.uuid = uuid; + } + + @Override + public ResourceLocation getLocation() { + return skinLocation; + } + + @Override + public SkinModel getModel() { + return skinModel; + } + + protected void handleSkinResultPreset(int skinID) { + DefaultSkins skin = DefaultSkins.getSkinFromId(skinID); + skinLocation = skin.location; + skinModel = skin.model; + state |= ServerTextureCache.STATE_S_LOADED; + } + + protected void handleSkinResultCustomV4(byte[] customSkin, int modelID) { + handleSkinResultCustomV3(SkinPacketVersionCache.convertToV3Raw(customSkin), modelID); + } + + protected void handleSkinResultCustomV3(byte[] customSkin, int modelID) { + skinModel = SkinModel.getSanitizedModelFromId(modelID); + skinTexture = new EaglerSkinTexture(customSkin, skinModel.width, skinModel.height); + state |= ServerTextureCache.STATE_S_COMPLETE; + } + + protected void loadSkin(TextureManager textureManager) { + skinLocation = new ResourceLocation("eagler:multiplayer/tex_" + ServerTextureCache.texId++); + textureManager.loadTexture(skinLocation, skinTexture); + state |= ServerTextureCache.STATE_S_LOADED; + } + + protected void handleCapeResultPreset(int presetCape) { + DefaultCapes cape = DefaultCapes.getCapeFromId(presetCape); + capeLocation = cape.location; + state |= ServerTextureCache.STATE_C_LOADED; + } + + protected void handleCapeResultCustom(byte[] customCape) { + byte[] pixels32x32 = new byte[4096]; + SkinConverter.convertCape23x17RGBto32x32RGBA(customCape, pixels32x32); + capeTexture = new EaglerSkinTexture(pixels32x32, 32, 32); + state |= ServerTextureCache.STATE_C_COMPLETE; + } + + protected void loadCape(TextureManager textureManager) { + capeLocation = new ResourceLocation("eagler:multiplayer/tex_" + ServerTextureCache.texId++); + textureManager.loadTexture(capeLocation, capeTexture); + state |= ServerTextureCache.STATE_C_LOADED; + } + + protected void release(TextureManager textureManager) { + if(skinTexture != null && (state & ServerTextureCache.STATE_S_LOADED) != 0) { + textureManager.deleteTexture(skinLocation); + } + if(capeTexture != null && (state & ServerTextureCache.STATE_C_LOADED) != 0) { + textureManager.deleteTexture(capeLocation); + } + } + + protected void drop(TextureManager textureManager, boolean skin, boolean cape) { + if(skin) { + if(skinTexture != null && (state & ServerTextureCache.STATE_S_LOADED) != 0) { + textureManager.deleteTexture(skinLocation); + } + skinTexture = null; + skinLocation = null; + state &= ~(ServerTextureCache.STATE_S_PENDING | ServerTextureCache.STATE_S_LOADED + | ServerTextureCache.STATE_S_COMPLETE); + } + if(cape) { + if(capeTexture != null && (state & ServerTextureCache.STATE_C_LOADED) != 0) { + textureManager.deleteTexture(capeLocation); + } + capeTexture = null; + capeLocation = null; + state &= ~(ServerTextureCache.STATE_C_PENDING | ServerTextureCache.STATE_C_LOADED + | ServerTextureCache.STATE_C_COMPLETE); + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/skin_cache/ServerTextureCache.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/skin_cache/ServerTextureCache.java new file mode 100755 index 00000000..6f9afb84 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/skin_cache/ServerTextureCache.java @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.skin_cache; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import com.carrotsearch.hppc.ObjectLongHashMap; +import com.carrotsearch.hppc.ObjectLongMap; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; +import net.lax1dude.eaglercraft.v1_8.mojang.authlib.TexturesProperty; +import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; +import net.lax1dude.eaglercraft.v1_8.profile.SkinModel; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.client.StateFlags; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*; +import net.minecraft.client.network.NetHandlerPlayClient; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.util.ResourceLocation; + +public abstract class ServerTextureCache { + + static final int STATE_S_PENDING = 1; + + static final int STATE_C_PENDING = 2; + + static final int STATE_SC_PENDING = STATE_S_PENDING | STATE_C_PENDING; + + static final int STATE_S_COMPLETE = 4; + + static final int STATE_C_COMPLETE = 8; + + static final int STATE_S_LOADED = 16; + + static final int STATE_C_LOADED = 32; + + static int texId = 0; + + protected final int protocolVers; + protected final NetHandlerPlayClient netHandler; + protected final TextureManager textureManager; + protected final EaglercraftUUID self; + + private EaglercraftUUID mruPlayerKey = null; + private PlayerTextureEntry mruPlayerTexture = null; + private final Map playerTextures = new HashMap<>(256); + + private EaglercraftUUID mruForeignKey = null; + private ForeignTextureEntry mruForeignTexture = null; + private final Map foreignTextures = new HashMap<>(256); + + private final Set pendingPlayerToLookup = new HashSet<>(32); + private final Set pendingForeignToLookup = new HashSet<>(32); + private final ObjectLongMap evictedPlayers = new ObjectLongHashMap<>(32); + + private int nextFlush = 200; + private int nextEvictFlush = 20; + private int flush = 0; + + public static ServerTextureCache create(NetHandlerPlayClient netHandler, TextureManager textureManager) { + if (netHandler.getEaglerMessageProtocol().ver >= 5) { + return new ServerTextureCacheV5(netHandler, textureManager); + } else { + return new ServerTextureCacheOld(netHandler, textureManager); + } + } + + public ServerTextureCache(NetHandlerPlayClient netHandler, TextureManager textureManager) { + this.protocolVers = netHandler.getEaglerMessageProtocol().ver; + this.netHandler = netHandler; + this.textureManager = textureManager; + this.self = netHandler.getGameProfile().getId(); + } + + private PlayerTextureEntry loadPlayer(EaglercraftUUID uuid) { + if (mruPlayerKey != null && uuid.equals(mruPlayerKey)) { + return mruPlayerTexture; + } + PlayerTextureEntry ret = playerTextures.get(uuid); + if (ret == null) { + playerTextures.put(uuid, ret = new PlayerTextureEntry(uuid)); + } + ret.lastHit = EagRuntime.steadyTimeMillis(); + mruPlayerKey = uuid; + mruPlayerTexture = ret; + return ret; + } + + private ForeignTextureEntry loadForeign(EaglercraftUUID uuid, String url, SkinModel model) { + if (mruForeignKey != null && uuid.equals(mruForeignKey)) { + return mruForeignTexture; + } + ForeignTextureEntry ret = foreignTextures.get(uuid); + if (ret == null) { + foreignTextures.put(uuid, ret = new ForeignTextureEntry(uuid, url, model)); + } + ret.lastHit = EagRuntime.steadyTimeMillis(); + mruForeignKey = uuid; + mruForeignTexture = ret; + return ret; + } + + public SkinData getPlayerSkin(GameProfile profile) { + if (self.equals(profile.getId())) { + return SkinData.defaultSkinSelf; + } + TexturesProperty prop = profile.getTextures(); + if (prop.eaglerPlayer != (byte) 0) { + return getPlayerSkinImpl(profile, prop); + } else { + SkinData ret = getForeignSkinImpl(profile, prop); + if (ret != null) { + return ret; + } else if (StateFlags.eaglerPlayerFlag) { + return SkinData.getDefaultSkin(profile.getId()); + } else { + return getPlayerSkinImpl(profile, prop); + } + } + } + + private SkinData getPlayerSkinImpl(GameProfile profile, TexturesProperty prop) { + EaglercraftUUID uuid = profile.getId(); + if (checkEvicted(uuid)) { + return SkinData.getDefaultSkin(uuid); + } + PlayerTextureEntry etr = loadPlayer(uuid); + int state = etr.state; + if ((state & STATE_S_LOADED) != 0) { + return etr; + } else if ((state & STATE_S_COMPLETE) != 0) { + etr.loadSkin(textureManager); + return etr; + } else { + if ((state & STATE_S_PENDING) == 0) { + etr.state = (state | STATE_S_PENDING); + pendingPlayerToLookup.add(etr); + } + return SkinData.getDefaultSkin(profile.getId()); + } + } + + private SkinData getForeignSkinImpl(GameProfile profile, TexturesProperty prop) { + if(StateFlags.disableSkinURLLookup) { + return null; + } + String url = prop.skin; + if (url != null) { + ForeignTextureEntry etr = loadForeign(prop.loadSkinTextureUUID(), url, prop.model); + int state = etr.state; + if ((state & STATE_S_LOADED) != 0) { + return etr.withModel(prop.model); + } else if ((state & STATE_S_COMPLETE) != 0) { + etr.loadSkin(textureManager); + return etr.withModel(prop.model); + } else { + if ((state & STATE_S_PENDING) == 0) { + etr.state = (state | STATE_S_PENDING); + pendingForeignToLookup.add(etr); + } + return SkinData.getDefaultSkin(prop.model); + } + } + return null; + } + + public ResourceLocation getPlayerCape(GameProfile profile) { + EaglercraftUUID uuid = profile.getId(); + if (uuid.equals(self)) { + return EaglerProfile.getActiveCapeResourceLocation(); + } + if (!StateFlags.eaglerPlayerFlag || profile.getTextures().eaglerPlayer != (byte) 0) { + if (checkEvicted(uuid)) { + return null; + } + PlayerTextureEntry etr = loadPlayer(uuid); + int state = etr.state; + if ((state & STATE_C_LOADED) != 0) { + return etr.capeLocation; + } else if ((state & STATE_C_COMPLETE) != 0) { + etr.loadCape(textureManager); + return etr.capeLocation; + } else { + if ((state & STATE_C_PENDING) == 0) { + etr.state = (state | STATE_C_PENDING); + pendingPlayerToLookup.add(etr); + } + return null; + } + } + // Loading capes by URL is not currently supported + // (Should only affect NPCs on a working setup) + return null; + } + + public void dropPlayer(EaglercraftUUID playerUUID, boolean skin, boolean cape) { + if (skin || cape) { + PlayerTextureEntry etr = playerTextures.get(playerUUID); + if (etr != null) { + etr.drop(textureManager, skin, cape); + _dropPlayer(playerUUID, skin, cape); + } + } + } + + protected abstract void _dropPlayer(EaglercraftUUID playerUUID, boolean skin, boolean cape); + + public void evictPlayer(EaglercraftUUID playerUUID) { + PlayerTextureEntry etr = playerTextures.remove(playerUUID); + if (etr != null) { + etr.release(textureManager); + } + if (mruPlayerKey != null && playerUUID.equals(mruPlayerKey)) { + mruPlayerKey = null; + mruPlayerTexture = null; + } + evictedPlayers.put(playerUUID, EagRuntime.steadyTimeMillis() + 2000l); + } + + private boolean checkEvicted(EaglercraftUUID playerUUID) { + if (!evictedPlayers.isEmpty()) { + long l = evictedPlayers.getOrDefault(playerUUID, -1l); + return l != -1l && EagRuntime.steadyTimeMillis() < l; + } + return false; + } + + public void runTick() { + if (!pendingPlayerToLookup.isEmpty()) { + for (PlayerTextureEntry etr : pendingPlayerToLookup) { + int state = etr.state; + if (protocolVers >= 5 && (state & STATE_SC_PENDING) == STATE_SC_PENDING) { + sendTexturesRequest(etr); + } else { + if ((state & STATE_S_PENDING) != 0) { + sendSkinRequest(etr); + } + if ((state & STATE_C_PENDING) != 0) { + sendCapeRequest(etr); + } + } + } + pendingPlayerToLookup.clear(); + } + if (!pendingForeignToLookup.isEmpty()) { + for (ForeignTextureEntry etr : pendingForeignToLookup) { + if ((etr.state & STATE_S_PENDING) != 0) { + sendSkinRequest(etr); + } + } + pendingForeignToLookup.clear(); + } + if (--nextEvictFlush <= 0) { + nextEvictFlush = 20; + long now = EagRuntime.steadyTimeMillis(); + evictedPlayers.removeAll((obj, millis) -> { + return now > millis; + }); + } + if (--nextFlush <= 0) { + nextFlush = 200; + long now = EagRuntime.steadyTimeMillis(); + lookupFlush(now); + if ((++flush & 3) == 0) { + if (!playerTextures.isEmpty()) { + Iterator itr1 = playerTextures.values().iterator(); + while (itr1.hasNext()) { + PlayerTextureEntry etr = itr1.next(); + if (now - etr.lastHit > (900l * 1000l)) { + itr1.remove(); + etr.release(textureManager); + } + } + mruPlayerKey = null; + mruPlayerTexture = null; + } + if (!foreignTextures.isEmpty()) { + Iterator itr2 = foreignTextures.values().iterator(); + while (itr2.hasNext()) { + ForeignTextureEntry etr = itr2.next(); + if (now - etr.lastHit > (900l * 1000l)) { + itr2.remove(); + etr.release(textureManager); + } + } + mruForeignKey = null; + mruForeignTexture = null; + } + } + } + } + + public void destroy() { + if (!playerTextures.isEmpty()) { + for (PlayerTextureEntry etr : playerTextures.values()) { + etr.release(textureManager); + } + playerTextures.clear(); + mruPlayerKey = null; + mruPlayerTexture = null; + } + if (!foreignTextures.isEmpty()) { + for (ForeignTextureEntry etr : foreignTextures.values()) { + etr.release(textureManager); + } + foreignTextures.clear(); + mruForeignKey = null; + mruForeignTexture = null; + } + pendingPlayerToLookup.clear(); + pendingForeignToLookup.clear(); + evictedPlayers.clear(); + } + + protected abstract void lookupFlush(long now); + + protected abstract void sendTexturesRequest(PlayerTextureEntry etr); + + protected abstract void sendSkinRequest(PlayerTextureEntry etr); + + protected abstract void sendSkinRequest(ForeignTextureEntry etr); + + protected abstract void sendCapeRequest(PlayerTextureEntry etr); + + public abstract void handlePacket(SPacketOtherSkinPresetEAG packet); + + public abstract void handlePacket(SPacketOtherSkinPresetV5EAG packet); + + public abstract void handlePacket(SPacketOtherSkinCustomV3EAG packet); + + public abstract void handlePacket(SPacketOtherSkinCustomV4EAG packet); + + public abstract void handlePacket(SPacketOtherSkinCustomV5EAG packet); + + public abstract void handlePacket(SPacketOtherCapePresetEAG packet); + + public abstract void handlePacket(SPacketOtherCapePresetV5EAG packet); + + public abstract void handlePacket(SPacketOtherCapeCustomEAG packet); + + public abstract void handlePacket(SPacketOtherCapeCustomV5EAG packet); + + public abstract void handlePacket(SPacketOtherTexturesV5EAG packet); + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/skin_cache/ServerTextureCacheOld.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/skin_cache/ServerTextureCacheOld.java new file mode 100755 index 00000000..251bc0ad --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/skin_cache/ServerTextureCacheOld.java @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.skin_cache; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.*; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*; +import net.minecraft.client.network.NetHandlerPlayClient; +import net.minecraft.client.renderer.texture.TextureManager; + +public class ServerTextureCacheOld extends ServerTextureCache { + + private static abstract class PendingLookup { + + protected final long expiresAt = EagRuntime.steadyTimeMillis() + (30l * 1000l); + + protected abstract void handleResult(GameMessagePacket packet); + + protected abstract void handleTimeout(); + + } + + private final Map pendingSkinLookup = new HashMap<>(64); + private final Map pendingCapeLookup = new HashMap<>(64); + + public ServerTextureCacheOld(NetHandlerPlayClient netHandler, TextureManager textureManager) { + super(netHandler, textureManager); + if(protocolVers > 4) { + throw new IllegalStateException(); + } + } + + @Override + protected void _dropPlayer(EaglercraftUUID playerUUID, boolean skin, boolean cape) { + if(skin) { + pendingSkinLookup.remove(playerUUID); + } + if(cape) { + pendingCapeLookup.remove(playerUUID); + } + } + + @Override + protected void sendSkinRequest(PlayerTextureEntry etr) { + pendingSkinLookup.put(etr.uuid, new PendingLookup() { + @Override + protected void handleResult(GameMessagePacket packet) { + etr.state &= ~STATE_S_PENDING; + if(packet instanceof SPacketOtherSkinPresetEAG) { + SPacketOtherSkinPresetEAG pkt = (SPacketOtherSkinPresetEAG) packet; + etr.handleSkinResultPreset(pkt.presetSkin); + }else if(packet instanceof SPacketOtherSkinCustomV4EAG) { + SPacketOtherSkinCustomV4EAG pkt = (SPacketOtherSkinCustomV4EAG) packet; + etr.handleSkinResultCustomV4(pkt.customSkin, pkt.modelID); + }else if(packet instanceof SPacketOtherSkinCustomV3EAG) { + SPacketOtherSkinCustomV3EAG pkt = (SPacketOtherSkinCustomV3EAG) packet; + etr.handleSkinResultCustomV3(pkt.customSkin, pkt.modelID); + }else { + throw new IllegalStateException(); + } + } + @Override + protected void handleTimeout() { + etr.state &= ~STATE_S_PENDING; + } + }); + netHandler.sendEaglerMessage(new CPacketGetOtherSkinEAG(etr.uuid.getMostSignificantBits(), + etr.uuid.getLeastSignificantBits())); + } + + @Override + protected void sendCapeRequest(PlayerTextureEntry etr) { + pendingCapeLookup.put(etr.uuid, new PendingLookup() { + @Override + protected void handleResult(GameMessagePacket packet) { + etr.state &= ~STATE_C_PENDING; + if(packet instanceof SPacketOtherCapePresetEAG) { + SPacketOtherCapePresetEAG pkt = (SPacketOtherCapePresetEAG) packet; + etr.handleCapeResultPreset(pkt.presetCape); + }else if(packet instanceof SPacketOtherCapeCustomEAG) { + SPacketOtherCapeCustomEAG pkt = (SPacketOtherCapeCustomEAG) packet; + etr.handleCapeResultCustom(pkt.customCape); + }else { + throw new IllegalStateException(); + } + } + @Override + protected void handleTimeout() { + etr.state &= ~STATE_C_PENDING; + } + }); + netHandler.sendEaglerMessage(new CPacketGetOtherCapeEAG(etr.uuid.getMostSignificantBits(), + etr.uuid.getLeastSignificantBits())); + } + + @Override + protected void sendSkinRequest(ForeignTextureEntry etr) { + pendingSkinLookup.put(etr.uuid, new PendingLookup() { + @Override + protected void handleResult(GameMessagePacket packet) { + etr.state &= ~STATE_S_PENDING; + if(packet instanceof SPacketOtherSkinPresetEAG) { + SPacketOtherSkinPresetEAG pkt = (SPacketOtherSkinPresetEAG) packet; + etr.handleSkinResultPreset(pkt.presetSkin); + }else if(packet instanceof SPacketOtherSkinCustomV4EAG) { + SPacketOtherSkinCustomV4EAG pkt = (SPacketOtherSkinCustomV4EAG) packet; + etr.handleSkinResultCustomV4(pkt.customSkin, pkt.modelID); + }else if(packet instanceof SPacketOtherSkinCustomV3EAG) { + SPacketOtherSkinCustomV3EAG pkt = (SPacketOtherSkinCustomV3EAG) packet; + etr.handleSkinResultCustomV3(pkt.customSkin, pkt.modelID); + }else { + throw new IllegalStateException(); + } + } + @Override + protected void handleTimeout() { + etr.state &= ~STATE_S_PENDING; + } + }); + netHandler.sendEaglerMessage(new CPacketGetSkinByURLEAG(etr.uuid.getMostSignificantBits(), + etr.uuid.getLeastSignificantBits(), etr.url)); + } + + @Override + protected void sendTexturesRequest(PlayerTextureEntry etr) { + throw new IllegalStateException(); + } + + @Override + protected void lookupFlush(long now) { + lookupFlush(pendingSkinLookup, now); + lookupFlush(pendingCapeLookup, now); + } + + private void lookupFlush(Map pending, long now) { + if(!pending.isEmpty()) { + Iterator itr1 = pending.values().iterator(); + while(itr1.hasNext()) { + PendingLookup etr = itr1.next(); + if(now > etr.expiresAt) { + itr1.remove(); + etr.handleTimeout(); + } + } + } + } + + @Override + public void handlePacket(SPacketOtherSkinPresetEAG packet) { + PendingLookup lookup = pendingSkinLookup.remove(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast)); + if(lookup != null) { + lookup.handleResult(packet); + } + } + + @Override + public void handlePacket(SPacketOtherSkinPresetV5EAG packet) { + throw new IllegalStateException(); + } + + @Override + public void handlePacket(SPacketOtherSkinCustomV3EAG packet) { + PendingLookup lookup = pendingSkinLookup.remove(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast)); + if(lookup != null) { + lookup.handleResult(packet); + } + } + + @Override + public void handlePacket(SPacketOtherSkinCustomV4EAG packet) { + PendingLookup lookup = pendingSkinLookup.remove(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast)); + if(lookup != null) { + lookup.handleResult(packet); + } + } + + @Override + public void handlePacket(SPacketOtherSkinCustomV5EAG packet) { + throw new IllegalStateException(); + } + + @Override + public void handlePacket(SPacketOtherCapePresetEAG packet) { + PendingLookup lookup = pendingCapeLookup.remove(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast)); + if(lookup != null) { + lookup.handleResult(packet); + } + } + + @Override + public void handlePacket(SPacketOtherCapePresetV5EAG packet) { + throw new IllegalStateException(); + } + + @Override + public void handlePacket(SPacketOtherCapeCustomEAG packet) { + PendingLookup lookup = pendingCapeLookup.remove(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast)); + if(lookup != null) { + lookup.handleResult(packet); + } + } + + @Override + public void handlePacket(SPacketOtherCapeCustomV5EAG packet) { + throw new IllegalStateException(); + } + + @Override + public void handlePacket(SPacketOtherTexturesV5EAG packet) { + throw new IllegalStateException(); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/skin_cache/ServerTextureCacheV5.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/skin_cache/ServerTextureCacheV5.java new file mode 100755 index 00000000..9d794066 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/skin_cache/ServerTextureCacheV5.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.skin_cache; + +import com.carrotsearch.hppc.IntObjectHashMap; +import com.carrotsearch.hppc.IntObjectMap; +import com.carrotsearch.hppc.predicates.IntObjectPredicate; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.*; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*; +import net.minecraft.client.network.NetHandlerPlayClient; +import net.minecraft.client.renderer.texture.TextureManager; + +public class ServerTextureCacheV5 extends ServerTextureCache { + + private static abstract class PendingLookup { + + protected final EaglercraftUUID uuid; + protected final long expiresAt = EagRuntime.steadyTimeMillis() + (30l * 1000l); + + protected PendingLookup(EaglercraftUUID uuid) { + this.uuid = uuid; + } + + protected abstract void handleResult(GameMessagePacket packet); + + protected abstract void handleTimeout(); + + } + + private final IntObjectMap pendingSkinLookup = new IntObjectHashMap<>(64); + private final IntObjectMap pendingCapeLookup = new IntObjectHashMap<>(64); + private final IntObjectMap pendingTextureLookup = new IntObjectHashMap<>(64); + + private int lookupIdA = 0; + private int lookupIdB = 0; + private int lookupIdC = 0; + + public ServerTextureCacheV5(NetHandlerPlayClient netHandler, TextureManager textureManager) { + super(netHandler, textureManager); + } + + private int nextLookupIdA() { + return lookupIdA = (lookupIdA + 1) & 0x3FFF; + } + + private int nextLookupIdB() { + return lookupIdB = (lookupIdB + 1) & 0x3FFF; + } + + private int nextLookupIdC() { + return lookupIdC = (lookupIdC + 1) & 0x3FFF; + } + + @Override + protected void sendSkinRequest(PlayerTextureEntry etr) { + int lookupId = nextLookupIdA(); + pendingSkinLookup.put(lookupId, new PendingLookup(etr.uuid) { + @Override + protected void handleResult(GameMessagePacket packet) { + etr.state &= ~STATE_S_PENDING; + if(packet instanceof SPacketOtherSkinPresetV5EAG) { + SPacketOtherSkinPresetV5EAG pkt = (SPacketOtherSkinPresetV5EAG) packet; + etr.handleSkinResultPreset(pkt.presetSkin); + }else if(packet instanceof SPacketOtherSkinCustomV5EAG) { + SPacketOtherSkinCustomV5EAG pkt = (SPacketOtherSkinCustomV5EAG) packet; + etr.handleSkinResultCustomV4(pkt.customSkin, pkt.modelID); + }else { + throw new IllegalStateException(); + } + } + @Override + protected void handleTimeout() { + etr.state &= ~STATE_S_PENDING; + } + }); + netHandler.sendEaglerMessage(new CPacketGetOtherSkinV5EAG(lookupId, etr.uuid.getMostSignificantBits(), + etr.uuid.getLeastSignificantBits())); + } + + @Override + protected void sendCapeRequest(PlayerTextureEntry etr) { + int lookupId = nextLookupIdB(); + pendingCapeLookup.put(lookupId, new PendingLookup(etr.uuid) { + @Override + protected void handleResult(GameMessagePacket packet) { + etr.state &= ~STATE_C_PENDING; + if(packet instanceof SPacketOtherCapePresetV5EAG) { + SPacketOtherCapePresetV5EAG pkt = (SPacketOtherCapePresetV5EAG) packet; + etr.handleCapeResultPreset(pkt.presetCape); + }else if(packet instanceof SPacketOtherCapeCustomV5EAG) { + SPacketOtherCapeCustomV5EAG pkt = (SPacketOtherCapeCustomV5EAG) packet; + etr.handleCapeResultCustom(pkt.customCape); + }else { + throw new IllegalStateException(); + } + } + @Override + protected void handleTimeout() { + etr.state &= ~STATE_C_PENDING; + } + }); + netHandler.sendEaglerMessage(new CPacketGetOtherCapeV5EAG(lookupId, etr.uuid.getMostSignificantBits(), + etr.uuid.getLeastSignificantBits())); + } + + @Override + protected void sendSkinRequest(ForeignTextureEntry etr) { + int lookupId = nextLookupIdA(); + pendingSkinLookup.put(lookupId, new PendingLookup(null) { + @Override + protected void handleResult(GameMessagePacket packet) { + etr.state &= ~STATE_S_PENDING; + if(packet instanceof SPacketOtherSkinPresetV5EAG) { + SPacketOtherSkinPresetV5EAG pkt = (SPacketOtherSkinPresetV5EAG) packet; + etr.handleSkinResultPreset(pkt.presetSkin); + }else if(packet instanceof SPacketOtherSkinCustomV5EAG) { + SPacketOtherSkinCustomV5EAG pkt = (SPacketOtherSkinCustomV5EAG) packet; + etr.handleSkinResultCustomV4(pkt.customSkin, pkt.modelID); + }else { + throw new IllegalStateException(); + } + } + @Override + protected void handleTimeout() { + etr.state &= ~STATE_S_PENDING; + } + }); + netHandler.sendEaglerMessage(new CPacketGetSkinByURLV5EAG(lookupId, etr.url)); + } + + @Override + protected void sendTexturesRequest(PlayerTextureEntry etr) { + int lookupId = nextLookupIdC(); + pendingTextureLookup.put(lookupId, new PendingLookup(etr.uuid) { + @Override + protected void handleResult(GameMessagePacket packet) { + etr.state &= ~STATE_SC_PENDING; + if(packet instanceof SPacketOtherTexturesV5EAG) { + SPacketOtherTexturesV5EAG pkt = (SPacketOtherTexturesV5EAG) packet; + if(pkt.skinID >= 0) { + etr.handleSkinResultPreset(pkt.skinID); + }else { + etr.handleSkinResultCustomV4(pkt.customSkin, -pkt.skinID - 1); + } + if(pkt.capeID >= 0) { + etr.handleCapeResultPreset(pkt.capeID); + }else { + etr.handleCapeResultCustom(pkt.customCape); + } + }else { + throw new IllegalStateException(); + } + } + @Override + protected void handleTimeout() { + etr.state &= ~STATE_SC_PENDING; + } + }); + netHandler.sendEaglerMessage(new CPacketGetOtherTexturesV5EAG(lookupId, etr.uuid.getMostSignificantBits(), + etr.uuid.getLeastSignificantBits())); + } + + @Override + protected void _dropPlayer(EaglercraftUUID playerUUID, boolean skin, boolean cape) { + IntObjectPredicate pred = (i, o) -> { + return o.uuid != null && playerUUID.equals(o.uuid); + }; + if(skin) { + pendingSkinLookup.removeAll(pred); + } + if(cape) { + pendingCapeLookup.removeAll(pred); + } + pendingTextureLookup.removeAll(pred); + } + + @Override + protected void lookupFlush(long now) { + IntObjectPredicate pred = (i, o) -> { + return now > o.expiresAt; + }; + pendingSkinLookup.removeAll(pred); + pendingCapeLookup.removeAll(pred); + pendingTextureLookup.removeAll(pred); + } + + @Override + public void handlePacket(SPacketOtherSkinPresetEAG packet) { + throw new IllegalStateException(); + } + + @Override + public void handlePacket(SPacketOtherSkinPresetV5EAG packet) { + PendingLookup lookup = pendingSkinLookup.remove(packet.requestId); + if(lookup != null) { + lookup.handleResult(packet); + } + } + + @Override + public void handlePacket(SPacketOtherSkinCustomV3EAG packet) { + throw new IllegalStateException(); + } + + @Override + public void handlePacket(SPacketOtherSkinCustomV4EAG packet) { + throw new IllegalStateException(); + } + + @Override + public void handlePacket(SPacketOtherSkinCustomV5EAG packet) { + PendingLookup lookup = pendingSkinLookup.remove(packet.requestId); + if(lookup != null) { + lookup.handleResult(packet); + } + } + + @Override + public void handlePacket(SPacketOtherCapePresetEAG packet) { + throw new IllegalStateException(); + } + + @Override + public void handlePacket(SPacketOtherCapePresetV5EAG packet) { + PendingLookup lookup = pendingCapeLookup.remove(packet.requestId); + if(lookup != null) { + lookup.handleResult(packet); + } + } + + @Override + public void handlePacket(SPacketOtherCapeCustomEAG packet) { + throw new IllegalStateException(); + } + + @Override + public void handlePacket(SPacketOtherCapeCustomV5EAG packet) { + PendingLookup lookup = pendingCapeLookup.remove(packet.requestId); + if(lookup != null) { + lookup.handleResult(packet); + } + } + + @Override + public void handlePacket(SPacketOtherTexturesV5EAG packet) { + PendingLookup lookup = pendingTextureLookup.remove(packet.requestId); + if(lookup != null) { + lookup.handleResult(packet); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/skin_cache/SkinData.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/skin_cache/SkinData.java new file mode 100755 index 00000000..6997b62a --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/skin_cache/SkinData.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.skin_cache; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.profile.DefaultSkins; +import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; +import net.lax1dude.eaglercraft.v1_8.profile.SkinModel; +import net.minecraft.util.ResourceLocation; + +public abstract class SkinData { + + public abstract ResourceLocation getLocation(); + + public abstract SkinModel getModel(); + + static SkinData getDefaultSkin(EaglercraftUUID uuid) { + return (uuid != null && (uuid.hashCode() & 1) != 0) ? defaultSkinDataAlex : defaultSkinDataSteve; + } + + static SkinData getDefaultSkin(SkinModel model) { + return (model == SkinModel.ALEX) ? defaultSkinDataAlex : defaultSkinDataSteve; + } + + static final SkinData defaultSkinDataSteve = new SkinData() { + @Override + public ResourceLocation getLocation() { + return DefaultSkins.DEFAULT_STEVE.location; + } + @Override + public SkinModel getModel() { + return SkinModel.STEVE; + } + }; + + static final SkinData defaultSkinDataAlex = new SkinData() { + @Override + public ResourceLocation getLocation() { + return DefaultSkins.DEFAULT_ALEX.location; + } + @Override + public SkinModel getModel() { + return SkinModel.ALEX; + } + }; + + static final SkinData defaultSkinSelf = new SkinData() { + @Override + public ResourceLocation getLocation() { + return EaglerProfile.getActiveSkinResourceLocation(); + } + @Override + public SkinModel getModel() { + return EaglerProfile.getActiveSkinModel(); + } + }; + +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/ConnectionHandshake.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/ConnectionHandshake.java deleted file mode 100755 index 7a4e77cb..00000000 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/ConnectionHandshake.java +++ /dev/null @@ -1,507 +0,0 @@ -/* - * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. - * - * 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. - * - */ - -package net.lax1dude.eaglercraft.v1_8.socket; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import net.lax1dude.eaglercraft.v1_8.ArrayUtils; -import net.lax1dude.eaglercraft.v1_8.ClientUUIDLoadingCache; -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.EagUtils; -import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; -import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream; -import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; -import net.lax1dude.eaglercraft.v1_8.PauseMenuCustomizeState; -import net.lax1dude.eaglercraft.v1_8.crypto.SHA256Digest; -import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient; -import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; -import net.lax1dude.eaglercraft.v1_8.profile.GuiAuthenticationScreen; -import net.lax1dude.eaglercraft.v1_8.update.UpdateService; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiDisconnected; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.multiplayer.GuiConnecting; -import net.minecraft.util.ChatComponentText; -import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.IChatComponent; - -public class ConnectionHandshake { - - private static final long baseTimeout = 10000l; - - private static final int protocolV2 = 2; - private static final int protocolV3 = 3; - private static final int protocolV4 = 4; - - private static final Logger logger = LogManager.getLogger(); - - public static String pluginVersion = null; - public static String pluginBrand = null; - public static int protocolVersion = -1; - - public static byte[] getSPHandshakeProtocolData() { - try { - EaglerOutputStream bao = new EaglerOutputStream(); - DataOutputStream d = new DataOutputStream(bao); - d.writeShort(3); // supported eagler protocols count - d.writeShort(protocolV2); // client supports v2 - d.writeShort(protocolV3); // client supports v3 - d.writeShort(protocolV4); // client supports v4 - return bao.toByteArray(); - }catch(IOException ex) { - throw new RuntimeException(ex); - } - } - - public static boolean attemptHandshake(Minecraft mc, IWebSocketClient client, GuiConnecting connecting, - GuiScreen ret, String password, boolean allowPlaintext, boolean enableCookies, byte[] cookieData) { - try { - EaglerProfile.clearServerSkinOverride(); - PauseMenuCustomizeState.reset(); - ClientUUIDLoadingCache.resetFlags(); - pluginVersion = null; - pluginBrand = null; - protocolVersion = -1; - EaglerOutputStream bao = new EaglerOutputStream(); - DataOutputStream d = new DataOutputStream(bao); - - d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_VERSION); - - d.writeByte(2); // legacy protocol version - - d.write(getSPHandshakeProtocolData()); // write supported eagler protocol versions - - d.writeShort(1); // supported game protocols count - d.writeShort(47); // client supports 1.8 protocol - - String clientBrand = EaglercraftVersion.projectForkName; - d.writeByte(clientBrand.length()); - d.writeBytes(clientBrand); - - String clientVers = EaglercraftVersion.projectOriginVersion; - d.writeByte(clientVers.length()); - d.writeBytes(clientVers); - - d.writeBoolean(password != null); - - String username = mc.getSession().getProfile().getName(); - d.writeByte(username.length()); - d.writeBytes(username); - - client.send(bao.toByteArray()); - - byte[] read = awaitNextPacket(client, baseTimeout); - if(read == null) { - logger.error("Read timed out while waiting for server protocol response!"); - return false; - } - - DataInputStream di = new DataInputStream(new EaglerInputStream(read)); - - int type = di.read(); - if(type == HandshakePacketTypes.PROTOCOL_VERSION_MISMATCH) { - - StringBuilder protocols = new StringBuilder(); - int c = di.readShort(); - for(int i = 0; i < c; ++i) { - if(i > 0) { - protocols.append(", "); - } - protocols.append("v").append(di.readShort()); - } - - StringBuilder games = new StringBuilder(); - c = di.readShort(); - for(int i = 0; i < c; ++i) { - if(i > 0) { - games.append(", "); - } - games.append("mc").append(di.readShort()); - } - - logger.info("Incompatible client: v2/v3/v4 & mc47"); - logger.info("Server supports: {}", protocols); - logger.info("Server supports: {}", games); - - int msgLen = di.read(); - byte[] dat = new byte[msgLen]; - di.read(dat); - String msg = new String(dat, StandardCharsets.UTF_8); - - mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", new ChatComponentText(msg))); - - return false; - }else if(type == HandshakePacketTypes.PROTOCOL_SERVER_VERSION) { - protocolVersion = di.readShort(); - - if(protocolVersion != protocolV2 && protocolVersion != protocolV3 && protocolVersion != protocolV4) { - logger.info("Incompatible server version: {}", protocolVersion); - mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", new ChatComponentText(protocolVersion < protocolV2 ? "Outdated Server" : "Outdated Client"))); - return false; - } - - int gameVers = di.readShort(); - if(gameVers != 47) { - logger.info("Incompatible minecraft protocol version: {}", gameVers); - mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", new ChatComponentText("This server does not support 1.8!"))); - return false; - } - - logger.info("Server protocol: {}", protocolVersion); - - int msgLen = di.read(); - byte[] dat = new byte[msgLen]; - di.read(dat); - pluginBrand = ArrayUtils.asciiString(dat); - - msgLen = di.read(); - dat = new byte[msgLen]; - di.read(dat); - pluginVersion = ArrayUtils.asciiString(dat); - - logger.info("Server version: {}", pluginVersion); - logger.info("Server brand: {}", pluginBrand); - - int authType = di.read(); - int saltLength = (int)di.readShort() & 0xFFFF; - - byte[] salt = new byte[saltLength]; - di.read(salt); - - bao.reset(); - d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_REQUEST_LOGIN); - - d.writeByte(username.length()); - d.writeBytes(username); - - String requestedServer = "default"; - d.writeByte(requestedServer.length()); - d.writeBytes(requestedServer); - - if(authType != 0 && password != null && password.length() > 0) { - if(authType == HandshakePacketTypes.AUTH_METHOD_PLAINTEXT) { - if(allowPlaintext) { - logger.warn("Server is using insecure plaintext authentication"); - d.writeByte(password.length() << 1); - d.writeChars(password); - }else { - logger.error("Plaintext authentication was attempted but no user confirmation has been given to proceed"); - mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", - new ChatComponentText(EnumChatFormatting.RED + "Plaintext authentication was attempted but no user confirmation has been given to proceed"))); - return false; - } - }else if(authType == HandshakePacketTypes.AUTH_METHOD_EAGLER_SHA256) { - SHA256Digest digest = new SHA256Digest(); - - int passLen = password.length(); - - digest.update((byte)((passLen >>> 8) & 0xFF)); - digest.update((byte)(passLen & 0xFF)); - - for(int i = 0; i < passLen; ++i) { - char codePoint = password.charAt(i); - digest.update((byte)((codePoint >>> 8) & 0xFF)); - digest.update((byte)(codePoint & 0xFF)); - } - - digest.update(HandshakePacketTypes.EAGLER_SHA256_SALT_SAVE, 0, 32); - - byte[] hashed = new byte[32]; - digest.doFinal(hashed, 0); - - digest.reset(); - - digest.update(hashed, 0, 32); - digest.update(salt, 0, 32); - digest.update(HandshakePacketTypes.EAGLER_SHA256_SALT_BASE, 0, 32); - - digest.doFinal(hashed, 0); - - digest.reset(); - - digest.update(hashed, 0, 32); - digest.update(salt, 32, 32); - digest.update(HandshakePacketTypes.EAGLER_SHA256_SALT_BASE, 0, 32); - - digest.doFinal(hashed, 0); - - d.writeByte(32); - d.write(hashed); - }else if(authType == HandshakePacketTypes.AUTH_METHOD_AUTHME_SHA256) { - SHA256Digest digest = new SHA256Digest(); - - byte[] passwd = password.getBytes(StandardCharsets.UTF_8); - digest.update(passwd, 0, passwd.length); - - byte[] hashed = new byte[32]; - digest.doFinal(hashed, 0); - - byte[] toHexAndSalt = new byte[64]; - for(int i = 0; i < 32; ++i) { - toHexAndSalt[i << 1] = HEX[(hashed[i] >> 4) & 0xF]; - toHexAndSalt[(i << 1) + 1] = HEX[hashed[i] & 0xF]; - } - - digest.reset(); - digest.update(toHexAndSalt, 0, 64); - digest.update(salt, 0, salt.length); - - digest.doFinal(hashed, 0); - - for(int i = 0; i < 32; ++i) { - toHexAndSalt[i << 1] = HEX[(hashed[i] >> 4) & 0xF]; - toHexAndSalt[(i << 1) + 1] = HEX[hashed[i] & 0xF]; - } - - d.writeByte(64); - d.write(toHexAndSalt); - }else { - logger.error("Unsupported authentication type: {}", authType); - mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", - new ChatComponentText(EnumChatFormatting.RED + "Unsupported authentication type: " + authType + "\n\n" + EnumChatFormatting.GRAY + "(Use a newer version of the client)"))); - return false; - } - }else { - d.writeByte(0); - } - if(protocolVersion >= protocolV4) { - d.writeBoolean(enableCookies); - if(enableCookies && cookieData != null) { - d.writeByte(cookieData.length); - d.write(cookieData); - }else { - d.writeByte(0); - } - } - - client.send(bao.toByteArray()); - - read = awaitNextPacket(client, baseTimeout); - if(read == null) { - logger.error("Read timed out while waiting for login negotiation response!"); - return false; - } - - di = new DataInputStream(new EaglerInputStream(read)); - type = di.read(); - if(type == HandshakePacketTypes.PROTOCOL_SERVER_ALLOW_LOGIN) { - msgLen = di.read(); - dat = new byte[msgLen]; - di.read(dat); - - String serverUsername = ArrayUtils.asciiString(dat); - - Minecraft.getMinecraft().getSession().update(serverUsername, new EaglercraftUUID(di.readLong(), di.readLong())); - - Map profileDataToSend = new HashMap<>(); - - if(protocolVersion >= 4) { - bao.reset(); - d.writeLong(EaglercraftVersion.clientBrandUUID.msb); - d.writeLong(EaglercraftVersion.clientBrandUUID.lsb); - profileDataToSend.put("brand_uuid_v1", bao.toByteArray()); - } - - byte[] packetSkin = EaglerProfile.getSkinPacket(protocolVersion); - if(packetSkin.length > 0xFFFF) { - throw new IOException("Skin packet is too long: " + packetSkin.length); - } - profileDataToSend.put(protocolVersion >= 4 ? "skin_v2" : "skin_v1", packetSkin); - - byte[] packetCape = EaglerProfile.getCapePacket(); - if(packetCape.length > 0xFFFF) { - throw new IOException("Cape packet is too long: " + packetCape.length); - } - profileDataToSend.put("cape_v1", packetCape); - - byte[] packetSignatureData = UpdateService.getClientSignatureData(); - if(packetSignatureData != null) { - profileDataToSend.put("update_cert_v1", packetSignatureData); - } - - if(protocolVersion >= 4) { - List> toSend = new ArrayList<>(profileDataToSend.entrySet()); - while(!toSend.isEmpty()) { - int sendLen = 2; - bao.reset(); - d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_PROFILE_DATA); - d.writeByte(0); // will be replaced - int packetCount = 0; - while(!toSend.isEmpty() && packetCount < 255) { - Entry etr = toSend.get(toSend.size() - 1); - int i = 3 + etr.getKey().length() + etr.getValue().length; - if(sendLen + i < 0xFF00) { - String profileDataType = etr.getKey(); - d.writeByte(profileDataType.length()); - d.writeBytes(profileDataType); - byte[] data = etr.getValue(); - d.writeShort(data.length); - d.write(data); - toSend.remove(toSend.size() - 1); - ++packetCount; - }else { - break; - } - } - byte[] send = bao.toByteArray(); - send[1] = (byte)packetCount; - client.send(send); - } - }else { - for(Entry etr : profileDataToSend.entrySet()) { - bao.reset(); - d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_PROFILE_DATA); - String profileDataType = etr.getKey(); - d.writeByte(profileDataType.length()); - d.writeBytes(profileDataType); - byte[] data = etr.getValue(); - d.writeShort(data.length); - d.write(data); - client.send(bao.toByteArray()); - } - } - - bao.reset(); - d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_FINISH_LOGIN); - client.send(bao.toByteArray()); - - read = awaitNextPacket(client, baseTimeout); - if(read == null) { - logger.error("Read timed out while waiting for login confirmation response!"); - return false; - } - - di = new DataInputStream(new EaglerInputStream(read)); - type = di.read(); - if(type == HandshakePacketTypes.PROTOCOL_SERVER_FINISH_LOGIN) { - return true; - }else if(type == HandshakePacketTypes.PROTOCOL_SERVER_ERROR) { - showError(mc, client, connecting, ret, di, protocolVersion == protocolV2); - return false; - }else { - return false; - } - }else if(type == HandshakePacketTypes.PROTOCOL_SERVER_DENY_LOGIN) { - if(protocolVersion == protocolV2) { - msgLen = di.read(); - }else { - msgLen = di.readUnsignedShort(); - } - dat = new byte[msgLen]; - di.read(dat); - String errStr = new String(dat, StandardCharsets.UTF_8); - mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", IChatComponent.Serializer.jsonToComponent(errStr))); - return false; - }else if(type == HandshakePacketTypes.PROTOCOL_SERVER_ERROR) { - showError(mc, client, connecting, ret, di, protocolVersion == protocolV2); - return false; - }else { - return false; - } - }else if(type == HandshakePacketTypes.PROTOCOL_SERVER_ERROR) { - showError(mc, client, connecting, ret, di, true); - return false; - }else { - return false; - } - }catch(Throwable t) { - logger.error("Exception in handshake"); - logger.error(t); - return false; - } - - } - - private static byte[] awaitNextPacket(IWebSocketClient client, long timeout) { - long millis = EagRuntime.steadyTimeMillis(); - IWebSocketFrame b; - while((b = client.getNextBinaryFrame()) == null) { - if(client.getState().isClosed()) { - return null; - } - EagUtils.sleep(50); - if(EagRuntime.steadyTimeMillis() - millis > timeout) { - client.close(); - return null; - } - } - return b.getByteArray(); - } - - private static void showError(Minecraft mc, IWebSocketClient client, GuiConnecting connecting, GuiScreen scr, DataInputStream err, boolean v2) throws IOException { - int errorCode = err.read(); - int msgLen = v2 ? err.read() : err.readUnsignedShort(); - - // workaround for bug in EaglerXBungee 1.2.7 and below - if(msgLen == 0) { - if(v2) { - if(err.available() == 256) { - msgLen = 256; - } - }else { - if(err.available() == 65536) { - msgLen = 65536; - } - } - } - - byte[] dat = new byte[msgLen]; - err.read(dat); - String errStr = new String(dat, StandardCharsets.UTF_8); - logger.info("Server Error Code {}: {}", errorCode, errStr); - if(errorCode == HandshakePacketTypes.SERVER_ERROR_RATELIMIT_BLOCKED) { - RateLimitTracker.registerBlock(client.getCurrentURI()); - mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(scr)); - }else if(errorCode == HandshakePacketTypes.SERVER_ERROR_RATELIMIT_LOCKED) { - RateLimitTracker.registerLockOut(client.getCurrentURI()); - mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(scr)); - }else if(errorCode == HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE) { - mc.displayGuiScreen(new GuiDisconnected(scr, "connect.failed", IChatComponent.Serializer.jsonToComponent(errStr))); - }else if(connecting != null && errorCode == HandshakePacketTypes.SERVER_ERROR_AUTHENTICATION_REQUIRED) { - mc.displayGuiScreen(new GuiAuthenticationScreen(connecting, scr, errStr)); - }else { - mc.displayGuiScreen(new GuiDisconnected(scr, "connect.failed", new ChatComponentText("Server Error Code " + errorCode + "\n" + errStr))); - } - } - - public static GuiScreen displayAuthProtocolConfirm(int protocol, GuiScreen no, GuiScreen yes) { - if(protocol == HandshakePacketTypes.AUTH_METHOD_PLAINTEXT) { - return new GuiHandshakeApprove("plaintext", no, yes); - }else if(protocol != HandshakePacketTypes.AUTH_METHOD_EAGLER_SHA256 && protocol != HandshakePacketTypes.AUTH_METHOD_AUTHME_SHA256) { - return new GuiHandshakeApprove("unsupportedAuth", no); - }else { - return null; - } - } - - private static final byte[] HEX = new byte[] { - (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', - (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' - }; -} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/EaglercraftNetworkManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/EaglercraftNetworkManager.java index 9d348ed0..f734c67e 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/EaglercraftNetworkManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/EaglercraftNetworkManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * Copyright (c) 2022-2025 lax1dude. All Rights Reserved. * * 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 @@ -22,6 +22,8 @@ import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.handshake.ServerCapabilities; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.message.InjectedMessageController; import net.minecraft.network.EnumConnectionState; import net.minecraft.network.INetHandler; import net.minecraft.network.Packet; @@ -38,6 +40,9 @@ public abstract class EaglercraftNetworkManager { protected String pluginBrand = null; protected String pluginVersion = null; + protected InjectedMessageController injectedController = null; + + protected ServerCapabilities serverCapabilities = null; public static final Logger logger = LogManager.getLogger("NetworkManager"); @@ -45,10 +50,17 @@ public abstract class EaglercraftNetworkManager { this.address = address; this.temporaryBuffer = new PacketBuffer(Unpooled.buffer(0x1FFFF)); } - - public void setPluginInfo(String pluginBrand, String pluginVersion) { + + public void setPluginInfo(String pluginBrand, String pluginVersion, ServerCapabilities serverCapabilities) { this.pluginBrand = pluginBrand; this.pluginVersion = pluginVersion; + this.serverCapabilities = serverCapabilities; + } + + public void setLANInfo(int protocolVer) { + this.pluginBrand = "integrated"; + this.pluginVersion = "v" + protocolVer; + this.serverCapabilities = ServerCapabilities.getLAN(); } public String getPluginBrand() { @@ -59,6 +71,14 @@ public abstract class EaglercraftNetworkManager { return pluginVersion; } + public ServerCapabilities getServerCapabilities() { + return serverCapabilities; + } + + public void setInjectedMessageController(InjectedMessageController controller) { + injectedController = controller; + } + public abstract void connect(); public abstract EnumEaglerConnectionState getConnectStatus(); @@ -109,5 +129,7 @@ public abstract class EaglercraftNetworkManager { } } } - + + public abstract void injectRawFrame(byte[] data); + } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/GuiHandshakeApprove.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/GuiHandshakeApprove.java index 5dcb8cbb..23750a00 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/GuiHandshakeApprove.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/GuiHandshakeApprove.java @@ -104,4 +104,14 @@ public class GuiHandshakeApprove extends GuiScreen { } } + public static GuiScreen displayAuthProtocolConfirm(int protocol, GuiScreen no, GuiScreen yes) { + if(protocol == HandshakePacketTypes.AUTH_METHOD_PLAINTEXT) { + return new GuiHandshakeApprove("plaintext", no, yes); + }else if(protocol != HandshakePacketTypes.AUTH_METHOD_EAGLER_SHA256 && protocol != HandshakePacketTypes.AUTH_METHOD_AUTHME_SHA256) { + return new GuiHandshakeApprove("unsupportedAuth", no); + }else { + return null; + } + } + } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/HandshakePacketTypes.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/HandshakePacketTypes.java index da69a94d..5df7a8a9 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/HandshakePacketTypes.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/HandshakePacketTypes.java @@ -29,12 +29,15 @@ public class HandshakePacketTypes { public static final int PROTOCOL_CLIENT_PROFILE_DATA = 0x07; public static final int PROTOCOL_CLIENT_FINISH_LOGIN = 0x08; public static final int PROTOCOL_SERVER_FINISH_LOGIN = 0x09; + public static final int PROTOCOL_SERVER_REDIRECT_TO = 0x0A; public static final int PROTOCOL_SERVER_ERROR = 0xFF; - public static final int STATE_OPENED = 0x00; - public static final int STATE_CLIENT_VERSION = 0x01; - public static final int STATE_CLIENT_LOGIN = 0x02; - public static final int STATE_CLIENT_COMPLETE = 0x03; + public static final int STATE_NEW = 0x00; + public static final int STATE_OPENED = 0x01; + public static final int STATE_CLIENT_VERSION = 0x02; + public static final int STATE_CLIENT_LOGIN = 0x03; + public static final int STATE_CLIENT_COMPLETE = 0x04; + public static final int STATE_FINISHED = 0x05; public static final int SERVER_ERROR_UNKNOWN_PACKET = 0x01; public static final int SERVER_ERROR_INVALID_PACKET = 0x02; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/WebSocketNetworkManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/WebSocketNetworkManager.java index 3b26c196..7b091476 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/WebSocketNetworkManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/WebSocketNetworkManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * Copyright (c) 2022-2025 lax1dude. All Rights Reserved. * * 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 @@ -71,6 +71,11 @@ public class WebSocketNetworkManager extends EaglercraftNetworkManager { ++debugPacketCounter; try { byte[] asByteArray = next.getByteArray(); + + if(injectedController != null && injectedController.handlePacket(asByteArray, 0)) { + continue; + } + ByteBuf nettyBuffer = Unpooled.buffer(asByteArray, asByteArray.length); nettyBuffer.writerIndex(asByteArray.length); PacketBuffer input = new PacketBuffer(nettyBuffer); @@ -150,4 +155,13 @@ public class WebSocketNetworkManager extends EaglercraftNetworkManager { } } + @Override + public void injectRawFrame(byte[] data) { + if(!isChannelOpen()) { + logger.error("Frame was injected on a closed connection"); + return; + } + webSocketClient.send(data); + } + } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientMessageHandler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientMessageHandler.java new file mode 100755 index 00000000..796f7fc2 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientMessageHandler.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.socket.protocol.client; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.minecraft.client.network.NetHandlerPlayClient; + +public abstract class ClientMessageHandler implements GameMessageHandler { + + protected final NetHandlerPlayClient netHandler; + + public ClientMessageHandler(NetHandlerPlayClient netHandler) { + this.netHandler = netHandler; + } + + public static ClientMessageHandler createClientHandler(int version, NetHandlerPlayClient netHandler) { + switch(version) { + case 3: + return new ClientV3MessageHandler(netHandler); + case 4: + return new ClientV4MessageHandler(netHandler); + case 5: + return new ClientV5MessageHandler(netHandler); + default: + throw new UnsupportedOperationException(); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientV3MessageHandler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientV3MessageHandler.java index b0b5f941..9ba67b96 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientV3MessageHandler.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientV3MessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 lax1dude. All Rights Reserved. + * Copyright (c) 2024-2025 lax1dude. All Rights Reserved. * * 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 @@ -20,20 +20,16 @@ import java.nio.charset.StandardCharsets; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.lax1dude.eaglercraft.v1_8.profile.SkinModel; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*; import net.lax1dude.eaglercraft.v1_8.update.UpdateService; import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; import net.minecraft.client.Minecraft; import net.minecraft.client.network.NetHandlerPlayClient; -public class ClientV3MessageHandler implements GameMessageHandler { - - private final NetHandlerPlayClient netHandler; +public class ClientV3MessageHandler extends ClientMessageHandler { public ClientV3MessageHandler(NetHandlerPlayClient netHandler) { - this.netHandler = netHandler; + super(netHandler); } public void handleServer(SPacketEnableFNAWSkinsEAG packet) { @@ -44,35 +40,19 @@ public class ClientV3MessageHandler implements GameMessageHandler { } public void handleServer(SPacketOtherCapeCustomEAG packet) { - netHandler.getCapeCache().cacheCapeCustom(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), - packet.customCape); + netHandler.getTextureCache().handlePacket(packet); } public void handleServer(SPacketOtherCapePresetEAG packet) { - netHandler.getCapeCache().cacheCapePreset(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), - packet.presetCape); + netHandler.getTextureCache().handlePacket(packet); } public void handleServer(SPacketOtherSkinCustomV3EAG packet) { - EaglercraftUUID responseUUID = new EaglercraftUUID(packet.uuidMost, packet.uuidLeast); - SkinModel modelId; - if(packet.modelID == (byte)0xFF) { - modelId = this.netHandler.getSkinCache().getRequestedSkinType(responseUUID); - }else { - modelId = SkinModel.getModelFromId(packet.modelID & 0x7F); - if((packet.modelID & 0x80) != 0 && modelId.sanitize) { - modelId = SkinModel.STEVE; - } - } - if(modelId.highPoly != null) { - modelId = SkinModel.STEVE; - } - this.netHandler.getSkinCache().cacheSkinCustom(responseUUID, packet.customSkin, modelId); + netHandler.getTextureCache().handlePacket(packet); } public void handleServer(SPacketOtherSkinPresetEAG packet) { - this.netHandler.getSkinCache().cacheSkinPreset(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), - packet.presetSkin); + netHandler.getTextureCache().handlePacket(packet); } public void handleServer(SPacketUpdateCertEAG packet) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientV4MessageHandler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientV4MessageHandler.java index 8202b6e2..1751b5de 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientV4MessageHandler.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientV4MessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 lax1dude. All Rights Reserved. + * Copyright (c) 2024-2025 lax1dude. All Rights Reserved. * * 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 @@ -16,81 +16,37 @@ package net.lax1dude.eaglercraft.v1_8.socket.protocol.client; -import java.nio.charset.StandardCharsets; - import net.lax1dude.eaglercraft.v1_8.ClientUUIDLoadingCache; -import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; import net.lax1dude.eaglercraft.v1_8.PauseMenuCustomizeState; import net.lax1dude.eaglercraft.v1_8.cookie.ServerCookieDataStore; import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; -import net.lax1dude.eaglercraft.v1_8.profile.SkinModel; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.skin_cache.ServerTextureCache; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.WrongPacketException; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*; import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache; -import net.lax1dude.eaglercraft.v1_8.update.UpdateService; import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; import net.lax1dude.eaglercraft.v1_8.webview.ServerInfoCache; import net.lax1dude.eaglercraft.v1_8.webview.WebViewOverlayController; import net.minecraft.client.Minecraft; import net.minecraft.client.network.NetHandlerPlayClient; -public class ClientV4MessageHandler implements GameMessageHandler { - - private final NetHandlerPlayClient netHandler; +public class ClientV4MessageHandler extends ClientV3MessageHandler { public ClientV4MessageHandler(NetHandlerPlayClient netHandler) { - this.netHandler = netHandler; + super(netHandler); } - public void handleServer(SPacketEnableFNAWSkinsEAG packet) { - netHandler.currentFNAWSkinAllowedState = packet.enableSkins; - netHandler.currentFNAWSkinForcedState = packet.force; - Minecraft.getMinecraft().getRenderManager().setEnableFNAWSkins(netHandler.currentFNAWSkinForcedState - || (netHandler.currentFNAWSkinAllowedState && Minecraft.getMinecraft().gameSettings.enableFNAWSkins)); - } - - public void handleServer(SPacketOtherCapeCustomEAG packet) { - netHandler.getCapeCache().cacheCapeCustom(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), - packet.customCape); - } - - public void handleServer(SPacketOtherCapePresetEAG packet) { - netHandler.getCapeCache().cacheCapePreset(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), - packet.presetCape); + public void handleServer(SPacketOtherSkinCustomV3EAG packet) { + throw new WrongPacketException(); } public void handleServer(SPacketOtherSkinCustomV4EAG packet) { - EaglercraftUUID responseUUID = new EaglercraftUUID(packet.uuidMost, packet.uuidLeast); - SkinModel modelId; - if(packet.modelID == (byte)0xFF) { - modelId = this.netHandler.getSkinCache().getRequestedSkinType(responseUUID); - }else { - modelId = SkinModel.getModelFromId(packet.modelID & 0x7F); - if((packet.modelID & 0x80) != 0 && modelId.sanitize) { - modelId = SkinModel.STEVE; - } - } - if(modelId.highPoly != null) { - modelId = SkinModel.STEVE; - } - this.netHandler.getSkinCache().cacheSkinCustom(responseUUID, SkinPacketVersionCache.convertToV3Raw(packet.customSkin), modelId); + netHandler.getTextureCache().handlePacket(packet); } - public void handleServer(SPacketOtherSkinPresetEAG packet) { - this.netHandler.getSkinCache().cacheSkinPreset(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.presetSkin); - } - - public void handleServer(SPacketUpdateCertEAG packet) { - if (EagRuntime.getConfiguration().allowUpdateSvc()) { - UpdateService.addCertificateToSet(packet.updateCert); - } - } - - public void handleServer(SPacketVoiceSignalAllowedEAG packet) { - if (VoiceClientController.isClientSupported()) { - VoiceClientController.handleVoiceSignalPacketTypeAllowed(packet.allowed, packet.iceServers); - } + public void handleServer(SPacketVoiceSignalConnectV3EAG packet) { + throw new WrongPacketException(); } public void handleServer(SPacketVoiceSignalConnectV4EAG packet) { @@ -105,34 +61,6 @@ public class ClientV4MessageHandler implements GameMessageHandler { } } - public void handleServer(SPacketVoiceSignalDescEAG packet) { - if (VoiceClientController.isClientSupported()) { - VoiceClientController.handleVoiceSignalPacketTypeDescription( - new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), - new String(packet.desc, StandardCharsets.UTF_8)); - } - } - - public void handleServer(SPacketVoiceSignalDisconnectPeerEAG packet) { - if (VoiceClientController.isClientSupported()) { - VoiceClientController.handleVoiceSignalPacketTypeDisconnect(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast)); - } - } - - public void handleServer(SPacketVoiceSignalGlobalEAG packet) { - if (VoiceClientController.isClientSupported()) { - VoiceClientController.handleVoiceSignalPacketTypeGlobalNew(packet.users); - } - } - - public void handleServer(SPacketVoiceSignalICEEAG packet) { - if (VoiceClientController.isClientSupported()) { - VoiceClientController.handleVoiceSignalPacketTypeICECandidate( - new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), - new String(packet.ice, StandardCharsets.UTF_8)); - } - } - public void handleServer(SPacketForceClientSkinPresetV4EAG packet) { EaglerProfile.handleForceSkinPreset(packet.presetSkin); } @@ -166,14 +94,10 @@ public class ClientV4MessageHandler implements GameMessageHandler { public void handleServer(SPacketInvalidatePlayerCacheV4EAG packet) { if(packet.players != null && packet.players.size() > 0) { + ServerTextureCache textureCache = this.netHandler.getTextureCache(); for(SPacketInvalidatePlayerCacheV4EAG.InvalidateRequest req : packet.players) { - EaglercraftUUID uuid = new EaglercraftUUID(req.uuidMost, req.uuidLeast); - if(req.invalidateSkin) { - this.netHandler.getSkinCache().handleInvalidate(uuid); - } - if(req.invalidateCape) { - this.netHandler.getCapeCache().handleInvalidate(uuid); - } + textureCache.dropPlayer(new EaglercraftUUID(req.uuidMost, req.uuidLeast), + req.invalidateSkin, req.invalidateCape); } } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientV5MessageHandler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientV5MessageHandler.java new file mode 100755 index 00000000..b4f2b6de --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/ClientV5MessageHandler.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.socket.protocol.client; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.WrongPacketException; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*; +import net.lax1dude.eaglercraft.v1_8.webview.GuiScreenPhishingWarning; +import net.lax1dude.eaglercraft.v1_8.webview.GuiScreenRecieveServerInfo; +import net.lax1dude.eaglercraft.v1_8.webview.GuiScreenRequestDisplay; +import net.lax1dude.eaglercraft.v1_8.webview.GuiScreenServerInfo; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.network.NetHandlerPlayClient; + +public class ClientV5MessageHandler extends ClientV4MessageHandler { + + public ClientV5MessageHandler(NetHandlerPlayClient netHandler) { + super(netHandler); + } + + public void handleServer(SPacketOtherSkinPresetEAG packet) { + throw new WrongPacketException(); + } + + public void handleServer(SPacketOtherSkinCustomV4EAG packet) { + throw new WrongPacketException(); + } + + public void handleServer(SPacketOtherCapePresetEAG packet) { + throw new WrongPacketException(); + } + + public void handleServer(SPacketOtherCapeCustomEAG packet) { + throw new WrongPacketException(); + } + + public void handleServer(SPacketOtherSkinPresetV5EAG packet) { + netHandler.getTextureCache().handlePacket(packet); + } + + public void handleServer(SPacketOtherSkinCustomV5EAG packet) { + netHandler.getTextureCache().handlePacket(packet); + } + + public void handleServer(SPacketOtherCapePresetV5EAG packet) { + netHandler.getTextureCache().handlePacket(packet); + } + + public void handleServer(SPacketOtherCapeCustomV5EAG packet) { + netHandler.getTextureCache().handlePacket(packet); + } + + public void handleServer(SPacketOtherTexturesV5EAG packet) { + netHandler.getTextureCache().handlePacket(packet); + } + + public void handleServer(SPacketClientStateFlagV5EAG packet) { + StateFlags.setFlag(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.state); + } + + public void handleServer(SPacketDisplayWebViewURLV5EAG packet) { + if (netHandler.allowedDisplayWebview && !netHandler.allowedDisplayWebviewYes) { + return; + } + Minecraft mc = Minecraft.getMinecraft(); + GuiScreen screen = GuiScreenServerInfo.createForDisplayRequest(mc.currentScreen, packet.flags, + packet.embedTitle, packet.embedURL); + if (!mc.gameSettings.hasHiddenPhishWarning && !GuiScreenPhishingWarning.hasShownMessage) { + screen = new GuiScreenPhishingWarning(screen); + } + if (!netHandler.allowedDisplayWebview) { + mc.displayGuiScreen(new GuiScreenRequestDisplay(screen, mc.currentScreen, netHandler)); + } else { + mc.displayGuiScreen(screen); + } + } + + public void handleServer(SPacketDisplayWebViewBlobV5EAG packet) { + if (netHandler.allowedDisplayWebview && !netHandler.allowedDisplayWebviewYes) { + return; + } + Minecraft mc = Minecraft.getMinecraft(); + GuiScreen screen = new GuiScreenRecieveServerInfo(mc.currentScreen, packet.embedHash, + (parent, blob, permissionsOriginUUID) -> { + return GuiScreenServerInfo.createForDisplayRequest(parent, packet.flags, packet.embedTitle, blob, + permissionsOriginUUID); + }); + if (!mc.gameSettings.hasHiddenPhishWarning && !GuiScreenPhishingWarning.hasShownMessage) { + screen = new GuiScreenPhishingWarning(screen); + } + if (!netHandler.allowedDisplayWebview) { + mc.displayGuiScreen(new GuiScreenRequestDisplay(screen, mc.currentScreen, netHandler)); + } else { + mc.displayGuiScreen(screen); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/GameProtocolMessageController.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/GameProtocolMessageController.java deleted file mode 100755 index ba147f1d..00000000 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/GameProtocolMessageController.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (c) 2024 lax1dude. All Rights Reserved. - * - * 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. - * - */ - -package net.lax1dude.eaglercraft.v1_8.socket.protocol.client; - -import java.io.IOException; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageConstants; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; -import net.lax1dude.eaglercraft.v1_8.sp.server.socket.protocol.ServerV3MessageHandler; -import net.lax1dude.eaglercraft.v1_8.sp.server.socket.protocol.ServerV4MessageHandler; -import net.minecraft.client.network.NetHandlerPlayClient; -import net.minecraft.network.NetHandlerPlayServer; -import net.minecraft.network.PacketBuffer; - -public class GameProtocolMessageController { - - private static final Logger logger = LogManager.getLogger("GameProtocolMessageController"); - - public final GamePluginMessageProtocol protocol; - public final int sendDirection; - public final int receiveDirection; - private final PacketBufferInputWrapper inputStream = new PacketBufferInputWrapper(null); - private final PacketBufferOutputWrapper outputStream = new PacketBufferOutputWrapper(null); - private final GameMessageHandler handler; - private final IPluginMessageSendFunction sendFunction; - private final List sendQueueV4; - private final boolean noDelay; - - public GameProtocolMessageController(GamePluginMessageProtocol protocol, int sendDirection, GameMessageHandler handler, - IPluginMessageSendFunction sendCallback) { - this.protocol = protocol; - this.sendDirection = sendDirection; - this.receiveDirection = GamePluginMessageConstants.oppositeDirection(sendDirection); - this.handler = handler; - this.sendFunction = sendCallback; - this.noDelay = protocol.ver < 4 || EagRuntime.getConfiguration().isEaglerNoDelay(); - this.sendQueueV4 = !noDelay ? new LinkedList<>() : null; - } - - public boolean handlePacket(String channel, PacketBuffer data) throws IOException { - GameMessagePacket pkt; - if(protocol.ver >= 4 && data.readableBytes() > 0 && data.getByte(data.readerIndex()) == (byte) 0xFF - && channel.equals(GamePluginMessageConstants.V4_CHANNEL)) { - data.readByte(); - inputStream.buffer = data; - int count = inputStream.readVarInt(); - for(int i = 0, j, k; i < count; ++i) { - j = data.readVarIntFromBuffer(); - k = data.readerIndex() + j; - if(j > data.readableBytes()) { - throw new IOException("Packet fragment is too long: " + j + " > " + data.readableBytes()); - } - pkt = protocol.readPacket(channel, receiveDirection, inputStream); - if(pkt != null) { - try { - pkt.handlePacket(handler); - }catch(Throwable t) { - logger.error("Failed to handle packet {} in direction {} using handler {}!", pkt.getClass().getSimpleName(), - GamePluginMessageConstants.getDirectionString(receiveDirection), handler); - logger.error(t); - } - }else { - logger.warn("Could not read packet fragment {} of {}, unknown packet", count, i); - } - if(data.readerIndex() != k) { - logger.warn("Packet fragment {} was the wrong length: {} != {}", - (pkt != null ? pkt.getClass().getSimpleName() : "unknown"), j + data.readerIndex() - k, j); - data.readerIndex(k); - } - } - if(data.readableBytes() > 0) { - logger.warn("Leftover data after reading multi-packet! ({} bytes)", data.readableBytes()); - } - inputStream.buffer = null; - return true; - } - inputStream.buffer = data; - pkt = protocol.readPacket(channel, receiveDirection, inputStream); - if(pkt != null && inputStream.available() > 0) { - logger.warn("Leftover data after reading packet {}! ({} bytes)", pkt.getClass().getSimpleName(), inputStream.available()); - } - inputStream.buffer = null; - if(pkt != null) { - try { - pkt.handlePacket(handler); - }catch(Throwable t) { - logger.error("Failed to handle packet {} in direction {} using handler {}!", pkt.getClass().getSimpleName(), - GamePluginMessageConstants.getDirectionString(receiveDirection), handler); - logger.error(t); - } - return true; - }else { - return false; - } - } - - public void sendPacket(GameMessagePacket packet) throws IOException { - int len = packet.length() + 1; - PacketBuffer buf = new PacketBuffer(len != 0 ? Unpooled.buffer(len) : Unpooled.buffer(64)); - outputStream.buffer = buf; - String chan = protocol.writePacket(sendDirection, outputStream, packet); - outputStream.buffer = null; - int j = buf.writerIndex(); - if(len != 0 && j != len && j + 1 != len) { - logger.warn("Packet {} was expected to be {} bytes but was serialized to {} bytes!", - packet.getClass().getSimpleName(), len, j); - } - if(sendQueueV4 != null && chan.equals(GamePluginMessageConstants.V4_CHANNEL)) { - sendQueueV4.add(buf); - }else { - sendFunction.sendPluginMessage(chan, buf); - } - } - - public void flush() { - if(sendQueueV4 != null) { - int queueLen = sendQueueV4.size(); - PacketBuffer pkt; - if(queueLen == 0) { - return; - }else if(queueLen == 1) { - pkt = sendQueueV4.remove(0); - sendFunction.sendPluginMessage(GamePluginMessageConstants.V4_CHANNEL, pkt); - }else { - int i, j, sendCount, totalLen, lastLen; - PacketBuffer sendBuffer; - while(sendQueueV4.size() > 0) { - sendCount = 0; - totalLen = 0; - Iterator itr = sendQueueV4.iterator(); - do { - i = itr.next().readableBytes(); - lastLen = GamePacketOutputBuffer.getVarIntSize(i) + i; - totalLen += lastLen; - ++sendCount; - }while(totalLen < 32760 && itr.hasNext()); - if(totalLen >= 32760) { - --sendCount; - totalLen -= lastLen; - } - if(sendCount <= 1) { - pkt = sendQueueV4.remove(0); - sendFunction.sendPluginMessage(GamePluginMessageConstants.V4_CHANNEL, pkt); - continue; - } - sendBuffer = new PacketBuffer(Unpooled.buffer(1 + totalLen + GamePacketOutputBuffer.getVarIntSize(sendCount))); - sendBuffer.writeByte(0xFF); - sendBuffer.writeVarIntToBuffer(sendCount); - for(j = 0; j < sendCount; ++j) { - pkt = sendQueueV4.remove(0); - sendBuffer.writeVarIntToBuffer(pkt.readableBytes()); - sendBuffer.writeBytes(pkt); - } - sendFunction.sendPluginMessage(GamePluginMessageConstants.V4_CHANNEL, sendBuffer); - } - } - } - } - - public static GameMessageHandler createClientHandler(int protocolVersion, NetHandlerPlayClient netHandler) { - switch(protocolVersion) { - case 2: - case 3: - return new ClientV3MessageHandler(netHandler); - case 4: - return new ClientV4MessageHandler(netHandler); - default: - throw new IllegalArgumentException("Unknown protocol verison: " + protocolVersion); - } - } - - public static GameMessageHandler createServerHandler(int protocolVersion, NetHandlerPlayServer netHandler) { - switch(protocolVersion) { - case 2: - case 3: - return new ServerV3MessageHandler(netHandler); - case 4: - return new ServerV4MessageHandler(netHandler); - default: - throw new IllegalArgumentException("Unknown protocol verison: " + protocolVersion); - } - } -} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/StateFlags.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/StateFlags.java new file mode 100755 index 00000000..da923e0d --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/StateFlags.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.socket.protocol.client; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; + +public class StateFlags { + + public static final EaglercraftUUID EAGLER_PLAYER_FLAG_PRESENT = new EaglercraftUUID(0x55F63601694140D9L, + 0xB77BCE7B99A62E52L); + + public static final EaglercraftUUID LEGACY_EAGLER_PLAYER_FLAG_PRESENT = new EaglercraftUUID(0xEEEEA64771094C4EL, + 0x86E55B81D17E67EBL); + + public static final EaglercraftUUID DISABLE_SKIN_URL_LOOKUP = new EaglercraftUUID(0xC41D641BE2DA4094L, + 0xB1B2DFF2E9D08180L); + + public static boolean eaglerPlayerFlag = false; + + public static boolean eaglerPlayerFlagSupervisor = false; + + public static boolean disableSkinURLLookup = false; + + public static void setFlag(EaglercraftUUID flag, int value) { + if (flag.equals(EAGLER_PLAYER_FLAG_PRESENT)) { + eaglerPlayerFlag = (value & 1) != 0; + eaglerPlayerFlagSupervisor = (value & 2) != 0; + } else if (flag.equals(DISABLE_SKIN_URL_LOOKUP)) { + disableSkinURLLookup = value != 0; + } + } + + public static void reset() { + eaglerPlayerFlag = false; + eaglerPlayerFlagSupervisor = false; + disableSkinURLLookup = false; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/AuthTypes.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/AuthTypes.java new file mode 100755 index 00000000..e40322c6 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/AuthTypes.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2022-2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.socket.protocol.handshake; + +import java.nio.charset.StandardCharsets; + +import net.lax1dude.eaglercraft.v1_8.crypto.SHA256Digest; +import net.lax1dude.eaglercraft.v1_8.socket.HandshakePacketTypes; + +public class AuthTypes { + + public static byte[] applyEaglerSHA256(String password, byte[] salt) { + SHA256Digest digest = new SHA256Digest(); + + int passLen = password.length(); + + digest.update((byte)((passLen >>> 8) & 0xFF)); + digest.update((byte)(passLen & 0xFF)); + + for (int i = 0; i < passLen; ++i) { + char codePoint = password.charAt(i); + digest.update((byte)((codePoint >>> 8) & 0xFF)); + digest.update((byte)(codePoint & 0xFF)); + } + + digest.update(HandshakePacketTypes.EAGLER_SHA256_SALT_SAVE, 0, 32); + + byte[] hashed = new byte[32]; + digest.doFinal(hashed, 0); + + digest.reset(); + + digest.update(hashed, 0, 32); + digest.update(salt, 0, 32); + digest.update(HandshakePacketTypes.EAGLER_SHA256_SALT_BASE, 0, 32); + + digest.doFinal(hashed, 0); + + digest.reset(); + + digest.update(hashed, 0, 32); + digest.update(salt, 32, 32); + digest.update(HandshakePacketTypes.EAGLER_SHA256_SALT_BASE, 0, 32); + + digest.doFinal(hashed, 0); + + return hashed; + } + + public static byte[] applyAuthMeSHA256(String password, byte[] salt) { + SHA256Digest digest = new SHA256Digest(); + + byte[] passwd = password.getBytes(StandardCharsets.UTF_8); + digest.update(passwd, 0, passwd.length); + + byte[] hashed = new byte[32]; + digest.doFinal(hashed, 0); + + byte[] toHexAndSalt = new byte[64]; + for (int i = 0; i < 32; ++i) { + toHexAndSalt[i << 1] = HEX[(hashed[i] >> 4) & 0xF]; + toHexAndSalt[(i << 1) + 1] = HEX[hashed[i] & 0xF]; + } + + digest.reset(); + digest.update(toHexAndSalt, 0, 64); + digest.update(salt, 0, salt.length); + + digest.doFinal(hashed, 0); + + for (int i = 0; i < 32; ++i) { + toHexAndSalt[i << 1] = HEX[(hashed[i] >> 4) & 0xF]; + toHexAndSalt[(i << 1) + 1] = HEX[hashed[i] & 0xF]; + } + + return toHexAndSalt; + } + + private static final byte[] HEX = new byte[] { + (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', + (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' + }; + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/ClientCapabilities.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/ClientCapabilities.java new file mode 100755 index 00000000..df50eaaf --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/ClientCapabilities.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.socket.protocol.handshake; + +import java.util.ArrayList; +import java.util.List; + +import com.carrotsearch.hppc.IntArrayList; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.update.UpdateService; +import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; +import net.lax1dude.eaglercraft.v1_8.webview.WebViewOverlayController; + +public class ClientCapabilities { + + static ClientCapabilities createCapabilities(boolean cookie) { + ClientCapabilities caps = new ClientCapabilities(); + + caps.addStandard(StandardCaps.REDIRECT, 0); + caps.addStandard(StandardCaps.NOTIFICATION, 0); + caps.addStandard(StandardCaps.PAUSE_MENU, 0); + + if (VoiceClientController.isClientSupported()) { + caps.addStandard(StandardCaps.VOICE, 0); + } + + if (UpdateService.supported()) { + caps.addStandard(StandardCaps.UPDATE, 0); + } + + if (WebViewOverlayController.supported() || WebViewOverlayController.fallbackSupported()) { + caps.addStandard(StandardCaps.WEBVIEW, 0); + } + + if (cookie) { + caps.addStandard(StandardCaps.COOKIE, 0); + } + + return caps; + } + + static class ExtCapability { + final EaglercraftUUID uuid; + final int vers; + ExtCapability(EaglercraftUUID uuid, int vers) { + this.uuid = uuid; + this.vers = vers; + } + } + + private int standardCaps = 0; + private IntArrayList standardCapsVers = new IntArrayList(); + private List extendedCaps = new ArrayList<>(); + + int getStandardCaps() { + return standardCaps; + } + + int[] getStandardCapsVers() { + return standardCapsVers.toArray(); + } + + ExtCapability[] getExtendedCaps() { + return extendedCaps.toArray(new ExtCapability[extendedCaps.size()]); + } + + private void addStandard(int type, int... versions) { + int bit = (1 << type); + standardCapsVers.insert(Integer.bitCount(standardCaps & (bit - 1)), bits(versions)); + standardCaps |= bit; + } + + private void addExtended(EaglercraftUUID type, int... versions) { + extendedCaps.add(new ExtCapability(type, bits(versions))); + } + + private int bits(int... versions) { + int bits = 0; + for(int i = 0; i < versions.length; ++i) { + bits |= (1 << versions[i]); + } + return bits; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/HandshakerHandler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/HandshakerHandler.java new file mode 100755 index 00000000..14052cb1 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/HandshakerHandler.java @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.socket.protocol.handshake; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import net.lax1dude.eaglercraft.v1_8.ArrayUtils; +import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream; +import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.lax1dude.eaglercraft.v1_8.profile.GuiAuthenticationScreen; +import net.lax1dude.eaglercraft.v1_8.socket.HandshakePacketTypes; +import net.lax1dude.eaglercraft.v1_8.socket.RateLimitTracker; +import net.lax1dude.eaglercraft.v1_8.socket.WebSocketNetworkManager; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiDisconnected; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.multiplayer.GuiConnecting; +import net.minecraft.client.network.NetHandlerPlayClient; +import net.minecraft.network.EnumConnectionState; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.IChatComponent; + +public class HandshakerHandler { + + static final Logger logger = LogManager.getLogger("HandshakerHandler"); + + protected final Minecraft mc; + protected final IWebSocketClient websocket; + protected final GuiConnecting parent; + protected final GuiScreen ret; + protected final String username; + protected final String password; + protected final boolean allowPlaintext; + protected final boolean enableCookies; + protected final byte[] cookieData; + protected HandshakerInstance handshaker; + protected boolean nicknameSelection = true; + protected int baseState = NEW; + protected WebSocketNetworkManager networkManager; + + protected static final int NEW = 0, SENT_HANDSHAKE = 1, PROCESSING = 2, FINISHED = 3; + + public HandshakerHandler(GuiConnecting parent, IWebSocketClient websocket, String username, String password, + boolean allowPlaintext, boolean enableCookies, byte[] cookieData) { + this.mc = GuiConnecting.getMC(parent); + this.websocket = websocket; + this.parent = parent; + this.ret = GuiConnecting.getPrevScreen(parent); + this.username = username; + this.password = password; + this.allowPlaintext = allowPlaintext; + this.enableCookies = enableCookies; + this.cookieData = cookieData; + } + + private static final int protocolV3 = 3; + private static final int protocolV4 = 4; + private static final int protocolV5 = 5; + + public static byte[] getSPHandshakeProtocolData() { + try { + EaglerOutputStream bao = new EaglerOutputStream(); + DataOutputStream d = new DataOutputStream(bao); + d.writeShort(3); // supported eagler protocols count + d.writeShort(protocolV3); // client supports v3 + d.writeShort(protocolV4); // client supports v4 + d.writeShort(protocolV5); // client supports v5 + return bao.toByteArray(); + }catch(IOException ex) { + throw new RuntimeException(ex); + } + } + + public void tick() { + if(baseState == NEW) { + if(websocket.isClosed()) { + handleError("Connection Closed", null); + return; + } + baseState = SENT_HANDSHAKE; + beginHandshake(); + }else if(baseState == SENT_HANDSHAKE) { + IWebSocketFrame frame = websocket.getNextBinaryFrame(); + if(frame != null) { + byte[] data = frame.getByteArray(); + handleServerHandshake(new PacketBuffer(Unpooled.buffer(data, data.length).writerIndex(data.length))); + } + }else if(baseState == PROCESSING) { + handshaker.tick(); + }else if(baseState == FINISHED) { + if(networkManager != null) { + try { + networkManager.processReceivedPackets(); + } catch (IOException e) { + } + } + } + } + + protected void beginHandshake() { + PacketBuffer buffer = new PacketBuffer(Unpooled.buffer()); + + buffer.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_VERSION); + + buffer.writeByte(2); // legacy protocol version + + buffer.writeBytes(getSPHandshakeProtocolData()); // write supported eagler protocol versions + + buffer.writeShort(1); // supported game protocols count + buffer.writeShort(47); // client supports 1.8 protocol + + String clientBrand = EaglercraftVersion.projectForkName; + buffer.writeByte(clientBrand.length()); + writeASCII(buffer, clientBrand); + + String clientVers = EaglercraftVersion.projectOriginVersion; + buffer.writeByte(clientVers.length()); + writeASCII(buffer, clientVers); + + buffer.writeBoolean(password != null); + + buffer.writeByte(username.length()); + writeASCII(buffer, username); + + websocket.send(buffer.toBytes()); + } + + protected static void writeASCII(PacketBuffer buffer, String str) { + for(int i = 0, l = str.length(); i < l; ++i) { + buffer.writeByte(str.charAt(i)); + } + } + + protected void handleServerHandshake(PacketBuffer packet) { + try { + int pktId = packet.readUnsignedByte(); + switch(pktId) { + case HandshakePacketTypes.PROTOCOL_SERVER_VERSION: + handleServerVersion(packet); + break; + case HandshakePacketTypes.PROTOCOL_VERSION_MISMATCH: + handleVersionMismatch(packet); + break; + case HandshakePacketTypes.PROTOCOL_SERVER_ERROR: + handleServerError(packet, false); + break; + default: + handleError("connect.failed", new ChatComponentText("Unknown packet type " + pktId + " received")); + break; + } + }catch(Exception ex) { + handleError("connect.failed", new ChatComponentText("Invalid packet received")); + logger.error("Invalid packet received"); + logger.error(ex); + } + } + + protected void handleServerVersion(PacketBuffer packet) { + int protocolVersion = packet.readUnsignedShort(); + + if(protocolVersion != protocolV3 && protocolVersion != protocolV4 && protocolVersion != protocolV5) { + logger.info("Incompatible server version: {}", protocolVersion); + handleError("connect.failed", new ChatComponentText(protocolVersion < protocolV3 ? "Outdated Server" : "Outdated Client")); + return; + } + + int gameVers = packet.readUnsignedShort(); + if(gameVers != 47) { + logger.info("Incompatible minecraft protocol version: {}", gameVers); + handleError("connect.failed", new ChatComponentText("This server does not support 1.8!")); + return; + } + + logger.info("Server protocol: {}", protocolVersion); + + int msgLen = packet.readUnsignedByte(); + byte[] dat = new byte[msgLen]; + packet.readBytes(dat); + String pluginBrand = ArrayUtils.asciiString(dat); + + msgLen = packet.readUnsignedByte(); + dat = new byte[msgLen]; + packet.readBytes(dat); + String pluginVersion = ArrayUtils.asciiString(dat); + + logger.info("Server version: {}", pluginVersion); + logger.info("Server brand: {}", pluginBrand); + + int authType = packet.readUnsignedByte(); + int saltLength = (int)packet.readUnsignedShort() & 0xFFFF; + + byte[] salt = new byte[saltLength]; + packet.readBytes(salt); + + if(protocolVersion >= protocolV5) { + nicknameSelection = packet.readBoolean(); + } + + baseState = PROCESSING; + switch(protocolVersion) { + case protocolV3: + handshaker = new HandshakerV3(this); + break; + case protocolV4: + handshaker = new HandshakerV4(this); + break; + case protocolV5: + handshaker = new HandshakerV5(this); + break; + } + + handshaker.begin(pluginBrand, pluginVersion, authType, salt); + } + + protected void handleVersionMismatch(PacketBuffer packet) { + StringBuilder protocols = new StringBuilder(); + int c = packet.readUnsignedShort(); + for(int i = 0; i < c; ++i) { + if(i > 0) { + protocols.append(", "); + } + protocols.append("v").append(packet.readUnsignedShort()); + } + + StringBuilder games = new StringBuilder(); + c = packet.readUnsignedShort(); + for(int i = 0; i < c; ++i) { + if(i > 0) { + games.append(", "); + } + games.append("mc").append(packet.readUnsignedShort()); + } + + logger.info("Incompatible client: v3/v4/v5 & mc47"); + logger.info("Server supports: {}", protocols); + logger.info("Server supports: {}", games); + + int msgLen = packet.readUnsignedByte(); + byte[] dat = new byte[msgLen]; + packet.readBytes(dat); + String msg = new String(dat, StandardCharsets.UTF_8); + + handleError("connect.failed", new ChatComponentText(msg)); + } + + protected void handleServerError(PacketBuffer packet, boolean v3) { + int errCode = packet.readUnsignedByte(); + int msgLen; + if(v3) { + msgLen = packet.readUnsignedShort(); + if(msgLen == 0 && packet.readableBytes() == 65536) { + // workaround for bug in EaglerXBungee 1.2.7 and below + msgLen = 65536; + } + }else { + msgLen = packet.readUnsignedByte(); + if(msgLen == 0 && packet.readableBytes() == 256) { + // workaround for bug in EaglerXBungee 1.2.7 and below + msgLen = 256; + } + } + byte[] dat = new byte[msgLen]; + packet.readBytes(dat); + String msg = new String(dat, StandardCharsets.UTF_8); + if(errCode == HandshakePacketTypes.SERVER_ERROR_RATELIMIT_BLOCKED) { + handleRatelimit(false, new ChatComponentText(msg)); + }else if(errCode == HandshakePacketTypes.SERVER_ERROR_RATELIMIT_LOCKED) { + handleRatelimit(true, new ChatComponentText(msg)); + }else if(errCode == HandshakePacketTypes.SERVER_ERROR_AUTHENTICATION_REQUIRED) { + handleAuthRequired(msg); + }else if(errCode == HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE) { + handleError("connect.failed", v3 ? IChatComponent.Serializer.jsonToComponent(msg) : new ChatComponentText(msg)); + }else { + handleError("connect.failed", new ChatComponentText("Server Error Code " + errCode + "\n" + msg)); + } + } + + protected void handleSuccess() { + if(baseState != FINISHED) { + baseState = FINISHED; + websocket.setEnableStringFrames(false); + websocket.clearStringFrames(); + networkManager = new WebSocketNetworkManager(websocket); + networkManager.setPluginInfo(handshaker.pluginBrand, handshaker.pluginVersion, new ServerCapabilities( + handshaker.serverStandardCaps, handshaker.serverStandardCapVers, handshaker.extendedCaps)); + mc.bungeeOutdatedMsgTimer = 80; + mc.clearTitles(); + mc.getSession().update(handshaker.username, handshaker.uuid); + networkManager.setConnectionState(EnumConnectionState.PLAY); + new NetHandlerPlayClient(this.mc, ret, networkManager, this.mc.getSession().getProfile(), + GamePluginMessageProtocol.getByVersion(handshaker.getVersion())); + } + } + + protected void handleServerRedirectTo(String address) { + mc.handleReconnectPacket(address); + websocket.close(); + if(baseState != FINISHED) { + baseState = FINISHED; + mc.displayGuiScreen(ret); + } + } + + protected void handleRatelimit(boolean locked, IChatComponent detail) { + if(locked) { + RateLimitTracker.registerLockOut(websocket.getCurrentURI()); + }else { + RateLimitTracker.registerBlock(websocket.getCurrentURI()); + } + websocket.close(); + if(baseState != FINISHED) { + baseState = FINISHED; + mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(ret)); + } + } + + protected void handleError(String message, IChatComponent detail) { + websocket.close(); + if(baseState != FINISHED) { + baseState = FINISHED; + mc.displayGuiScreen(new GuiDisconnected(ret, message, detail != null ? detail : new ChatComponentText(""))); + } + } + + protected void handleAuthRequired(String message) { + websocket.close(); + if(baseState != FINISHED) { + baseState = FINISHED; + mc.displayGuiScreen(new GuiAuthenticationScreen(parent, ret, message)); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/HandshakerInstance.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/HandshakerInstance.java new file mode 100755 index 00000000..d50177b2 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/HandshakerInstance.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.socket.protocol.handshake; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import com.carrotsearch.hppc.ObjectByteMap; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; +import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame; +import net.lax1dude.eaglercraft.v1_8.netty.ByteBuf; +import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; +import net.lax1dude.eaglercraft.v1_8.socket.HandshakePacketTypes; +import net.lax1dude.eaglercraft.v1_8.update.UpdateService; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.IChatComponent; + +public abstract class HandshakerInstance { + + protected final HandshakerHandler handler; + protected String pluginBrand; + protected String pluginVersion; + protected String username; + protected EaglercraftUUID uuid; + protected int state = HandshakePacketTypes.STATE_NEW; + protected int serverStandardCaps; + protected byte[] serverStandardCapVers; + protected ObjectByteMap extendedCaps; + + public HandshakerInstance(HandshakerHandler handler) { + this.handler = handler; + } + + protected void begin(String pluginBrand, String pluginVersion, int authType, byte[] salt) { + this.pluginBrand = pluginBrand; + this.pluginVersion = pluginVersion; + byte[] password = null; + if (handler.password != null) { + switch(authType) { + case HandshakePacketTypes.AUTH_METHOD_NONE: + break; + case HandshakePacketTypes.AUTH_METHOD_EAGLER_SHA256: + password = AuthTypes.applyEaglerSHA256(handler.password, salt); + break; + case HandshakePacketTypes.AUTH_METHOD_AUTHME_SHA256: + password = AuthTypes.applyAuthMeSHA256(handler.password, salt); + break; + case HandshakePacketTypes.AUTH_METHOD_PLAINTEXT: + if(!handler.allowPlaintext) { + handleError("disconnect.loginFailed", new ChatComponentText("Server attempted insecure plaintext authentication without user consent!")); + return; + } + password = handler.password.getBytes(StandardCharsets.UTF_8); + if(password.length > 255) { + handleError("disconnect.loginFailed", new ChatComponentText("Password is too long!")); + return; + } + break; + default: + handleError("disconnect.loginFailed", new ChatComponentText("Unknown auth method #" + authType + " requested")); + return; + } + } + + sendClientRequestLogin(handler.username, "default", password, handler.enableCookies, handler.cookieData); + + state = HandshakePacketTypes.STATE_CLIENT_LOGIN; + } + + protected abstract int getVersion(); + + protected abstract void sendClientRequestLogin(String username, String requestedServer, byte[] password, + boolean enableCookies, byte[] cookie); + + protected void tick() { + IWebSocketFrame frame; + while (state != HandshakePacketTypes.STATE_FINISHED + && (frame = handler.websocket.getNextBinaryFrame()) != null) { + handleInboundPacket(frame.getByteArray()); + } + if(handler.websocket.isClosed()) { + handleError("Connection Closed", (IChatComponent) null); + } + } + + protected void handleInboundPacket(byte[] data) { + try { + PacketBuffer buffer = new PacketBuffer(Unpooled.buffer(data, data.length).writerIndex(data.length)); + int pktId = buffer.readUnsignedByte(); + switch(pktId) { + case HandshakePacketTypes.PROTOCOL_SERVER_ALLOW_LOGIN: + handleInboundServerAllowLogin(buffer); + break; + case HandshakePacketTypes.PROTOCOL_SERVER_DENY_LOGIN: + handleInboundServerDenyLogin(buffer); + break; + case HandshakePacketTypes.PROTOCOL_SERVER_FINISH_LOGIN: + handleServerFinishLogin(); + break; + case HandshakePacketTypes.PROTOCOL_SERVER_REDIRECT_TO: + handleInboundServerRedirectTo(buffer); + break; + case HandshakePacketTypes.PROTOCOL_SERVER_ERROR: + handleInboundServerError(buffer); + break; + default: + handleError("connect.failed", "Unknown packet type " + pktId + " received"); + break; + } + }catch(Exception ex) { + handler.handleError("connect.failed", new ChatComponentText("Invalid packet received")); + HandshakerHandler.logger.error("Invalid packet received"); + HandshakerHandler.logger.error(ex); + } + } + + protected void handleError(String message, String detail) { + state = HandshakePacketTypes.STATE_FINISHED; + handler.handleError(message, IChatComponent.Serializer.jsonToComponent(detail)); + } + + protected void handleError(String message, IChatComponent detail) { + state = HandshakePacketTypes.STATE_FINISHED; + handler.handleError(message, detail); + } + + protected abstract void handleInboundServerAllowLogin(PacketBuffer buffer); + + protected abstract void handleInboundServerDenyLogin(PacketBuffer buffer); + + protected void handleServerAllowLogin(String username, EaglercraftUUID uuid, int serverStandardCaps, + byte[] serverStandardCapVers, ObjectByteMap extendedCaps) { + if(state != HandshakePacketTypes.STATE_CLIENT_LOGIN) { + handleError("connect.failed", "Unexpected allow login packet in state " + state); + return; + } + + this.username = username; + this.uuid = uuid; + this.serverStandardCaps = serverStandardCaps; + this.serverStandardCapVers = serverStandardCapVers; + this.extendedCaps = extendedCaps; + + Map profileDataToSend = new HashMap<>(); + + if(getVersion() >= 4) { + byte[] arr = new byte[16]; + ByteBuf buf = Unpooled.buffer(arr, 16); + buf.writeLong(EaglercraftVersion.clientBrandUUID.msb); + buf.writeLong(EaglercraftVersion.clientBrandUUID.lsb); + profileDataToSend.put("brand_uuid_v1", arr); + } + + byte[] packetSkin = EaglerProfile.getSkinPacket(getVersion()); + if(packetSkin.length > 0xFFFF) { + handleError("connect.failed", new ChatComponentText("Skin packet is too long: " + packetSkin.length)); + return; + } + profileDataToSend.put(getVersion() >= 4 ? "skin_v2" : "skin_v1", packetSkin); + + byte[] packetCape = EaglerProfile.getCapePacket(); + if(packetCape.length > 0xFFFF) { + handleError("connect.failed", new ChatComponentText("Cape packet is too long: " + packetCape.length)); + return; + } + profileDataToSend.put("cape_v1", packetCape); + + byte[] packetSignatureData = UpdateService.getClientSignatureData(); + if(packetSignatureData != null) { + profileDataToSend.put("update_cert_v1", packetSignatureData); + } + + sendClientProfileData(profileDataToSend); + + sendFinishLogin(); + + state = HandshakePacketTypes.STATE_CLIENT_COMPLETE; + } + + protected abstract void sendClientProfileData(Map profileDataToSend); + + protected abstract void sendFinishLogin(); + + protected void handleServerFinishLogin() { + if(state != HandshakePacketTypes.STATE_CLIENT_COMPLETE) { + handleError("connect.failed", "Unexpected finish login packet in state " + state); + return; + } + + state = HandshakePacketTypes.STATE_FINISHED; + + handler.handleSuccess(); + } + + protected abstract void handleInboundServerRedirectTo(PacketBuffer buffer); + + protected void handleServerRedirectTo(String address) { + state = HandshakePacketTypes.STATE_FINISHED; + + handler.handleServerRedirectTo(address); + } + + protected abstract void handleInboundServerError(PacketBuffer buffer); + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/HandshakerV3.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/HandshakerV3.java new file mode 100755 index 00000000..80b5e10b --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/HandshakerV3.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.socket.protocol.handshake; + +import java.nio.charset.StandardCharsets; +import java.util.Map; + +import net.lax1dude.eaglercraft.v1_8.ArrayUtils; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.lax1dude.eaglercraft.v1_8.socket.HandshakePacketTypes; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.IChatComponent; + +public class HandshakerV3 extends HandshakerInstance { + + public HandshakerV3(HandshakerHandler handler) { + super(handler); + } + + @Override + protected int getVersion() { + return 3; + } + + @Override + protected void sendClientRequestLogin(String username, String requestedServer, byte[] password, + boolean enableCookies, byte[] cookie) { + PacketBuffer buffer = new PacketBuffer(Unpooled.buffer()); + buffer.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_REQUEST_LOGIN); + buffer.writeByte(username.length()); + HandshakerHandler.writeASCII(buffer, username); + buffer.writeByte(requestedServer.length()); + HandshakerHandler.writeASCII(buffer, requestedServer); + if(password != null) { + buffer.writeByte(password.length); + buffer.writeBytes(password); + }else { + buffer.writeByte(0); + } + handler.websocket.send(buffer.toBytes()); + } + + @Override + protected void handleInboundServerAllowLogin(PacketBuffer buffer) { + byte[] username = new byte[buffer.readUnsignedByte()]; + buffer.readBytes(username); + EaglercraftUUID uuid = new EaglercraftUUID(buffer.readLong(), buffer.readLong()); + handleServerAllowLogin(ArrayUtils.asciiString(username), uuid, ServerCapabilities.VIRTUAL_V3_SERVER_CAPS, + ServerCapabilities.VIRTUAL_V3_SERVER_CAPS_VERS, null); + } + + @Override + protected void handleInboundServerDenyLogin(PacketBuffer buffer) { + byte[] dat = new byte[buffer.readUnsignedShort()]; + buffer.readBytes(dat); + handleError("disconnect.loginFailed", + IChatComponent.Serializer.jsonToComponent(new String(dat, StandardCharsets.UTF_8))); + } + + @Override + protected void sendClientProfileData(Map profileDataToSend) { + for(Map.Entry etr : profileDataToSend.entrySet()) { + PacketBuffer buffer = new PacketBuffer(Unpooled.buffer()); + buffer.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_PROFILE_DATA); + String profileDataType = etr.getKey(); + buffer.writeByte(profileDataType.length()); + HandshakerHandler.writeASCII(buffer, profileDataType); + byte[] data = etr.getValue(); + buffer.writeShort(data.length); + buffer.writeBytes(data); + handler.websocket.send(buffer.toBytes()); + } + } + + @Override + protected void sendFinishLogin() { + handler.websocket.send(new byte[] { (byte) HandshakePacketTypes.PROTOCOL_CLIENT_FINISH_LOGIN }); + } + + @Override + protected void handleInboundServerRedirectTo(PacketBuffer buffer) { + handleError("disconnect.loginFailed", "Unexpected login redirect packet"); + } + + @Override + protected void handleInboundServerError(PacketBuffer buffer) { + state = HandshakePacketTypes.STATE_FINISHED; + handler.handleServerError(buffer, true); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/HandshakerV4.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/HandshakerV4.java new file mode 100755 index 00000000..125ffb5d --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/HandshakerV4.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.socket.protocol.handshake; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import net.lax1dude.eaglercraft.v1_8.ArrayUtils; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.lax1dude.eaglercraft.v1_8.socket.HandshakePacketTypes; +import net.minecraft.network.PacketBuffer; + +public class HandshakerV4 extends HandshakerV3 { + + public HandshakerV4(HandshakerHandler handler) { + super(handler); + } + + @Override + protected int getVersion() { + return 4; + } + + @Override + protected void sendClientRequestLogin(String username, String requestedServer, byte[] password, + boolean enableCookies, byte[] cookie) { + PacketBuffer buffer = new PacketBuffer(Unpooled.buffer()); + buffer.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_REQUEST_LOGIN); + buffer.writeByte(username.length()); + HandshakerHandler.writeASCII(buffer, username); + buffer.writeByte(requestedServer.length()); + HandshakerHandler.writeASCII(buffer, requestedServer); + if(password != null) { + buffer.writeByte(password.length); + buffer.writeBytes(password); + }else { + buffer.writeByte(0); + } + buffer.writeBoolean(enableCookies); + if(enableCookies && cookie != null) { + buffer.writeByte(cookie.length); + buffer.writeBytes(cookie); + }else { + buffer.writeByte(0); + } + handler.websocket.send(buffer.toBytes()); + } + + @Override + protected void handleInboundServerAllowLogin(PacketBuffer buffer) { + byte[] username = new byte[buffer.readUnsignedByte()]; + buffer.readBytes(username); + EaglercraftUUID uuid = new EaglercraftUUID(buffer.readLong(), buffer.readLong()); + handleServerAllowLogin(ArrayUtils.asciiString(username), uuid, ServerCapabilities.VIRTUAL_V4_SERVER_CAPS, + ServerCapabilities.VIRTUAL_V4_SERVER_CAPS_VERS, null); + } + + @Override + protected void sendClientProfileData(Map profileDataToSend) { + List> toSend = new ArrayList<>(profileDataToSend.entrySet()); + PacketBuffer buffer = new PacketBuffer(Unpooled.buffer()); + while(!toSend.isEmpty()) { + int sendLen = 2; + buffer.writerIndex(0); + buffer.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_PROFILE_DATA); + buffer.writeByte(0); // will be replaced + int packetCount = 0; + while(!toSend.isEmpty() && packetCount < 255) { + Entry etr = toSend.get(toSend.size() - 1); + int i = 3 + etr.getKey().length() + etr.getValue().length; + if(sendLen + i < 0xFF00) { + String profileDataType = etr.getKey(); + buffer.writeByte(profileDataType.length()); + HandshakerHandler.writeASCII(buffer, profileDataType); + byte[] data = etr.getValue(); + buffer.writeShort(data.length); + buffer.writeBytes(data); + toSend.remove(toSend.size() - 1); + ++packetCount; + }else { + break; + } + } + byte[] send = buffer.toBytes(); + send[1] = (byte)packetCount; + handler.websocket.send(send); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/HandshakerV5.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/HandshakerV5.java new file mode 100755 index 00000000..99e255f0 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/HandshakerV5.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.socket.protocol.handshake; + +import java.nio.charset.StandardCharsets; + +import com.carrotsearch.hppc.ObjectByteHashMap; +import com.carrotsearch.hppc.ObjectByteMap; + +import net.lax1dude.eaglercraft.v1_8.ArrayUtils; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.lax1dude.eaglercraft.v1_8.socket.HandshakePacketTypes; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.handshake.ClientCapabilities.ExtCapability; +import net.minecraft.network.PacketBuffer; + +public class HandshakerV5 extends HandshakerV4 { + + public HandshakerV5(HandshakerHandler handler) { + super(handler); + } + + @Override + protected int getVersion() { + return 5; + } + + @Override + protected void sendClientRequestLogin(String username, String requestedServer, byte[] password, + boolean enableCookies, byte[] cookie) { + PacketBuffer buffer = new PacketBuffer(Unpooled.buffer()); + buffer.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_REQUEST_LOGIN); + if(handler.nicknameSelection) { + buffer.writeByte(username.length()); + HandshakerHandler.writeASCII(buffer, username); + }else { + buffer.writeByte(0); + } + buffer.writeByte(requestedServer.length()); + HandshakerHandler.writeASCII(buffer, requestedServer); + if(password != null) { + buffer.writeByte(password.length); + buffer.writeBytes(password); + }else { + buffer.writeByte(0); + } + buffer.writeBoolean(enableCookies); + if(enableCookies && cookie != null) { + buffer.writeByte(cookie.length); + buffer.writeBytes(cookie); + }else { + buffer.writeByte(0); + } + ClientCapabilities caps = ClientCapabilities.createCapabilities(enableCookies); + buffer.writeVarIntToBuffer(caps.getStandardCaps()); + int[] vers = caps.getStandardCapsVers(); + for(int i = 0; i < vers.length; ++i) { + buffer.writeVarIntToBuffer(vers[i]); + } + ExtCapability[] extVers = caps.getExtendedCaps(); + buffer.writeByte(extVers.length); + for(int i = 0; i < extVers.length; ++i) { + ExtCapability extCap = extVers[i]; + buffer.writeLong(extCap.uuid.msb); + buffer.writeLong(extCap.uuid.lsb); + buffer.writeVarIntToBuffer(extCap.vers); + } + handler.websocket.send(buffer.toBytes()); + } + + @Override + protected void handleInboundServerAllowLogin(PacketBuffer buffer) { + byte[] username = new byte[buffer.readUnsignedByte()]; + buffer.readBytes(username); + EaglercraftUUID uuid = new EaglercraftUUID(buffer.readLong(), buffer.readLong()); + int standardCaps = buffer.readVarIntFromBuffer(); + byte[] standardCapsVers = new byte[Integer.bitCount(standardCaps)]; + buffer.readBytes(standardCapsVers); + int extCaps = buffer.readUnsignedByte(); + ObjectByteMap extCapsMap = null; + if(extCaps > 0) { + extCapsMap = new ObjectByteHashMap<>(extCaps); + for (int i = 0; i < extCaps; ++i) { + extCapsMap.put(new EaglercraftUUID(buffer.readLong(), buffer.readLong()), buffer.readByte()); + } + } + handleServerAllowLogin(ArrayUtils.asciiString(username), uuid, standardCaps, standardCapsVers, extCapsMap); + } + + @Override + protected void handleInboundServerRedirectTo(PacketBuffer buffer) { + byte[] urlLen = new byte[buffer.readShort()]; + buffer.readBytes(urlLen); + handleServerRedirectTo(new String(urlLen, StandardCharsets.UTF_8)); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/ServerCapabilities.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/ServerCapabilities.java new file mode 100755 index 00000000..523998b0 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/ServerCapabilities.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.socket.protocol.handshake; + +import com.carrotsearch.hppc.ObjectByteMap; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; + +public class ServerCapabilities { + + static final int VIRTUAL_V3_SERVER_CAPS = (1 << StandardCaps.UPDATE) | (1 << StandardCaps.VOICE); + static final byte[] VIRTUAL_V3_SERVER_CAPS_VERS = new byte[] { 0, 0 }; + + static final int VIRTUAL_V4_SERVER_CAPS = (1 << StandardCaps.UPDATE) | (1 << StandardCaps.VOICE) + | (1 << StandardCaps.REDIRECT) | (1 << StandardCaps.NOTIFICATION) | (1 << StandardCaps.PAUSE_MENU) + | (1 << StandardCaps.WEBVIEW) | (1 << StandardCaps.COOKIE); + static final byte[] VIRTUAL_V4_SERVER_CAPS_VERS = new byte[] { 0, 0, 0, 0, 0, 0 }; + + private final int standardCaps; + private final byte[] standardCapVers; + private final ObjectByteMap extendedCaps; + + public ServerCapabilities(int standardCaps, byte[] standardCapVers, ObjectByteMap extendedCaps) { + this.standardCaps = standardCaps; + this.standardCapVers = standardCapVers; + this.extendedCaps = extendedCaps; + } + + public boolean hasCapability(int cap, int ver) { + int bit = 1 << cap; + if((standardCaps & bit) != 0) { + int versIndex = Integer.bitCount(standardCaps & (bit - 1)); + if(versIndex < standardCapVers.length) { + return (standardCapVers[versIndex] & 0xFF) >= ver; + } + } + return false; + } + + public int getCapability(int cap) { + int bit = 1 << cap; + if((standardCaps & bit) != 0) { + int versIndex = Integer.bitCount(standardCaps & (bit - 1)); + if(versIndex < standardCapVers.length) { + return standardCapVers[versIndex] & 0xFF; + } + } + return -1; + } + + public int getExtCapability(EaglercraftUUID uuid) { + if(extendedCaps != null) { + int idx = extendedCaps.indexOf(uuid); + if(idx >= 0) { + return (int) extendedCaps.indexGet(idx) & 0xFF; + } + } + return -1; + } + + public static ServerCapabilities getLAN() { + return new ServerCapabilities(VIRTUAL_V3_SERVER_CAPS, VIRTUAL_V3_SERVER_CAPS_VERS, null); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/StandardCaps.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/StandardCaps.java new file mode 100755 index 00000000..773b57c3 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/handshake/StandardCaps.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.socket.protocol.handshake; + +public class StandardCaps { + + public static final int UPDATE = 0; + + public static final int VOICE = 1; + + public static final int REDIRECT = 2; + + public static final int NOTIFICATION = 3; + + public static final int PAUSE_MENU = 4; + + public static final int WEBVIEW = 5; + + public static final int COOKIE = 6; + + // reserved + public static final int EAGLER_IP = 7; + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/message/InjectedMessageController.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/message/InjectedMessageController.java new file mode 100755 index 00000000..a7a7a7c4 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/message/InjectedMessageController.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.socket.protocol.message; + +import java.io.IOException; +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.ReusableByteArrayInputStream; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.ReusableByteArrayOutputStream; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SimpleInputBufferImpl; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SimpleOutputBufferImpl; + +public class InjectedMessageController extends MessageController { + + private static final Logger logger = LogManager.getLogger("InjectedMessageController"); + + private final ReusableByteArrayInputStream byteInputStreamSingleton = new ReusableByteArrayInputStream(); + private final ReusableByteArrayOutputStream byteOutputStreamSingleton = new ReusableByteArrayOutputStream(); + private final SimpleInputBufferImpl inputStreamSingleton = new SimpleInputBufferImpl(byteInputStreamSingleton); + private final SimpleOutputBufferImpl outputStreamSingleton = new SimpleOutputBufferImpl(byteOutputStreamSingleton); + + public interface IBinarySendFunction { + void sendBinaryFrame(byte[] contents); + } + + private final IBinarySendFunction send; + + public InjectedMessageController(GamePluginMessageProtocol protocol, GameMessageHandler handler, int direction, + IBinarySendFunction send) { + super(protocol, handler, direction); + this.send = send; + } + + public boolean handlePacket(byte[] data, int offset) throws IOException { + if(data.length - offset > 1 && data[offset] == (byte) 0xEE) { + GameMessagePacket pkt; + byteInputStreamSingleton.feedBuffer(data, offset); + inputStreamSingleton.readByte(); + if(data[offset + 1] == (byte) 0xFF) { + inputStreamSingleton.readByte(); + int count = inputStreamSingleton.readVarInt(); + for(int i = 0, j, k; i < count; ++i) { + j = inputStreamSingleton.readVarInt(); + inputStreamSingleton.setToByteArrayReturns(j - 1); + k = byteInputStreamSingleton.getReaderIndex() + j; + if(j < 0 || j > inputStreamSingleton.available()) { + throw new IOException("Packet fragment is too long: " + j + " > " + inputStreamSingleton.available()); + } + pkt = protocol.readPacketV5(receiveDirection, inputStreamSingleton); + if(byteInputStreamSingleton.getReaderIndex() != k) { + throw new IOException("Packet fragment was the wrong length: " + (j + byteInputStreamSingleton.getReaderIndex() - k) + " != " + j); + } + handlePacket(pkt); + } + if(inputStreamSingleton.available() > 0) { + throw new IOException("Leftover data after reading multi-packet! (" + inputStreamSingleton.available() + " bytes)"); + } + byteOutputStreamSingleton.feedBuffer(null); + return true; + } + inputStreamSingleton.setToByteArrayReturns(data.length - offset - 2); + pkt = protocol.readPacketV5(receiveDirection, inputStreamSingleton); + if(byteInputStreamSingleton.available() != 0) { + throw new IOException("Packet was the wrong length: " + pkt.getClass().getSimpleName()); + } + byteOutputStreamSingleton.feedBuffer(null); + handlePacket(pkt); + return true; + } + return false; + } + + @Override + protected void writePacket(GameMessagePacket packet) throws IOException { + int len = packet.length() + 2; + byteOutputStreamSingleton.feedBuffer(len == 1 ? new byte[64] : new byte[len]); + byteOutputStreamSingleton.write(0xEE); + protocol.writePacketV5(sendDirection, outputStreamSingleton, packet); + byte[] data = byteOutputStreamSingleton.returnBuffer(); + byteOutputStreamSingleton.feedBuffer(null); + if(len != 1 && data.length != len) { + logger.warn("Packet " + packet.getClass().getSimpleName() + " was the wrong length after serialization, " + + data.length + " != " + len); + } + send.sendBinaryFrame(data); + } + + @Override + protected void writeMultiPacket(List packets) throws IOException { + int total = packets.size(); + byte[][] buffer = new byte[total][]; + byte[] dat; + for(int i = 0; i < total; ++i) { + GameMessagePacket packet = packets.get(i); + int len = packet.length() + 2; + byteOutputStreamSingleton.feedBuffer(len == 1 ? new byte[64] : new byte[len]); + byteOutputStreamSingleton.write(0xEE); + protocol.writePacketV5(sendDirection, outputStreamSingleton, packet); + dat = byteOutputStreamSingleton.returnBuffer(); + byteOutputStreamSingleton.feedBuffer(null); + if(len != 1 && dat.length != len) { + logger.warn("Packet " + packet.getClass().getSimpleName() + + " was the wrong length after serialization, " + dat.length + " != " + len); + } + buffer[i] = dat; + } + int start = 0; + int i, j, sendCount, totalLen, lastLen; + while(total > start) { + sendCount = 0; + totalLen = 0; + do { + i = buffer[start + sendCount].length - 1; + lastLen = GamePacketOutputBuffer.getVarIntSize(i) + i; + totalLen += lastLen; + ++sendCount; + }while(totalLen < 32760 && sendCount < total - start); + if(totalLen >= 32760) { + --sendCount; + totalLen -= lastLen; + } + if(sendCount <= 1) { + send.sendBinaryFrame(buffer[start++]); + continue; + } + byteOutputStreamSingleton.feedBuffer(new byte[2 + totalLen + GamePacketOutputBuffer.getVarIntSize(sendCount)]); + outputStreamSingleton.writeShort(0xEEFF); + outputStreamSingleton.writeVarInt(sendCount); + for(j = 0; j < sendCount; ++j) { + dat = buffer[start++]; + i = dat.length - 1; + outputStreamSingleton.writeVarInt(i); + outputStreamSingleton.write(dat, 1, i); + } + send.sendBinaryFrame(byteOutputStreamSingleton.returnBuffer()); + byteOutputStreamSingleton.feedBuffer(null); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/message/LegacyMessageController.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/message/LegacyMessageController.java new file mode 100755 index 00000000..92544472 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/message/LegacyMessageController.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2024-2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.socket.protocol.message; + +import java.io.IOException; +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageConstants; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.minecraft.network.PacketBuffer; + +public class LegacyMessageController extends MessageController { + + private static final Logger logger = LogManager.getLogger("LegacyMessageController"); + + public interface IPluginMessageSendFunction { + void sendPluginMessage(String channel, PacketBuffer contents); + } + + private final PacketBufferInputWrapper inputStream = new PacketBufferInputWrapper(null); + private final PacketBufferOutputWrapper outputStream = new PacketBufferOutputWrapper(null); + private final IPluginMessageSendFunction send; + + public LegacyMessageController(GamePluginMessageProtocol protocol, GameMessageHandler handler, int direction, + IPluginMessageSendFunction send) { + super(protocol, handler, direction); + this.send = send; + } + + public boolean handlePacket(String channel, PacketBuffer data) throws IOException { + GameMessagePacket pkt; + if(protocol.ver >= 4 && data.readableBytes() > 0 && data.getByte(data.readerIndex()) == (byte) 0xFF + && channel.equals(GamePluginMessageConstants.V4_CHANNEL)) { + data.readByte(); + inputStream.buffer = data; + int count = inputStream.readVarInt(); + for(int i = 0, j, k, l; i < count; ++i) { + j = data.readVarIntFromBuffer(); + k = data.readerIndex() + j; + l = data.writerIndex(); + if(j < 0 || k > l) { + throw new IOException("Packet fragment is too long: " + j + " > " + data.readableBytes()); + } + data.writerIndex(k); + pkt = protocol.readPacket(channel, receiveDirection, inputStream); + if(pkt != null) { + handlePacket(pkt); + }else { + logger.warn("Could not read packet fragment {} of {}, unknown packet", count, i); + } + if(data.readerIndex() != k) { + logger.warn("Packet fragment {} was the wrong length: {} != {}", + (pkt != null ? pkt.getClass().getSimpleName() : "unknown"), j + data.readerIndex() - k, j); + data.readerIndex(k); + } + data.writerIndex(l); + } + if(data.readableBytes() > 0) { + logger.warn("Leftover data after reading multi-packet! ({} bytes)", data.readableBytes()); + } + inputStream.buffer = null; + return true; + } + inputStream.buffer = data; + pkt = protocol.readPacket(channel, receiveDirection, inputStream); + if(pkt != null && inputStream.available() > 0) { + logger.warn("Leftover data after reading packet {}! ({} bytes)", pkt.getClass().getSimpleName(), inputStream.available()); + } + inputStream.buffer = null; + if(pkt != null) { + handlePacket(pkt); + return true; + }else { + return false; + } + } + + @Override + protected void writePacket(GameMessagePacket packet) throws IOException { + int len = packet.length() + 1; + PacketBuffer buf = new PacketBuffer(len != 0 ? Unpooled.buffer(len) : Unpooled.buffer(64)); + outputStream.buffer = buf; + String chan = protocol.writePacket(sendDirection, outputStream, packet); + outputStream.buffer = null; + int j = buf.writerIndex(); + if(len != 0 && j != len && (protocol.ver > 3 || j + 1 != len)) { + logger.warn("Packet {} was expected to be {} bytes but was serialized to {} bytes!", + packet.getClass().getSimpleName(), len, j); + } + send.sendPluginMessage(chan, buf); + } + + @Override + protected void writeMultiPacket(List packets) throws IOException { + int total = packets.size(); + PacketBuffer[] toSend = new PacketBuffer[total]; + for(int i = 0; i < total; ++i) { + GameMessagePacket packet = packets.get(i); + int len = packet.length() + 1; + PacketBuffer buf = new PacketBuffer(len != 0 ? Unpooled.buffer(len) : Unpooled.buffer(64)); + outputStream.buffer = buf; + protocol.writePacket(sendDirection, outputStream, packet); + outputStream.buffer = null; + int j = buf.writerIndex(); + if(len != 0 && j != len && (protocol.ver > 3 || j + 1 != len)) { + logger.warn("Packet {} was expected to be {} bytes but was serialized to {} bytes!", + packet.getClass().getSimpleName(), len, j); + } + toSend[i] = buf; + } + int start = 0; + int i, j, sendCount, totalLen, lastLen; + while(total > start) { + sendCount = 0; + totalLen = 0; + do { + i = toSend[start + sendCount].readableBytes(); + lastLen = GamePacketOutputBuffer.getVarIntSize(i) + i; + totalLen += lastLen; + ++sendCount; + }while(totalLen < 32760 && sendCount < total - start); + if(totalLen >= 32760) { + --sendCount; + totalLen -= lastLen; + } + if(sendCount <= 1) { + send.sendPluginMessage(GamePluginMessageConstants.V4_CHANNEL, toSend[start++]); + continue; + } + PacketBuffer sendBuffer = new PacketBuffer( + Unpooled.buffer(1 + totalLen + GamePacketOutputBuffer.getVarIntSize(sendCount))); + sendBuffer.writerIndex(0); + sendBuffer.writeByte(0xFF); + sendBuffer.writeVarIntToBuffer(sendCount); + for(j = 0; j < sendCount; ++j) { + PacketBuffer dat = toSend[start++]; + sendBuffer.writeVarIntToBuffer(dat.readableBytes()); + sendBuffer.writeBytes(dat); + } + send.sendPluginMessage(GamePluginMessageConstants.V4_CHANNEL, sendBuffer); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/message/MessageController.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/message/MessageController.java new file mode 100755 index 00000000..1ff62e11 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/message/MessageController.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.socket.protocol.message; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageConstants; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; + +public abstract class MessageController { + + private static final Logger logger = LogManager.getLogger("MessageController"); + + protected final GamePluginMessageProtocol protocol; + protected final GameMessageHandler handler; + protected final int sendDirection; + protected final int receiveDirection; + protected List sendQueue; + + public MessageController(GamePluginMessageProtocol protocol, GameMessageHandler handler, int direction) { + this.protocol = protocol; + this.handler = handler; + this.sendDirection = direction; + this.receiveDirection = direction == GamePluginMessageConstants.CLIENT_TO_SERVER + ? GamePluginMessageConstants.SERVER_TO_CLIENT + : GamePluginMessageConstants.CLIENT_TO_SERVER; + this.sendQueue = protocol.ver >= 4 && !EagRuntime.getConfiguration().isEaglerNoDelay() + ? new ArrayList<>() : null; + } + + public GamePluginMessageProtocol getProtocol() { + return protocol; + } + + public boolean isSendQueueEnabled() { + return sendQueue != null; + } + + public void sendPacket(GameMessagePacket packet) { + if(sendQueue != null) { + sendQueue.add(packet); + }else { + try { + writePacket(packet); + } catch (IOException ex) { + throw new RuntimeException("Failed to serialize packet: " + packet.getClass().getSimpleName(), ex); + } + } + } + + protected abstract void writePacket(GameMessagePacket packet) throws IOException; + + public void flush() { + if(sendQueue != null && !sendQueue.isEmpty()) { + try { + writeMultiPacket(sendQueue); + } catch (IOException ex) { + throw new RuntimeException("Failed to serialize packet multi-packet!", ex); + } + if(sendQueue.size() < 64) { + sendQueue.clear(); + }else { + sendQueue = new ArrayList<>(); + } + } + } + + protected abstract void writeMultiPacket(List packets) throws IOException; + + protected void handlePacket(GameMessagePacket packet) { + try { + packet.handlePacket(handler); + }catch(Throwable t) { + logger.error("Failed to handle packet {} in direction {} using handler {}!", packet.getClass().getSimpleName(), + GamePluginMessageConstants.getDirectionString(receiveDirection), handler); + logger.error(t); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/PacketBufferInputWrapper.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/message/PacketBufferInputWrapper.java similarity index 94% rename from src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/PacketBufferInputWrapper.java rename to src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/message/PacketBufferInputWrapper.java index c69335bb..d4ac3370 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/PacketBufferInputWrapper.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/message/PacketBufferInputWrapper.java @@ -14,7 +14,7 @@ * */ -package net.lax1dude.eaglercraft.v1_8.socket.protocol.client; +package net.lax1dude.eaglercraft.v1_8.socket.protocol.message; import java.io.DataInputStream; import java.io.EOFException; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/PacketBufferOutputWrapper.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/message/PacketBufferOutputWrapper.java similarity index 95% rename from src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/PacketBufferOutputWrapper.java rename to src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/message/PacketBufferOutputWrapper.java index 05dab4c9..d6ff3e86 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/PacketBufferOutputWrapper.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/message/PacketBufferOutputWrapper.java @@ -14,7 +14,7 @@ * */ -package net.lax1dude.eaglercraft.v1_8.socket.protocol.client; +package net.lax1dude.eaglercraft.v1_8.socket.protocol.message; import java.io.IOException; import java.io.OutputStream; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenLANConnecting.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenLANConnecting.java index 6efff00d..3fb21f86 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenLANConnecting.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenLANConnecting.java @@ -19,7 +19,7 @@ package net.lax1dude.eaglercraft.v1_8.sp.gui; import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC; import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; -import net.lax1dude.eaglercraft.v1_8.socket.ConnectionHandshake; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.handshake.HandshakerHandler; import net.lax1dude.eaglercraft.v1_8.sp.lan.LANClientNetworkManager; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServer; @@ -101,8 +101,11 @@ public class GuiScreenLANConnecting extends GuiScreen { if(++renderCount > 1) { RelayServerSocket sock; if(relay == null) { - sock = RelayManager.relayManager.getWorkingRelay((str) -> ls.resetProgressAndMessage("Connecting: " + str), 0x02, code); + ls.resetProgressAndMessage("Connecting to '" + code + "'..."); + sock = RelayManager.relayManager.getWorkingRelay((str) -> ls.displayLoadingString("Connecting: " + str), 0x02, code); }else { + ls.resetProgressAndMessage("Connecting to '" + code + "'..."); + ls.displayLoadingString("Connecting: " + relay.address); sock = RelayManager.relayManager.connectHandshake(relay, 0x02, code); } if(sock == null) { @@ -125,7 +128,7 @@ public class GuiScreenLANConnecting extends GuiScreen { networkManager.setNetHandler(new NetHandlerSingleplayerLogin(networkManager, mc, parent)); networkManager.sendPacket(new C00PacketLoginStart(this.mc.getSession().getProfile(), EaglerProfile.getSkinPacket(3), EaglerProfile.getCapePacket(), - ConnectionHandshake.getSPHandshakeProtocolData(), EaglercraftVersion.clientBrandUUID)); + HandshakerHandler.getSPHandshakeProtocolData(), EaglercraftVersion.clientBrandUUID)); } } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenSingleplayerConnecting.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenSingleplayerConnecting.java index 961070a9..b9d4751e 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenSingleplayerConnecting.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenSingleplayerConnecting.java @@ -21,7 +21,7 @@ import java.io.IOException; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; -import net.lax1dude.eaglercraft.v1_8.socket.ConnectionHandshake; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.handshake.HandshakerHandler; import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; import net.lax1dude.eaglercraft.v1_8.sp.socket.ClientIntegratedServerNetworkManager; import net.lax1dude.eaglercraft.v1_8.sp.socket.NetHandlerSingleplayerLogin; @@ -94,7 +94,7 @@ public class GuiScreenSingleplayerConnecting extends GuiScreen { this.networkManager.setNetHandler(new NetHandlerSingleplayerLogin(this.networkManager, this.mc, this.menu)); this.networkManager.sendPacket(new C00PacketLoginStart(this.mc.getSession().getProfile(), EaglerProfile.getSkinPacket(3), EaglerProfile.getCapePacket(), - ConnectionHandshake.getSPHandshakeProtocolData(), EaglercraftVersion.clientBrandUUID)); + HandshakerHandler.getSPHandshakeProtocolData(), EaglercraftVersion.clientBrandUUID)); } try { this.networkManager.processReceivedPackets(); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientNetworkManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientNetworkManager.java index 1d5be8ab..40aa3a93 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientNetworkManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientNetworkManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * Copyright (c) 2022-2025 lax1dude, ayunami2000. All Rights Reserved. * * 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 @@ -366,6 +366,10 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager { } } + if(injectedController != null && injectedController.handlePacket(fullData, off)) { + continue; + } + ByteBuf nettyBuffer = Unpooled.buffer(fullData, fullData.length); nettyBuffer.writerIndex(fullData.length); nettyBuffer.readerIndex(off); @@ -399,6 +403,32 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager { } } + @Override + public void injectRawFrame(byte[] data) { + if(!isChannelOpen()) { + logger.error("Frame was injected on a closed connection"); + return; + } + int len = data.length; + int fragmentSizeN1 = fragmentSize - 1; + if(len > fragmentSizeN1) { + int idx = 0; + do { + int readLen = len > fragmentSizeN1 ? fragmentSizeN1 : len; + byte[] frag = new byte[readLen + 1]; + System.arraycopy(data, idx, frag, 1, readLen); + idx += readLen; + len -= readLen; + frag[0] = len == 0 ? (byte)0 : (byte)1; + PlatformWebRTC.clientLANSendPacket(frag); + }while(len > 0); + }else { + byte[] bytes = new byte[len + 1]; + System.arraycopy(data, 0, bytes, 1, len); + PlatformWebRTC.clientLANSendPacket(bytes); + } + } + @Override public void closeChannel(IChatComponent reason) { if(!PlatformWebRTC.clientLANClosed()) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java index 1f28b08d..e33e2672 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024 lax1dude, ayunami2000. All Rights Reserved. + * Copyright (c) 2023-2025 lax1dude, ayunami2000. All Rights Reserved. * * 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 diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerMinecraftServer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerMinecraftServer.java index 4a264433..872086d0 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerMinecraftServer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerMinecraftServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024 lax1dude, ayunami2000. All Rights Reserved. + * Copyright (c) 2023-2025 lax1dude, ayunami2000. All Rights Reserved. * * 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 @@ -23,6 +23,7 @@ import java.util.List; import com.google.common.collect.Lists; import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EagUtils; import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.minecraft.entity.player.EntityPlayer; @@ -33,8 +34,7 @@ import net.minecraft.world.EnumDifficulty; import net.minecraft.world.WorldServer; import net.minecraft.world.WorldSettings; import net.minecraft.world.WorldSettings.GameType; -import net.lax1dude.eaglercraft.v1_8.sp.server.skins.IntegratedCapeService; -import net.lax1dude.eaglercraft.v1_8.sp.server.skins.IntegratedSkinService; +import net.lax1dude.eaglercraft.v1_8.sp.server.skins.IntegratedTextureService; import net.lax1dude.eaglercraft.v1_8.sp.server.voice.IntegratedVoiceService; public class EaglerMinecraftServer extends MinecraftServer { @@ -48,8 +48,7 @@ public class EaglerMinecraftServer extends MinecraftServer { protected WorldSettings newWorldSettings; protected boolean paused; protected EaglerSaveHandler saveHandler; - protected IntegratedSkinService skinService; - protected IntegratedCapeService capeService; + protected IntegratedTextureService textureService; protected IntegratedVoiceService voiceService; private long lastTPSUpdate = 0l; @@ -67,25 +66,22 @@ public class EaglerMinecraftServer extends MinecraftServer { super(world); Bootstrap.register(); this.saveHandler = new EaglerSaveHandler(savesDir, world); - this.skinService = new IntegratedSkinService(WorldsDB.newVFile(saveHandler.getWorldDirectory(), "eagler/skulls")); - this.capeService = new IntegratedCapeService(); + EaglerPlayerList playerList = new EaglerPlayerList(this, viewDistance); + this.textureService = new IntegratedTextureService(playerList, + WorldsDB.newVFile(saveHandler.getWorldDirectory(), "eagler/skulls")); this.voiceService = null; this.setServerOwner(owner); logger.info("server owner: " + owner); this.setDemo(demo); this.canCreateBonusChest(currentWorldSettings != null && currentWorldSettings.isBonusChestEnabled()); this.setBuildLimit(256); - this.setConfigManager(new EaglerPlayerList(this, viewDistance)); + this.setConfigManager(playerList); this.newWorldSettings = currentWorldSettings; this.paused = false; } - public IntegratedSkinService getSkinService() { - return skinService; - } - - public IntegratedCapeService getCapeService() { - return capeService; + public IntegratedTextureService getTextureService() { + return textureService; } public IntegratedVoiceService getVoiceService() { @@ -166,12 +162,14 @@ public class EaglerMinecraftServer extends MinecraftServer { this.currentTime += 50l; this.tick(); ++counterTicksPerSecond; + } else if (!singleThreadMode) { + EagUtils.sleep(1); } } } public void updateTimeLightAndEntities() { - this.skinService.flushCache(); + this.textureService.flushCache(); super.updateTimeLightAndEntities(); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerPlayerList.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerPlayerList.java index 55572ac8..f0ac1e62 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerPlayerList.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerPlayerList.java @@ -44,8 +44,5 @@ public class EaglerPlayerList extends ServerConfigurationManager { public void playerLoggedOut(EntityPlayerMP playerIn) { super.playerLoggedOut(playerIn); - EaglerMinecraftServer svr = (EaglerMinecraftServer)getServerInstance(); - svr.skinService.unregisterPlayer(playerIn.getUniqueID()); - svr.capeService.unregisterPlayer(playerIn.getUniqueID()); } } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/CustomSkullData.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/CustomSkullData.java index 9df49add..45dc5b0d 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/CustomSkullData.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/CustomSkullData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * Copyright (c) 2023-2025 lax1dude. All Rights Reserved. * * 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 @@ -17,35 +17,56 @@ package net.lax1dude.eaglercraft.v1_8.sp.server.skins; import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinCustomV3EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinCustomV4EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinCustomV5EAG; import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache; public class CustomSkullData { - public String skinURL; - public long lastHit; - public SkinPacketVersionCache skinData; + protected long lastHit; + protected byte[] textureDataV3; + protected byte[] textureDataV4; - public CustomSkullData(String skinURL, byte[] skinData) { - this.skinURL = skinURL; - this.lastHit = EagRuntime.steadyTimeMillis(); + public CustomSkullData(byte[] skinData) { if(skinData.length != 16384) { byte[] fixed = new byte[16384]; System.arraycopy(skinData, 0, fixed, 0, skinData.length > fixed.length ? fixed.length : skinData.length); skinData = fixed; } - this.skinData = SkinPacketVersionCache.createCustomV3(0l, 0l, 0, skinData); + textureDataV3 = skinData; + lastHit = EagRuntime.steadyTimeMillis(); } public byte[] getFullSkin() { - return ((SPacketOtherSkinCustomV3EAG)skinData.getV3()).customSkin; + return textureDataV3; } - public GameMessagePacket getSkinPacket(EaglercraftUUID uuid, GamePluginMessageProtocol protocol) { - return SkinPacketVersionCache.rewriteUUID(skinData.get(protocol), uuid.getMostSignificantBits(), uuid.getLeastSignificantBits()); + public GameMessagePacket getSkin(long uuidMost, long uuidLeast, GamePluginMessageProtocol protocol) { + switch(protocol) { + case V3: + return new SPacketOtherSkinCustomV3EAG(uuidMost, uuidLeast, 0xFF, textureDataV3); + case V4: + if(textureDataV4 == null) { + textureDataV4 = SkinPacketVersionCache.convertToV4Raw(textureDataV3); + } + return new SPacketOtherSkinCustomV4EAG(uuidMost, uuidLeast, 0xFF, textureDataV4); + default: + throw new IllegalStateException(); + } + } + + public GameMessagePacket getSkinV5(int requestId, GamePluginMessageProtocol protocol) { + if(protocol.ver >= 5) { + if(textureDataV4 == null) { + textureDataV4 = SkinPacketVersionCache.convertToV4Raw(textureDataV3); + } + return new SPacketOtherSkinCustomV5EAG(requestId, 0xFF, textureDataV4); + }else { + throw new IllegalStateException(); + } } } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/CustomSkullLoader.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/CustomSkullLoader.java new file mode 100755 index 00000000..ba5f81ec --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/CustomSkullLoader.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.sp.server.skins; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.crypto.SHA1Digest; +import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2; +import net.lax1dude.eaglercraft.v1_8.sp.server.WorldsDB; + +public class CustomSkullLoader { + + private static final byte[] skullNotFoundTexture = new byte[4096]; + + static { + for(int y = 0; y < 16; ++y) { + for(int x = 0; x < 64; ++x) { + int i = (y << 8) | (x << 2); + byte j = ((x + y) & 1) == 1 ? (byte)255 : 0; + skullNotFoundTexture[i] = (byte)255; + skullNotFoundTexture[i + 1] = j; + skullNotFoundTexture[i + 2] = 0; + skullNotFoundTexture[i + 3] = j; + } + } + } + + private final VFile2 folder; + + private final Map customSkulls = new HashMap<>(64); + + private long lastFlush = 0l; + + public CustomSkullLoader(VFile2 folder) { + this.folder = folder; + } + + private CustomSkullData loadSkullData0(String urlStr) { + byte[] data = WorldsDB.newVFile(folder, urlStr).getAllBytes(); + if(data == null) { + return new CustomSkullData(skullNotFoundTexture); + }else { + return new CustomSkullData(data); + } + } + + public CustomSkullData loadSkullData(String url) { + CustomSkullData sk = customSkulls.get(url); + if(sk == null) { + customSkulls.put(url, sk = loadSkullData0(url)); + }else { + sk.lastHit = EagRuntime.steadyTimeMillis(); + } + return sk; + } + + private static final String hex = "0123456789abcdef"; + + public String installNewSkull(byte[] skullData) { + // set to 16384 to save a full 64x64 skin + if(skullData.length > 4096) { + byte[] tmp = skullData; + skullData = new byte[4096]; + System.arraycopy(tmp, 0, skullData, 0, 4096); + } + SHA1Digest sha = new SHA1Digest(); + sha.update(skullData, 0, skullData.length); + byte[] hash = new byte[20]; + sha.doFinal(hash, 0); + char[] hashText = new char[40]; + for(int i = 0; i < 20; ++i) { + hashText[i << 1] = hex.charAt((hash[i] & 0xF0) >> 4); + hashText[(i << 1) + 1] = hex.charAt(hash[i] & 0x0F); + } + String str = "skin-" + new String(hashText) + ".bmp"; + customSkulls.put(str, new CustomSkullData(skullData)); + WorldsDB.newVFile(folder, str).setAllBytes(skullData); + return str; + } + + public void flushCache() { + long cur = EagRuntime.steadyTimeMillis(); + if(cur - lastFlush > 300000l) { + lastFlush = cur; + Iterator customSkullsItr = customSkulls.values().iterator(); + while(customSkullsItr.hasNext()) { + if(cur - customSkullsItr.next().lastHit > 900000l) { + customSkullsItr.remove(); + } + } + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapePackets.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapePackets.java deleted file mode 100755 index 57d5dfe1..00000000 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapePackets.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2024 lax1dude. All Rights Reserved. - * - * 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. - * - */ - -package net.lax1dude.eaglercraft.v1_8.sp.server.skins; - -import java.io.IOException; - -import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapeCustomEAG; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapePresetEAG; - -public class IntegratedCapePackets { - - public static final int PACKET_MY_CAPE_PRESET = 0x01; - public static final int PACKET_MY_CAPE_CUSTOM = 0x02; - - public static void registerEaglerPlayer(EaglercraftUUID clientUUID, byte[] bs, IntegratedCapeService capeService) throws IOException { - if(bs.length == 0) { - throw new IOException("Zero-length packet recieved"); - } - GameMessagePacket generatedPacket; - int packetType = (int)bs[0] & 0xFF; - switch(packetType) { - case PACKET_MY_CAPE_PRESET: - if(bs.length != 5) { - throw new IOException("Invalid length " + bs.length + " for preset cape packet"); - } - generatedPacket = new SPacketOtherCapePresetEAG(clientUUID.msb, clientUUID.lsb, (bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF)); - break; - case PACKET_MY_CAPE_CUSTOM: - if(bs.length != 1174) { - throw new IOException("Invalid length " + bs.length + " for custom cape packet"); - } - byte[] capePixels = new byte[bs.length - 1]; - System.arraycopy(bs, 1, capePixels, 0, capePixels.length); - generatedPacket = new SPacketOtherCapeCustomEAG(clientUUID.msb, clientUUID.lsb, capePixels); - break; - default: - throw new IOException("Unknown skin packet type: " + packetType); - } - capeService.registerEaglercraftPlayer(clientUUID, generatedPacket); - } - - public static void registerEaglerPlayerFallback(EaglercraftUUID clientUUID, IntegratedCapeService capeService) { - capeService.registerEaglercraftPlayer(clientUUID, new SPacketOtherCapePresetEAG(clientUUID.msb, clientUUID.lsb, 0)); - } - -} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapeService.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapeService.java deleted file mode 100755 index ce2e37cb..00000000 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapeService.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2024 lax1dude. All Rights Reserved. - * - * 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. - * - */ - -package net.lax1dude.eaglercraft.v1_8.sp.server.skins; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapePresetEAG; -import net.minecraft.entity.player.EntityPlayerMP; - -public class IntegratedCapeService { - - public static final Logger logger = LogManager.getLogger("IntegratedCapeService"); - - public static final int masterRateLimitPerPlayer = 250; - - private final Map capesCache = new HashMap<>(); - - public void processLoginPacket(byte[] packetData, EntityPlayerMP sender) { - try { - IntegratedCapePackets.registerEaglerPlayer(sender.getUniqueID(), packetData, this); - } catch (IOException e) { - logger.error("Invalid skin data packet recieved from player {}!", sender.getName()); - logger.error(e); - sender.playerNetServerHandler.kickPlayerFromServer("Invalid skin data packet recieved!"); - } - } - - public void registerEaglercraftPlayer(EaglercraftUUID playerUUID, GameMessagePacket capePacket) { - capesCache.put(playerUUID, capePacket); - } - - public void processGetOtherCape(EaglercraftUUID searchUUID, EntityPlayerMP sender) { - GameMessagePacket maybeCape = capesCache.get(searchUUID); - if(maybeCape == null) { - maybeCape = new SPacketOtherCapePresetEAG(searchUUID.msb, searchUUID.lsb, 0); - } - sender.playerNetServerHandler.sendEaglerMessage(maybeCape); - } - - public void unregisterPlayer(EaglercraftUUID playerUUID) { - synchronized(capesCache) { - capesCache.remove(playerUUID); - } - } -} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedSkinPackets.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedSkinPackets.java deleted file mode 100755 index 500f3c07..00000000 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedSkinPackets.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. - * - * 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. - * - */ - -package net.lax1dude.eaglercraft.v1_8.sp.server.skins; - -import java.io.IOException; - -import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache; - -public class IntegratedSkinPackets { - - public static final int PACKET_MY_SKIN_PRESET = 0x01; - public static final int PACKET_MY_SKIN_CUSTOM = 0x02; - - public static void registerEaglerPlayer(EaglercraftUUID clientUUID, byte[] bs, IntegratedSkinService skinService, - int protocolVers) throws IOException { - if(bs.length == 0) { - throw new IOException("Zero-length packet recieved"); - } - GameMessagePacket generatedPacketV3 = null; - GameMessagePacket generatedPacketV4 = null; - int skinModel = -1; - int packetType = (int)bs[0] & 0xFF; - switch(packetType) { - case PACKET_MY_SKIN_PRESET: - if(bs.length != 5) { - throw new IOException("Invalid length " + bs.length + " for preset skin packet"); - } - generatedPacketV3 = generatedPacketV4 = new SPacketOtherSkinPresetEAG(clientUUID.msb, clientUUID.lsb, - (bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF)); - break; - case PACKET_MY_SKIN_CUSTOM: - if(protocolVers <= 3) { - byte[] pixels = new byte[16384]; - if(bs.length != 2 + pixels.length) { - throw new IOException("Invalid length " + bs.length + " for custom skin packet"); - } - setAlphaForChestV3(pixels); - System.arraycopy(bs, 2, pixels, 0, pixels.length); - generatedPacketV3 = new SPacketOtherSkinCustomV3EAG(clientUUID.msb, clientUUID.lsb, (skinModel = (int)bs[1] & 0xFF), pixels); - }else { - byte[] pixels = new byte[12288]; - if(bs.length != 2 + pixels.length) { - throw new IOException("Invalid length " + bs.length + " for custom skin packet"); - } - setAlphaForChestV4(pixels); - System.arraycopy(bs, 2, pixels, 0, pixels.length); - generatedPacketV4 = new SPacketOtherSkinCustomV4EAG(clientUUID.msb, clientUUID.lsb, (skinModel = (int)bs[1] & 0xFF), pixels); - } - break; - default: - throw new IOException("Unknown skin packet type: " + packetType); - } - skinService.processPacketPlayerSkin(clientUUID, new SkinPacketVersionCache(generatedPacketV3, generatedPacketV4), skinModel); - } - - public static void registerEaglerPlayerFallback(EaglercraftUUID clientUUID, IntegratedSkinService skinService) throws IOException { - int skinModel = (clientUUID.hashCode() & 1) != 0 ? 1 : 0; - skinService.processPacketPlayerSkin(clientUUID, SkinPacketVersionCache.createPreset(clientUUID.msb, clientUUID.lsb, skinModel), skinModel); - } - - public static void setAlphaForChestV3(byte[] skin64x64) { - if(skin64x64.length != 16384) { - throw new IllegalArgumentException("Skin is not 64x64!"); - } - for(int y = 20; y < 32; ++y) { - for(int x = 16; x < 40; ++x) { - skin64x64[(y << 8) | (x << 2)] = (byte)0xFF; - } - } - } - - public static void setAlphaForChestV4(byte[] skin64x64) { - if(skin64x64.length != 12288) { - throw new IllegalArgumentException("Skin is not 64x64!"); - } - for(int y = 20; y < 32; ++y) { - for(int x = 16; x < 40; ++x) { - skin64x64[((y << 6) | x) * 3] |= 0x80; - } - } - } - - public static SPacketOtherSkinPresetEAG makePresetResponse(EaglercraftUUID uuid) { - return new SPacketOtherSkinPresetEAG(uuid.msb, uuid.lsb, (uuid.hashCode() & 1) != 0 ? 1 : 0); - } - - public static byte[] asciiString(String string) { - byte[] str = new byte[string.length()]; - for(int i = 0; i < str.length; ++i) { - str[i] = (byte)string.charAt(i); - } - return str; - } - - public static EaglercraftUUID createEaglerURLSkinUUID(String skinUrl) { - return EaglercraftUUID.nameUUIDFromBytes(asciiString("EaglercraftSkinURL:" + skinUrl)); - } - - public static int getModelId(String modelName) { - return "slim".equalsIgnoreCase(modelName) ? 1 : 0; - } - -} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedSkinService.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedSkinService.java deleted file mode 100755 index 9a62b8de..00000000 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedSkinService.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. - * - * 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. - * - */ - -package net.lax1dude.eaglercraft.v1_8.sp.server.skins; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import net.lax1dude.eaglercraft.v1_8.Base64; -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.lax1dude.eaglercraft.v1_8.crypto.SHA1Digest; -import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2; -import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinPresetEAG; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache; -import net.lax1dude.eaglercraft.v1_8.sp.server.WorldsDB; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.init.Items; -import net.minecraft.item.ItemStack; -import net.minecraft.util.ChatComponentTranslation; -import net.minecraft.util.EnumChatFormatting; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; -import net.minecraft.nbt.NBTTagString; - -public class IntegratedSkinService { - - public static final Logger logger = LogManager.getLogger("IntegratedSkinService"); - - public static final byte[] skullNotFoundTexture = new byte[4096]; - - static { - for(int y = 0; y < 16; ++y) { - for(int x = 0; x < 64; ++x) { - int i = (y << 8) | (x << 2); - byte j = ((x + y) & 1) == 1 ? (byte)255 : 0; - skullNotFoundTexture[i] = (byte)255; - skullNotFoundTexture[i + 1] = j; - skullNotFoundTexture[i + 2] = 0; - skullNotFoundTexture[i + 3] = j; - } - } - } - - public final VFile2 skullsDirectory; - - public final Map playerSkins = new HashMap<>(); - public final Map customSkulls = new HashMap<>(); - - private long lastFlush = 0l; - - public IntegratedSkinService(VFile2 skullsDirectory) { - this.skullsDirectory = skullsDirectory; - } - - public void processLoginPacket(byte[] packetData, EntityPlayerMP sender, int protocolVers) { - try { - IntegratedSkinPackets.registerEaglerPlayer(sender.getUniqueID(), packetData, this, protocolVers); - } catch (IOException e) { - logger.error("Invalid skin data packet recieved from player {}!", sender.getName()); - logger.error(e); - sender.playerNetServerHandler.kickPlayerFromServer("Invalid skin data packet recieved!"); - } - } - - public void processPacketGetOtherSkin(EaglercraftUUID searchUUID, EntityPlayerMP sender) { - SkinPacketVersionCache playerSkin = playerSkins.get(searchUUID); - GameMessagePacket toSend = null; - if(playerSkin != null) { - toSend = playerSkin.get(sender.playerNetServerHandler.getEaglerMessageProtocol()); - }else { - toSend = IntegratedSkinPackets.makePresetResponse(searchUUID); - } - sender.playerNetServerHandler.sendEaglerMessage(toSend); - } - - public void processPacketGetOtherSkin(EaglercraftUUID searchUUID, String urlStr, EntityPlayerMP sender) { - urlStr = urlStr.toLowerCase(); - GameMessagePacket playerSkin; - if(!urlStr.startsWith("eagler://")) { - playerSkin = new SPacketOtherSkinPresetEAG(searchUUID.msb, searchUUID.lsb, 0); - }else { - urlStr = urlStr.substring(9); - if(urlStr.contains(VFile2.pathSeperator)) { - playerSkin = new SPacketOtherSkinPresetEAG(searchUUID.msb, searchUUID.lsb, 0); - }else { - CustomSkullData sk = customSkulls.get(urlStr); - if(sk == null) { - customSkulls.put(urlStr, sk = loadCustomSkull(urlStr)); - }else { - sk.lastHit = EagRuntime.steadyTimeMillis(); - } - playerSkin = sk.getSkinPacket(searchUUID, sender.playerNetServerHandler.getEaglerMessageProtocol()); - } - } - sender.playerNetServerHandler.sendEaglerMessage(playerSkin); - } - - public void processPacketPlayerSkin(EaglercraftUUID clientUUID, SkinPacketVersionCache generatedPacket, int skinModel) { - playerSkins.put(clientUUID, generatedPacket); - } - - public void unregisterPlayer(EaglercraftUUID clientUUID) { - playerSkins.remove(clientUUID); - } - - public void processPacketInstallNewSkin(byte[] skullData, EntityPlayerMP sender) { - if(!sender.canCommandSenderUseCommand(2, "give")) { - ChatComponentTranslation cc = new ChatComponentTranslation("command.skull.nopermission"); - cc.getChatStyle().setColor(EnumChatFormatting.RED); - sender.addChatMessage(cc); - return; - } - String fileName = "eagler://" + installNewSkull(skullData); - NBTTagCompound rootTagCompound = new NBTTagCompound(); - NBTTagCompound ownerTagCompound = new NBTTagCompound(); - ownerTagCompound.setString("Name", "Eagler"); - ownerTagCompound.setString("Id", EaglercraftUUID.nameUUIDFromBytes((("EaglerSkullUUID:" + fileName).getBytes(StandardCharsets.UTF_8))).toString()); - NBTTagCompound propertiesTagCompound = new NBTTagCompound(); - NBTTagList texturesTagList = new NBTTagList(); - NBTTagCompound texturesTagCompound = new NBTTagCompound(); - String texturesProp = "{\"textures\":{\"SKIN\":{\"url\":\"" + fileName + "\",\"metadata\":{\"model\":\"default\"}}}}"; - texturesTagCompound.setString("Value", Base64.encodeBase64String(texturesProp.getBytes(StandardCharsets.UTF_8))); - texturesTagList.appendTag(texturesTagCompound); - propertiesTagCompound.setTag("textures", texturesTagList); - ownerTagCompound.setTag("Properties", propertiesTagCompound); - rootTagCompound.setTag("SkullOwner", ownerTagCompound); - NBTTagCompound displayTagCompound = new NBTTagCompound(); - displayTagCompound.setString("Name", EnumChatFormatting.RESET + "Custom Eaglercraft Skull"); - NBTTagList loreList = new NBTTagList(); - loreList.appendTag(new NBTTagString(EnumChatFormatting.GRAY + (fileName.length() > 24 ? (fileName.substring(0, 22) + "...") : fileName))); - displayTagCompound.setTag("Lore", loreList); - rootTagCompound.setTag("display", displayTagCompound); - ItemStack stack = new ItemStack(Items.skull, 1, 3); - stack.setTagCompound(rootTagCompound); - boolean flag = sender.inventory.addItemStackToInventory(stack); - if (flag) { - sender.worldObj.playSoundAtEntity(sender, "random.pop", 0.2F, - ((sender.getRNG().nextFloat() - sender.getRNG().nextFloat()) * 0.7F + 1.0F) - * 2.0F); - sender.inventoryContainer.detectAndSendChanges(); - } - sender.addChatMessage(new ChatComponentTranslation("command.skull.feedback", fileName)); - } - - private static final String hex = "0123456789abcdef"; - - public String installNewSkull(byte[] skullData) { - // set to 16384 to save a full 64x64 skin - if(skullData.length > 4096) { - byte[] tmp = skullData; - skullData = new byte[4096]; - System.arraycopy(tmp, 0, skullData, 0, 4096); - } - SHA1Digest sha = new SHA1Digest(); - sha.update(skullData, 0, skullData.length); - byte[] hash = new byte[20]; - sha.doFinal(hash, 0); - char[] hashText = new char[40]; - for(int i = 0; i < 20; ++i) { - hashText[i << 1] = hex.charAt((hash[i] & 0xF0) >> 4); - hashText[(i << 1) + 1] = hex.charAt(hash[i] & 0x0F); - } - String str = "skin-" + new String(hashText) + ".bmp"; - customSkulls.put(str, new CustomSkullData(str, skullData)); - WorldsDB.newVFile(skullsDirectory, str).setAllBytes(skullData); - return str; - } - - private CustomSkullData loadCustomSkull(String urlStr) { - byte[] data = WorldsDB.newVFile(skullsDirectory, urlStr).getAllBytes(); - if(data == null) { - return new CustomSkullData(urlStr, skullNotFoundTexture); - }else { - return new CustomSkullData(urlStr, data); - } - } - - public void flushCache() { - long cur = EagRuntime.steadyTimeMillis(); - if(cur - lastFlush > 300000l) { - lastFlush = cur; - Iterator customSkullsItr = customSkulls.values().iterator(); - while(customSkullsItr.hasNext()) { - if(cur - customSkullsItr.next().lastHit > 900000l) { - customSkullsItr.remove(); - } - } - } - } -} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedTexturePackets.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedTexturePackets.java new file mode 100755 index 00000000..154f9684 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedTexturePackets.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.sp.server.skins; + +public class IntegratedTexturePackets { + + public static PlayerTextureData handleTextureData(byte[] skinV1, byte[] capeV1) { + int skinId; + byte[] skinTextureDataV3; + eagler: { + if (skinV1 != null && skinV1.length > 0) { + int packetType = (int)skinV1[0] & 0xFF; + switch (packetType) { + case 0x01: + if (skinV1.length == 5) { + skinId = ((skinV1[1] & 0x7F) << 24) | ((skinV1[2] & 0xFF) << 16) + | ((skinV1[3] & 0xFF) << 8) | (skinV1[4] & 0xFF); + skinTextureDataV3 = null; + break eagler; + } + break; + case 0x02: + if (skinV1.length == 16386) { + skinId = -(Math.min((int) skinV1[1] & 0x7F, 0x7E) | 0x80) - 1; + skinTextureDataV3 = new byte[16384]; + System.arraycopy(skinV1, 2, skinTextureDataV3, 0, 16384); + break eagler; + } + break; + default: + break; + } + } + skinId = 0; + skinTextureDataV3 = null; + } + int capeId; + byte[] capeTextureData; + eagler: { + if (capeV1 != null && capeV1.length > 0) { + int packetType = (int)capeV1[0] & 0xFF; + switch (packetType) { + case 0x01: + if(capeV1.length == 5) { + capeId = ((capeV1[1] & 0x7F) << 24) | ((capeV1[2] & 0xFF) << 16) + | ((capeV1[3] & 0xFF) << 8) | (capeV1[4] & 0xFF); + capeTextureData = null; + break eagler; + } + break; + case 0x02: + if (capeV1.length == 1174) { + capeId = -1; + capeTextureData = new byte[1173]; + System.arraycopy(capeV1, 1, capeTextureData, 0, 1173); + break eagler; + } + break; + default: + break; + } + } + capeId = 0; + capeTextureData = null; + } + return new PlayerTextureData(skinId, skinTextureDataV3, null, capeId, capeTextureData); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedTextureService.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedTextureService.java new file mode 100755 index 00000000..b62ff36a --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedTextureService.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.sp.server.skins; + +import java.nio.charset.StandardCharsets; + +import net.lax1dude.eaglercraft.v1_8.Base64; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapePresetEAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapePresetV5EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinPresetEAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinPresetV5EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherTexturesV5EAG; +import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerPlayerList; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.nbt.NBTTagString; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.EnumChatFormatting; + +public class IntegratedTextureService { + + private final EaglerPlayerList playerList; + private final CustomSkullLoader skullHandler; + + public IntegratedTextureService(EaglerPlayerList playerList, VFile2 file) { + this.playerList = playerList; + this.skullHandler = new CustomSkullLoader(file); + } + + public void handleRequestPlayerSkin(EntityPlayerMP requester, EaglercraftUUID uuid) { + EntityPlayerMP target = playerList.getPlayerByUUID(uuid); + if (target != null && target.textureData != null) { + requester.playerNetServerHandler.sendEaglerMessage(target.textureData.getSkin(uuid.msb, uuid.lsb, + requester.playerNetServerHandler.getEaglerMessageProtocol())); + } else { + requester.playerNetServerHandler.sendEaglerMessage( + new SPacketOtherSkinPresetEAG(uuid.msb, uuid.lsb, (uuid.hashCode() & 1) != 0 ? 1 : 0)); + } + } + + public void handleRequestPlayerCape(EntityPlayerMP requester, EaglercraftUUID uuid) { + EntityPlayerMP target = playerList.getPlayerByUUID(uuid); + if (target != null && target.textureData != null) { + requester.playerNetServerHandler.sendEaglerMessage(target.textureData.getCape(uuid.msb, uuid.lsb, + requester.playerNetServerHandler.getEaglerMessageProtocol())); + } else { + requester.playerNetServerHandler.sendEaglerMessage(new SPacketOtherCapePresetEAG(uuid.msb, uuid.lsb, 0)); + } + } + + public void handleRequestPlayerSkinV5(EntityPlayerMP requester, int requestId, EaglercraftUUID uuid) { + EntityPlayerMP target = playerList.getPlayerByUUID(uuid); + if (target != null && target.textureData != null) { + requester.playerNetServerHandler.sendEaglerMessage(target.textureData.getSkinV5(requestId, + requester.playerNetServerHandler.getEaglerMessageProtocol())); + } else { + requester.playerNetServerHandler.sendEaglerMessage( + new SPacketOtherSkinPresetV5EAG(requestId, (uuid.hashCode() & 1) != 0 ? 1 : 0)); + } + } + + public void handleRequestPlayerCapeV5(EntityPlayerMP requester, int requestId, EaglercraftUUID uuid) { + EntityPlayerMP target = playerList.getPlayerByUUID(uuid); + if (target != null && target.textureData != null) { + requester.playerNetServerHandler.sendEaglerMessage(target.textureData.getCapeV5(requestId, + requester.playerNetServerHandler.getEaglerMessageProtocol())); + } else { + requester.playerNetServerHandler.sendEaglerMessage(new SPacketOtherCapePresetV5EAG(requestId, 0)); + } + } + + public void handleRequestPlayerTexturesV5(EntityPlayerMP requester, int requestId, EaglercraftUUID uuid) { + EntityPlayerMP target = playerList.getPlayerByUUID(uuid); + if (target != null && target.textureData != null) { + requester.playerNetServerHandler.sendEaglerMessage(target.textureData.getTexturesV5(requestId, + requester.playerNetServerHandler.getEaglerMessageProtocol())); + } else { + requester.playerNetServerHandler + .sendEaglerMessage(new SPacketOtherTexturesV5EAG(requestId, 0, null, 0, null)); + } + } + + public void handleRequestSkinByURL(EntityPlayerMP requester, EaglercraftUUID uuid, String url) { + url = url.toLowerCase(); + if (url.startsWith("eagler://")) { + url = url.substring(9); + if (!url.contains(VFile2.pathSeperator)) { + CustomSkullData skull = skullHandler.loadSkullData(url); + if (skull != null) { + requester.playerNetServerHandler.sendEaglerMessage(skull.getSkin(uuid.msb, uuid.lsb, + requester.playerNetServerHandler.getEaglerMessageProtocol())); + return; + } + } + } + requester.playerNetServerHandler.sendEaglerMessage(new SPacketOtherSkinPresetEAG(uuid.msb, uuid.lsb, 0)); + } + + public void handleRequestSkinByURLV5(EntityPlayerMP requester, int requestId, String url) { + url = url.toLowerCase(); + if (url.startsWith("eagler://")) { + url = url.substring(9); + if (!url.contains(VFile2.pathSeperator)) { + CustomSkullData skull = skullHandler.loadSkullData(url); + if (skull != null) { + requester.playerNetServerHandler.sendEaglerMessage(skull.getSkinV5(requestId, + requester.playerNetServerHandler.getEaglerMessageProtocol())); + return; + } + } + } + requester.playerNetServerHandler.sendEaglerMessage(new SPacketOtherSkinPresetV5EAG(requestId, 0)); + } + + public void handleInstallNewSkin(EntityPlayerMP requester, byte[] skullData) { + if (!requester.canCommandSenderUseCommand(2, "give")) { + ChatComponentTranslation cc = new ChatComponentTranslation("command.skull.nopermission"); + cc.getChatStyle().setColor(EnumChatFormatting.RED); + requester.addChatMessage(cc); + return; + } + String fileName = "eagler://" + skullHandler.installNewSkull(skullData); + NBTTagCompound rootTagCompound = new NBTTagCompound(); + NBTTagCompound ownerTagCompound = new NBTTagCompound(); + ownerTagCompound.setString("Name", "Eagler"); + ownerTagCompound.setString("Id", EaglercraftUUID.nameUUIDFromBytes((("EaglerSkullUUID:" + fileName).getBytes(StandardCharsets.UTF_8))).toString()); + NBTTagCompound propertiesTagCompound = new NBTTagCompound(); + NBTTagList texturesTagList = new NBTTagList(); + NBTTagCompound texturesTagCompound = new NBTTagCompound(); + String texturesProp = "{\"textures\":{\"SKIN\":{\"url\":\"" + fileName + "\",\"metadata\":{\"model\":\"default\"}}}}"; + texturesTagCompound.setString("Value", Base64.encodeBase64String(texturesProp.getBytes(StandardCharsets.UTF_8))); + texturesTagList.appendTag(texturesTagCompound); + propertiesTagCompound.setTag("textures", texturesTagList); + ownerTagCompound.setTag("Properties", propertiesTagCompound); + rootTagCompound.setTag("SkullOwner", ownerTagCompound); + NBTTagCompound displayTagCompound = new NBTTagCompound(); + displayTagCompound.setString("Name", EnumChatFormatting.RESET + "Custom Eaglercraft Skull"); + NBTTagList loreList = new NBTTagList(); + loreList.appendTag(new NBTTagString(EnumChatFormatting.GRAY + (fileName.length() > 24 ? (fileName.substring(0, 22) + "...") : fileName))); + displayTagCompound.setTag("Lore", loreList); + rootTagCompound.setTag("display", displayTagCompound); + ItemStack stack = new ItemStack(Items.skull, 1, 3); + stack.setTagCompound(rootTagCompound); + boolean flag = requester.inventory.addItemStackToInventory(stack); + if (flag) { + requester.worldObj.playSoundAtEntity(requester, "random.pop", 0.2F, + ((requester.getRNG().nextFloat() - requester.getRNG().nextFloat()) * 0.7F + 1.0F) + * 2.0F); + requester.inventoryContainer.detectAndSendChanges(); + } + requester.addChatMessage(new ChatComponentTranslation("command.skull.feedback", fileName)); + } + + public void flushCache() { + skullHandler.flushCache(); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/PlayerTextureData.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/PlayerTextureData.java new file mode 100755 index 00000000..36479a24 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/PlayerTextureData.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.sp.server.skins; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapeCustomEAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapeCustomV5EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapePresetEAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapePresetV5EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinCustomV3EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinCustomV4EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinCustomV5EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinPresetEAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinPresetV5EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherTexturesV5EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache; + +public class PlayerTextureData { + + private int skinId; + private byte[] skinTextureDataV3; + private byte[] skinTextureDataV4; + + private int capeId; + private byte[] capeTextureData; + + public PlayerTextureData(int skinId, byte[] skinTextureDataV3, byte[] skinTextureDataV4, int capeId, + byte[] capeTextureData) { + this.skinId = skinId; + this.skinTextureDataV3 = skinTextureDataV3; + this.skinTextureDataV4 = skinTextureDataV4; + this.capeId = capeId; + this.capeTextureData = capeTextureData; + } + + private byte[] getTextureV3() { + if (skinTextureDataV3 != null) { + return skinTextureDataV3; + } else { + return skinTextureDataV3 = SkinPacketVersionCache.convertToV3Raw(skinTextureDataV4); + } + } + + private byte[] getTextureV4() { + if (skinTextureDataV4 != null) { + return skinTextureDataV4; + } else { + return skinTextureDataV4 = SkinPacketVersionCache.convertToV4Raw(skinTextureDataV3); + } + } + + public GameMessagePacket getSkin(long uuidMost, long uuidLeast, GamePluginMessageProtocol protocol) { + switch(protocol) { + case V3: + if (skinId < 0) { + return new SPacketOtherSkinCustomV3EAG(uuidMost, uuidLeast, -skinId + 1, getTextureV3()); + } else { + return new SPacketOtherSkinPresetEAG(uuidMost, uuidLeast, skinId); + } + case V4: + if (skinId < 0) { + return new SPacketOtherSkinCustomV4EAG(uuidMost, uuidLeast, -skinId + 1, getTextureV4()); + } else { + return new SPacketOtherSkinPresetEAG(uuidMost, uuidLeast, skinId); + } + default: + throw new IllegalStateException(); + } + } + + public GameMessagePacket getSkinV5(int requestId, GamePluginMessageProtocol protocol) { + if(protocol.ver >= 5) { + if (skinId < 0) { + return new SPacketOtherSkinCustomV5EAG(requestId, -skinId + 1, getTextureV4()); + } else { + return new SPacketOtherSkinPresetV5EAG(requestId, skinId); + } + }else { + throw new IllegalStateException(); + } + } + + public GameMessagePacket getCape(long uuidMost, long uuidLeast, GamePluginMessageProtocol protocol) { + if(protocol.ver <= 4) { + if (capeId < 0) { + return new SPacketOtherCapeCustomEAG(uuidMost, uuidLeast, capeTextureData); + } else { + return new SPacketOtherCapePresetEAG(uuidMost, uuidLeast, capeId); + } + }else { + throw new IllegalStateException(); + } + } + + public GameMessagePacket getCapeV5(int requestId, GamePluginMessageProtocol protocol) { + if(protocol.ver >= 5) { + if (capeId < 0) { + return new SPacketOtherCapeCustomV5EAG(requestId, capeTextureData); + } else { + return new SPacketOtherCapePresetV5EAG(requestId, capeId); + } + }else { + throw new IllegalStateException(); + } + } + + public GameMessagePacket getTexturesV5(int requestId, GamePluginMessageProtocol protocol) { + if(protocol.ver >= 5) { + return new SPacketOtherTexturesV5EAG(requestId, skinId, skinId < 0 ? getTextureV4() : null, capeId, + capeId < 0 ? capeTextureData : null); + }else { + throw new IllegalStateException(); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/IntegratedServerPlayerNetworkManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/IntegratedServerPlayerNetworkManager.java index fdebb03d..390ddbd6 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/IntegratedServerPlayerNetworkManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/IntegratedServerPlayerNetworkManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * Copyright (c) 2022-2025 lax1dude, ayunami2000. All Rights Reserved. * * 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 @@ -31,6 +31,7 @@ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.netty.ByteBuf; import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; import net.lax1dude.eaglercraft.v1_8.socket.CompressionNotSupportedException; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.message.InjectedMessageController; import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerIntegratedServerWorker; import net.minecraft.network.EnumConnectionState; @@ -53,6 +54,7 @@ public class IntegratedServerPlayerNetworkManager { private int debugPacketCounter = 0; private final List recievedPacketBuffer = new LinkedList<>(); private final boolean enableSendCompression; + protected InjectedMessageController injectedController = null; private boolean firstPacket = true; @@ -70,6 +72,10 @@ public class IntegratedServerPlayerNetworkManager { this.playerChannel = playerChannel; this.enableSendCompression = !SingleplayerServerController.PLAYER_CHANNEL.equals(playerChannel); } + + public void setInjectedMessageController(InjectedMessageController controller) { + injectedController = controller; + } public void connect() { fragmentedPacket.clear(); @@ -160,6 +166,10 @@ public class IntegratedServerPlayerNetworkManager { ++debugPacketCounter; try { + if(injectedController != null && injectedController.handlePacket(fullData, 0)) { + continue; + } + ByteBuf nettyBuffer = Unpooled.buffer(fullData, fullData.length); nettyBuffer.writerIndex(fullData.length); PacketBuffer input = new PacketBuffer(nettyBuffer); @@ -274,6 +284,66 @@ public class IntegratedServerPlayerNetworkManager { } } + public void injectRawFrame(byte[] data) { + if(!isChannelOpen()) { + return; + } + if(enableSendCompression) { + int len = data.length; + if(len > compressionThreshold) { + if(compressedPacketTmp == null || compressedPacketTmp.length < len) { + compressedPacketTmp = new byte[len]; + } + int cmpLen; + try { + cmpLen = EaglerZLIB.deflateFull(data, 0, len, compressedPacketTmp, 0, compressedPacketTmp.length); + }catch(IOException ex) { + logger.error("Failed to compress injected frame!"); + logger.error(ex); + return; + } + byte[] compressedData = new byte[5 + cmpLen]; + compressedData[0] = (byte)2; + compressedData[1] = (byte)((len >>> 24) & 0xFF); + compressedData[2] = (byte)((len >>> 16) & 0xFF); + compressedData[3] = (byte)((len >>> 8) & 0xFF); + compressedData[4] = (byte)(len & 0xFF); + System.arraycopy(compressedPacketTmp, 0, compressedData, 5, cmpLen); + if(compressedData.length > fragmentSize) { + int fragmentSizeN1 = fragmentSize - 1; + for (int j = 1; j < compressedData.length; j += fragmentSizeN1) { + byte[] fragData = new byte[((j + fragmentSizeN1 > (compressedData.length - 1)) ? ((compressedData.length - 1) % fragmentSizeN1) : fragmentSizeN1) + 1]; + System.arraycopy(compressedData, j, fragData, 1, fragData.length - 1); + fragData[0] = (j + fragmentSizeN1 < compressedData.length) ? (byte) 1 : (byte) 2; + ServerPlatformSingleplayer.sendPacket(new IPCPacketData(playerChannel, fragData)); + } + }else { + ServerPlatformSingleplayer.sendPacket(new IPCPacketData(playerChannel, compressedData)); + } + }else { + int fragmentSizeN1 = fragmentSize - 1; + if(len > fragmentSizeN1) { + int idx = 0; + do { + int readLen = len > fragmentSizeN1 ? fragmentSizeN1 : len; + byte[] frag = new byte[readLen + 1]; + System.arraycopy(data, idx, frag, 1, readLen); + idx += readLen; + len -= readLen; + frag[0] = len == 0 ? (byte)0 : (byte)1; + ServerPlatformSingleplayer.sendPacket(new IPCPacketData(playerChannel, frag)); + }while(len > 0); + }else { + byte[] bytes = new byte[len + 1]; + System.arraycopy(data, 0, bytes, 1, len); + ServerPlatformSingleplayer.sendPacket(new IPCPacketData(playerChannel, bytes)); + } + } + }else { + ServerPlatformSingleplayer.sendPacket(new IPCPacketData(playerChannel, data)); + } + } + public void setNetHandler(INetHandler nethandler) { this.nethandler = nethandler; } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerMessageHandler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerMessageHandler.java new file mode 100755 index 00000000..69b09275 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerMessageHandler.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.sp.server.socket.protocol; + +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerMinecraftServer; +import net.minecraft.network.NetHandlerPlayServer; + +public abstract class ServerMessageHandler implements GameMessageHandler { + + protected final NetHandlerPlayServer netHandler; + protected final EaglerMinecraftServer server; + + public ServerMessageHandler(NetHandlerPlayServer netHandler) { + this.netHandler = netHandler; + this.server = (EaglerMinecraftServer)netHandler.serverController; + } + + public static ServerMessageHandler createServerHandler(int version, NetHandlerPlayServer netHandler) { + switch(version) { + case 3: + return new ServerV3MessageHandler(netHandler); + case 4: + return new ServerV4MessageHandler(netHandler); + case 5: + return new ServerV5MessageHandler(netHandler); + default: + throw new UnsupportedOperationException(); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerV3MessageHandler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerV3MessageHandler.java index 40fa2d31..50243d26 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerV3MessageHandler.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerV3MessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 lax1dude. All Rights Reserved. + * Copyright (c) 2024-2025 lax1dude. All Rights Reserved. * * 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 @@ -17,58 +17,57 @@ package net.lax1dude.eaglercraft.v1_8.sp.server.socket.protocol; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.*; -import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerMinecraftServer; import net.lax1dude.eaglercraft.v1_8.sp.server.voice.IntegratedVoiceService; import net.minecraft.network.NetHandlerPlayServer; -public class ServerV3MessageHandler implements GameMessageHandler { - - private final NetHandlerPlayServer netHandler; - private final EaglerMinecraftServer server; +public class ServerV3MessageHandler extends ServerMessageHandler { public ServerV3MessageHandler(NetHandlerPlayServer netHandler) { - this.netHandler = netHandler; - this.server = (EaglerMinecraftServer)netHandler.serverController; + super(netHandler); } public void handleClient(CPacketGetOtherCapeEAG packet) { - server.getCapeService().processGetOtherCape(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity); + server.getTextureService().handleRequestPlayerCape(netHandler.playerEntity, + new EaglercraftUUID(packet.uuidMost, packet.uuidLeast)); } public void handleClient(CPacketGetOtherSkinEAG packet) { - server.getSkinService().processPacketGetOtherSkin(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity); + server.getTextureService().handleRequestPlayerSkin(netHandler.playerEntity, + new EaglercraftUUID(packet.uuidMost, packet.uuidLeast)); } public void handleClient(CPacketGetSkinByURLEAG packet) { - server.getSkinService().processPacketGetOtherSkin(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.url, netHandler.playerEntity); + server.getTextureService().handleRequestSkinByURL(netHandler.playerEntity, + new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.url); } public void handleClient(CPacketInstallSkinSPEAG packet) { - server.getSkinService().processPacketInstallNewSkin(packet.customSkin, netHandler.playerEntity); + server.getTextureService().handleInstallNewSkin(netHandler.playerEntity, packet.customSkin); } public void handleClient(CPacketVoiceSignalConnectEAG packet) { IntegratedVoiceService voiceSvc = server.getVoiceService(); - if(voiceSvc != null) { + if (voiceSvc != null) { voiceSvc.handleVoiceSignalPacketTypeConnect(netHandler.playerEntity); } } public void handleClient(CPacketVoiceSignalDescEAG packet) { IntegratedVoiceService voiceSvc = server.getVoiceService(); - if(voiceSvc != null) { - voiceSvc.handleVoiceSignalPacketTypeDesc(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.desc, netHandler.playerEntity); + if (voiceSvc != null) { + voiceSvc.handleVoiceSignalPacketTypeDesc(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), + packet.desc, netHandler.playerEntity); } } public void handleClient(CPacketVoiceSignalDisconnectV3EAG packet) { IntegratedVoiceService voiceSvc = server.getVoiceService(); - if(voiceSvc != null) { - if(packet.isPeerType) { - voiceSvc.handleVoiceSignalPacketTypeDisconnectPeer(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity); - }else { + if (voiceSvc != null) { + if (packet.isPeerType) { + voiceSvc.handleVoiceSignalPacketTypeDisconnectPeer( + new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity); + } else { voiceSvc.handleVoiceSignalPacketTypeDisconnect(netHandler.playerEntity); } } @@ -76,15 +75,17 @@ public class ServerV3MessageHandler implements GameMessageHandler { public void handleClient(CPacketVoiceSignalICEEAG packet) { IntegratedVoiceService voiceSvc = server.getVoiceService(); - if(voiceSvc != null) { - voiceSvc.handleVoiceSignalPacketTypeICE(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.ice, netHandler.playerEntity); + if (voiceSvc != null) { + voiceSvc.handleVoiceSignalPacketTypeICE(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.ice, + netHandler.playerEntity); } } public void handleClient(CPacketVoiceSignalRequestEAG packet) { IntegratedVoiceService voiceSvc = server.getVoiceService(); - if(voiceSvc != null) { - voiceSvc.handleVoiceSignalPacketTypeRequest(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity); + if (voiceSvc != null) { + voiceSvc.handleVoiceSignalPacketTypeRequest(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), + netHandler.playerEntity); } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerV4MessageHandler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerV4MessageHandler.java index 4c0fdef7..5161cad8 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerV4MessageHandler.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerV4MessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 lax1dude. All Rights Reserved. + * Copyright (c) 2024-2025 lax1dude. All Rights Reserved. * * 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 @@ -17,52 +17,21 @@ package net.lax1dude.eaglercraft.v1_8.sp.server.socket.protocol; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.WrongPacketException; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.*; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherPlayerClientUUIDV4EAG; -import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerMinecraftServer; import net.lax1dude.eaglercraft.v1_8.sp.server.voice.IntegratedVoiceService; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.network.NetHandlerPlayServer; -public class ServerV4MessageHandler implements GameMessageHandler { - - private final NetHandlerPlayServer netHandler; - private final EaglerMinecraftServer server; +public class ServerV4MessageHandler extends ServerV3MessageHandler { public ServerV4MessageHandler(NetHandlerPlayServer netHandler) { - this.netHandler = netHandler; - this.server = (EaglerMinecraftServer)netHandler.serverController; + super(netHandler); } - public void handleClient(CPacketGetOtherCapeEAG packet) { - server.getCapeService().processGetOtherCape(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity); - } - - public void handleClient(CPacketGetOtherSkinEAG packet) { - server.getSkinService().processPacketGetOtherSkin(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity); - } - - public void handleClient(CPacketGetSkinByURLEAG packet) { - server.getSkinService().processPacketGetOtherSkin(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.url, netHandler.playerEntity); - } - - public void handleClient(CPacketInstallSkinSPEAG packet) { - server.getSkinService().processPacketInstallNewSkin(packet.customSkin, netHandler.playerEntity); - } - - public void handleClient(CPacketVoiceSignalConnectEAG packet) { - IntegratedVoiceService voiceSvc = server.getVoiceService(); - if(voiceSvc != null) { - voiceSvc.handleVoiceSignalPacketTypeConnect(netHandler.playerEntity); - } - } - - public void handleClient(CPacketVoiceSignalDescEAG packet) { - IntegratedVoiceService voiceSvc = server.getVoiceService(); - if(voiceSvc != null) { - voiceSvc.handleVoiceSignalPacketTypeDesc(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.desc, netHandler.playerEntity); - } + public void handleClient(CPacketVoiceSignalDisconnectV3EAG packet) { + throw new WrongPacketException(); } public void handleClient(CPacketVoiceSignalDisconnectV4EAG packet) { @@ -74,30 +43,19 @@ public class ServerV4MessageHandler implements GameMessageHandler { public void handleClient(CPacketVoiceSignalDisconnectPeerV4EAG packet) { IntegratedVoiceService voiceSvc = server.getVoiceService(); - if(voiceSvc != null) { - voiceSvc.handleVoiceSignalPacketTypeDisconnectPeer(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity); - } - } - - public void handleClient(CPacketVoiceSignalICEEAG packet) { - IntegratedVoiceService voiceSvc = server.getVoiceService(); - if(voiceSvc != null) { - voiceSvc.handleVoiceSignalPacketTypeICE(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.ice, netHandler.playerEntity); - } - } - - public void handleClient(CPacketVoiceSignalRequestEAG packet) { - IntegratedVoiceService voiceSvc = server.getVoiceService(); - if(voiceSvc != null) { - voiceSvc.handleVoiceSignalPacketTypeRequest(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity); + if (voiceSvc != null) { + voiceSvc.handleVoiceSignalPacketTypeDisconnectPeer(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), + netHandler.playerEntity); } } public void handleClient(CPacketGetOtherClientUUIDV4EAG packet) { - EntityPlayerMP player = server.getConfigurationManager().getPlayerByUUID(new EaglercraftUUID(packet.playerUUIDMost, packet.playerUUIDLeast)); - if(player != null && player.clientBrandUUID != null) { - netHandler.sendEaglerMessage(new SPacketOtherPlayerClientUUIDV4EAG(packet.requestId, player.clientBrandUUID.msb, player.clientBrandUUID.lsb)); - }else { + EntityPlayerMP player = server.getConfigurationManager() + .getPlayerByUUID(new EaglercraftUUID(packet.playerUUIDMost, packet.playerUUIDLeast)); + if (player != null && player.clientBrandUUID != null) { + netHandler.sendEaglerMessage(new SPacketOtherPlayerClientUUIDV4EAG(packet.requestId, + player.clientBrandUUID.msb, player.clientBrandUUID.lsb)); + } else { netHandler.sendEaglerMessage(new SPacketOtherPlayerClientUUIDV4EAG(packet.requestId, 0l, 0l)); } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerV5MessageHandler.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerV5MessageHandler.java new file mode 100755 index 00000000..6f00ef9a --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/protocol/ServerV5MessageHandler.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024-2025 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.sp.server.socket.protocol; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.WrongPacketException; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.*; +import net.minecraft.network.NetHandlerPlayServer; + +public class ServerV5MessageHandler extends ServerV4MessageHandler { + + public ServerV5MessageHandler(NetHandlerPlayServer netHandler) { + super(netHandler); + } + + public void handleClient(CPacketGetOtherCapeEAG packet) { + throw new WrongPacketException(); + } + + public void handleClient(CPacketGetOtherSkinEAG packet) { + throw new WrongPacketException(); + } + + public void handleClient(CPacketGetSkinByURLEAG packet) { + throw new WrongPacketException(); + } + + public void handleClient(CPacketGetOtherCapeV5EAG packet) { + server.getTextureService().handleRequestPlayerCapeV5(netHandler.playerEntity, packet.requestId, + new EaglercraftUUID(packet.uuidMost, packet.uuidLeast)); + } + + public void handleClient(CPacketGetOtherSkinV5EAG packet) { + server.getTextureService().handleRequestPlayerSkinV5(netHandler.playerEntity, packet.requestId, + new EaglercraftUUID(packet.uuidMost, packet.uuidLeast)); + } + + public void handleClient(CPacketGetSkinByURLV5EAG packet) { + server.getTextureService().handleRequestSkinByURLV5(netHandler.playerEntity, packet.requestId, packet.url); + } + + public void handleClient(CPacketGetOtherTexturesV5EAG packet) { + server.getTextureService().handleRequestPlayerTexturesV5(netHandler.playerEntity, packet.requestId, + new EaglercraftUUID(packet.uuidMost, packet.uuidLeast)); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/voice/IntegratedVoiceService.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/voice/IntegratedVoiceService.java index db87352c..c4a95c97 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/voice/IntegratedVoiceService.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/voice/IntegratedVoiceService.java @@ -32,6 +32,7 @@ import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*; import net.lax1dude.eaglercraft.v1_8.voice.ExpiringSet; import net.minecraft.entity.player.EntityPlayerMP; +//TODO: Rewrite to be more like EaglerXServer public class IntegratedVoiceService { public static final Logger logger = LogManager.getLogger("IntegratedVoiceService"); @@ -151,22 +152,33 @@ public class IntegratedVoiceService { } GameMessagePacket v3p = null; GameMessagePacket v4p = null; - for(EntityPlayerMP conn : voicePlayers.values()) { - if(conn.playerNetServerHandler.getEaglerMessageProtocol().ver <= 3) { - conn.playerNetServerHandler.sendEaglerMessage(v3p == null ? (v3p = new SPacketVoiceSignalConnectV3EAG(senderUuid.msb, senderUuid.lsb, true, false)) : v3p); - } else { - conn.playerNetServerHandler.sendEaglerMessage(v4p == null ? (v4p = new SPacketVoiceSignalConnectAnnounceV4EAG(senderUuid.msb, senderUuid.lsb)) : v4p); - } - } Collection userDatas = new ArrayList<>(voicePlayers.size()); - for(EntityPlayerMP player : voicePlayers.values()) { - EaglercraftUUID uuid = player.getUniqueID(); - userDatas.add(new SPacketVoiceSignalGlobalEAG.UserData(uuid.msb, uuid.lsb, player.getName())); + for(EntityPlayerMP conn : voicePlayers.values()) { + EaglercraftUUID otherUuid = conn.getUniqueID(); + if(conn != sender) { + if(conn.playerNetServerHandler.getEaglerMessageProtocol().ver <= 3) { + conn.playerNetServerHandler.sendEaglerMessage(v3p == null ? (v3p = new SPacketVoiceSignalConnectV3EAG(senderUuid.msb, senderUuid.lsb, true, false)) : v3p); + } else { + conn.playerNetServerHandler.sendEaglerMessage(v4p == null ? (v4p = new SPacketVoiceSignalConnectAnnounceV4EAG(senderUuid.msb, senderUuid.lsb)) : v4p); + } + } + userDatas.add(new SPacketVoiceSignalGlobalEAG.UserData(otherUuid.msb, otherUuid.lsb, conn.getName())); } SPacketVoiceSignalGlobalEAG packetToBroadcast = new SPacketVoiceSignalGlobalEAG(userDatas); for (EntityPlayerMP userCon : voicePlayers.values()) { userCon.playerNetServerHandler.sendEaglerMessage(packetToBroadcast); } + boolean selfV3 = sender.playerNetServerHandler.getEaglerMessageProtocol().ver <= 3; + for(EntityPlayerMP conn : voicePlayers.values()) { + EaglercraftUUID otherUuid = conn.getUniqueID(); + if(conn != sender) { + if(selfV3) { + sender.playerNetServerHandler.sendEaglerMessage(new SPacketVoiceSignalConnectV3EAG(otherUuid.msb, otherUuid.lsb, true, false)); + }else { + sender.playerNetServerHandler.sendEaglerMessage(new SPacketVoiceSignalConnectAnnounceV4EAG(otherUuid.msb, otherUuid.lsb)); + } + } + } } public void handleVoiceSignalPacketTypeICE(EaglercraftUUID player, byte[] str, EntityPlayerMP sender) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/socket/ClientIntegratedServerNetworkManager.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/socket/ClientIntegratedServerNetworkManager.java index 250b723e..79584de0 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/socket/ClientIntegratedServerNetworkManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/socket/ClientIntegratedServerNetworkManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024 lax1dude, ayunami2000. All Rights Reserved. + * Copyright (c) 2023-2025 lax1dude, ayunami2000. All Rights Reserved. * * 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 @@ -78,6 +78,10 @@ public class ClientIntegratedServerNetworkManager extends EaglercraftNetworkMana byte[] next = recievedPacketBuffer.remove(0); ++debugPacketCounter; try { + if(injectedController != null && injectedController.handlePacket(next, 0)) { + continue; + } + ByteBuf nettyBuffer = Unpooled.buffer(next, next.length); nettyBuffer.writerIndex(next.length); PacketBuffer input = new PacketBuffer(nettyBuffer); @@ -168,4 +172,14 @@ public class ClientIntegratedServerNetworkManager extends EaglercraftNetworkMana public void clearRecieveQueue() { recievedPacketBuffer.clear(); } + + @Override + public void injectRawFrame(byte[] data) { + if(!isChannelOpen()) { + logger.error("Frame was injected on a closed connection"); + return; + } + ClientPlatformSingleplayer.sendPacket(new IPCPacketData(address, data)); + } + } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/socket/NetHandlerSingleplayerLogin.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/socket/NetHandlerSingleplayerLogin.java index c0d623e1..46913ae3 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/socket/NetHandlerSingleplayerLogin.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/socket/NetHandlerSingleplayerLogin.java @@ -20,9 +20,7 @@ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageConstants; import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.client.GameProtocolMessageController; import net.lax1dude.eaglercraft.v1_8.update.UpdateService; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiDisconnected; @@ -73,15 +71,14 @@ public class NetHandlerSingleplayerLogin implements INetHandlerLoginClient { return; } logger.info("Server is using protocol: {}", p); - NetHandlerPlayClient netHandler = new NetHandlerPlayClient(this.mc, this.previousGuiScreen, this.networkManager, var1.getProfile()); - netHandler.setEaglerMessageController( - new GameProtocolMessageController(mp, GamePluginMessageConstants.CLIENT_TO_SERVER, - GameProtocolMessageController.createClientHandler(p, netHandler), - (ch, msg) -> netHandler.addToSendQueue(new C17PacketCustomPayload(ch, msg)))); + this.networkManager.setLANInfo(p); + NetHandlerPlayClient netHandler = new NetHandlerPlayClient(this.mc, this.previousGuiScreen, this.networkManager, + var1.getProfile(), mp); this.networkManager.setNetHandler(netHandler); byte[] b = UpdateService.getClientSignatureData(); if(b != null) { - this.networkManager.sendPacket(new C17PacketCustomPayload("EAG|MyUpdCert-1.8", new PacketBuffer(Unpooled.buffer(b, b.length).writerIndex(b.length)))); + this.networkManager.sendPacket(new C17PacketCustomPayload("EAG|MyUpdCert-1.8", + new PacketBuffer(Unpooled.buffer(b, b.length).writerIndex(b.length)))); } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceClientController.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceClientController.java index 6e8559e9..6f255a30 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceClientController.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceClientController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * Copyright (c) 2022-2025 lax1dude, ayunami2000. All Rights Reserved. * * 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 @@ -18,13 +18,10 @@ package net.lax1dude.eaglercraft.v1_8.voice; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.function.Consumer; -import java.util.stream.Collectors; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; @@ -32,32 +29,21 @@ import net.lax1dude.eaglercraft.v1_8.Keyboard; import net.lax1dude.eaglercraft.v1_8.internal.PlatformVoiceClient; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; -import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.*; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketVoiceSignalGlobalEAG; -import net.lax1dude.eaglercraft.v1_8.sp.lan.LANServerController; import net.minecraft.client.Minecraft; -import net.minecraft.entity.player.EntityPlayer; public class VoiceClientController { - public static final String SIGNAL_CHANNEL = "EAG|Voice-1.8"; - static final Logger logger = LogManager.getLogger("VoiceClientController"); private static boolean clientSupport = false; private static boolean serverSupport = false; private static Consumer packetSendCallback = null; private static int protocolVersion = -1; - private static EnumVoiceChannelType voiceChannel = EnumVoiceChannelType.NONE; - private static final HashSet nearbyPlayers = new HashSet<>(); - private static final ExpiringSet recentlyNearbyPlayers = new ExpiringSet<>(5000, uuid -> { - if (!nearbyPlayers.contains(uuid)) { - PlatformVoiceClient.signalDisconnect(uuid, false); - } - }); - private static final Map uuidToNameLookup = new HashMap<>(256); + private static VoiceClientInstance voiceClient = null; + + static EnumVoiceChannelType lastVoiceChannel = EnumVoiceChannelType.NONE; public static boolean isSupported() { return isClientSupported() && isServerSupported(); @@ -78,202 +64,118 @@ public class VoiceClientController { } public static void initializeVoiceClient(Consumer signalSendCallbackIn, int proto) { + if (!isClientSupported()) return; packetSendCallback = signalSendCallbackIn; protocolVersion = proto; - uuidToNameLookup.clear(); - if (getVoiceChannel() != EnumVoiceChannelType.NONE) sendInitialVoice(); - } - - public static void handleVoiceSignalPacketTypeGlobal(EaglercraftUUID[] voicePlayers, String[] voiceNames) { - uuidToNameLookup.clear(); - for (int i = 0; i < voicePlayers.length; i++) { - if(voiceNames != null) { - uuidToNameLookup.put(voicePlayers[i], voiceNames[i]); - } - sendPacketRequestIfNeeded(voicePlayers[i]); + handleRelease(); + if (signalSendCallbackIn != null && serverSupport) { + voiceClient = new VoiceClientInstance(proto, signalSendCallbackIn); + voiceClient.initialize(lastVoiceChannel); } } + private static void handleRelease() { + if (voiceClient != null) { + voiceClient.release(); + voiceClient = null; + } + speakingSet.clear(); + activateVoice(false); + } + public static void handleVoiceSignalPacketTypeGlobalNew(Collection voicePlayers) { - boolean isGlobal = voiceChannel == EnumVoiceChannelType.GLOBAL; - uuidToNameLookup.clear(); - for (SPacketVoiceSignalGlobalEAG.UserData player : voicePlayers) { - EaglercraftUUID uuid = new EaglercraftUUID(player.uuidMost, player.uuidLeast); - if(player.username != null) { - uuidToNameLookup.put(uuid, player.username); - } - if (isGlobal) { - sendPacketRequestIfNeeded(uuid); - } + if (voiceClient != null) { + voiceClient.handleVoiceSignalPacketTypeGlobal(voicePlayers); } } public static void handleServerDisconnect() { - if(!isClientSupported()) return; + if (!isClientSupported()) return; serverSupport = false; - uuidToNameLookup.clear(); - for (EaglercraftUUID uuid : nearbyPlayers) { - PlatformVoiceClient.signalDisconnect(uuid, false); - } - for (EaglercraftUUID uuid : recentlyNearbyPlayers) { - PlatformVoiceClient.signalDisconnect(uuid, false); - } - nearbyPlayers.clear(); - recentlyNearbyPlayers.clear(); - Set antiConcurrentModificationUUIDs = new HashSet<>(listeningSet); - for (EaglercraftUUID uuid : antiConcurrentModificationUUIDs) { - PlatformVoiceClient.signalDisconnect(uuid, false); - } - activateVoice(false); + packetSendCallback = null; + protocolVersion = -1; + handleRelease(); } public static void handleVoiceSignalPacketTypeAllowed(boolean voiceAvailableStat, String[] servs) { - serverSupport = voiceAvailableStat; - PlatformVoiceClient.setICEServers(servs); - if(isSupported()) { - EnumVoiceChannelType ch = getVoiceChannel(); - setVoiceChannel(EnumVoiceChannelType.NONE); - setVoiceChannel(ch); + if(packetSendCallback != null) { + handleRelease(); + PlatformVoiceClient.setICEServers(servs); + if (serverSupport != voiceAvailableStat) { + serverSupport = voiceAvailableStat; + if (voiceAvailableStat) { + voiceClient = new VoiceClientInstance(protocolVersion, packetSendCallback); + voiceClient.initialize(lastVoiceChannel); + } + } } } public static void handleVoiceSignalPacketTypeConnect(EaglercraftUUID user, boolean offer) { - if (voiceChannel != EnumVoiceChannelType.NONE) PlatformVoiceClient.signalConnect(user, offer); + if (voiceClient != null) { + voiceClient.handleVoiceSignalPacketTypeConnect(user, offer); + } } public static void handleVoiceSignalPacketTypeConnectAnnounce(EaglercraftUUID user) { - if (voiceChannel != EnumVoiceChannelType.NONE && (voiceChannel == EnumVoiceChannelType.GLOBAL || listeningSet.contains(user))) sendPacketRequest(user); + if (voiceClient != null) { + voiceClient.handleVoiceSignalPacketTypeConnectAnnounce(user); + } } public static void handleVoiceSignalPacketTypeDisconnect(EaglercraftUUID user) { - if (voiceChannel != EnumVoiceChannelType.NONE) PlatformVoiceClient.signalDisconnect(user, true); + if (voiceClient != null) { + voiceClient.handleVoiceSignalPacketTypeDisconnect(user); + } } public static void handleVoiceSignalPacketTypeICECandidate(EaglercraftUUID user, String ice) { - if (voiceChannel != EnumVoiceChannelType.NONE) PlatformVoiceClient.signalICECandidate(user, ice); + if (voiceClient != null) { + voiceClient.handleVoiceSignalPacketTypeICECandidate(user, ice); + } } public static void handleVoiceSignalPacketTypeDescription(EaglercraftUUID user, String desc) { - if (voiceChannel != EnumVoiceChannelType.NONE) PlatformVoiceClient.signalDescription(user, desc); + if (voiceClient != null) { + voiceClient.handleVoiceSignalPacketTypeDescription(user, desc); + } } - public static void tickVoiceClient(Minecraft mc) { - if(!isClientSupported()) return; - recentlyNearbyPlayers.checkForExpirations(); - speakingSet.clear(); - PlatformVoiceClient.tickVoiceClient(); - - if (getVoiceChannel() != EnumVoiceChannelType.NONE && (getVoiceStatus() == EnumVoiceChannelStatus.CONNECTING || getVoiceStatus() == EnumVoiceChannelStatus.CONNECTED)) { - activateVoice((mc.currentScreen == null || !mc.currentScreen.blockPTTKey()) && Keyboard.isKeyDown(mc.gameSettings.voicePTTKey)); - - if(mc.isSingleplayer() && !LANServerController.isHostingLAN()) { - setVoiceChannel(EnumVoiceChannelType.NONE); - return; - } - - if (mc.theWorld != null && mc.thePlayer != null) { - HashSet seenPlayers = new HashSet<>(); - for (EntityPlayer player : mc.theWorld.playerEntities) { - if (player == mc.thePlayer) continue; - if (getVoiceChannel() == EnumVoiceChannelType.PROXIMITY) updateVoicePosition(player.getUniqueID(), player.posX, player.posY + player.getEyeHeight(), player.posZ); - int prox = 22; - // cube - if (Math.abs(mc.thePlayer.posX - player.posX) <= prox && Math.abs(mc.thePlayer.posY - player.posY) <= prox && Math.abs(mc.thePlayer.posZ - player.posZ) <= prox) { - if (!uuidToNameLookup.containsKey(player.getUniqueID())) { - uuidToNameLookup.put(player.getUniqueID(), player.getName()); - } - if (addNearbyPlayer(player.getUniqueID())) { - seenPlayers.add(player.getUniqueID()); - } - } - } - cleanupNearbyPlayers(seenPlayers); + public static void tickVoiceClient() { + if (voiceClient != null) { + voiceClient.tickVoiceClient(); + Minecraft mc = Minecraft.getMinecraft(); + if (voiceClient.getVoiceChannel() != EnumVoiceChannelType.NONE) { + activateVoice((mc.currentScreen == null || !mc.currentScreen.blockPTTKey()) + && Keyboard.isKeyDown(mc.gameSettings.voicePTTKey)); + } else { + activateVoice(false); } + speakingSet.clear(); + PlatformVoiceClient.tickVoiceClient(); } } - public static final boolean addNearbyPlayer(EaglercraftUUID uuid) { - recentlyNearbyPlayers.remove(uuid); - if (nearbyPlayers.add(uuid)) { - sendPacketRequestIfNeeded(uuid); - return true; - } - return false; - } - - public static final void removeNearbyPlayer(EaglercraftUUID uuid) { - if (nearbyPlayers.remove(uuid)) { - if (getVoiceStatus() == EnumVoiceChannelStatus.DISCONNECTED || getVoiceStatus() == EnumVoiceChannelStatus.UNAVAILABLE) return; - if (voiceChannel == EnumVoiceChannelType.PROXIMITY) recentlyNearbyPlayers.add(uuid); - } - } - - public static final void cleanupNearbyPlayers(HashSet existingPlayers) { - nearbyPlayers.stream().filter(ud -> !existingPlayers.contains(ud)).collect(Collectors.toSet()).forEach(VoiceClientController::removeNearbyPlayer); - } - - public static final void updateVoicePosition(EaglercraftUUID uuid, double x, double y, double z) { - PlatformVoiceClient.updateVoicePosition(uuid, x, y, z); - } - public static void setVoiceChannel(EnumVoiceChannelType channel) { - if (voiceChannel == channel) return; - if (channel != EnumVoiceChannelType.NONE) PlatformVoiceClient.initializeDevices(); - if (channel == EnumVoiceChannelType.NONE) { - for (EaglercraftUUID uuid : nearbyPlayers) { - PlatformVoiceClient.signalDisconnect(uuid, false); - } - for (EaglercraftUUID uuid : recentlyNearbyPlayers) { - PlatformVoiceClient.signalDisconnect(uuid, false); - } - nearbyPlayers.clear(); - recentlyNearbyPlayers.clear(); - Set antiConcurrentModificationUUIDs = new HashSet<>(listeningSet); - for (EaglercraftUUID uuid : antiConcurrentModificationUUIDs) { - PlatformVoiceClient.signalDisconnect(uuid, false); - } - sendPacketDisconnect(); - activateVoice(false); - } else if (voiceChannel == EnumVoiceChannelType.PROXIMITY) { - for (EaglercraftUUID uuid : nearbyPlayers) { - PlatformVoiceClient.signalDisconnect(uuid, false); - } - for (EaglercraftUUID uuid : recentlyNearbyPlayers) { - PlatformVoiceClient.signalDisconnect(uuid, false); - } - nearbyPlayers.clear(); - recentlyNearbyPlayers.clear(); - sendPacketDisconnect(); - } else if(voiceChannel == EnumVoiceChannelType.GLOBAL) { - Set antiConcurrentModificationUUIDs = new HashSet<>(listeningSet); - antiConcurrentModificationUUIDs.removeAll(nearbyPlayers); - antiConcurrentModificationUUIDs.removeAll(recentlyNearbyPlayers); - for (EaglercraftUUID uuid : antiConcurrentModificationUUIDs) { - PlatformVoiceClient.signalDisconnect(uuid, false); - } - sendPacketDisconnect(); - } - voiceChannel = channel; - if (channel != EnumVoiceChannelType.NONE) { - sendInitialVoice(); - } - } - - public static void sendInitialVoice() { - sendPacketConnect(); - for (EaglercraftUUID uuid : nearbyPlayers) { - sendPacketRequest(uuid); + if (voiceClient != null) { + voiceClient.setVoiceChannel(channel); } } public static EnumVoiceChannelType getVoiceChannel() { - return voiceChannel; + if (voiceClient != null) { + return voiceClient.getVoiceChannel(); + } else { + return EnumVoiceChannelType.NONE; + } } public static EnumVoiceChannelStatus getVoiceStatus() { - return (!isClientSupported() || !isServerSupported()) ? EnumVoiceChannelStatus.UNAVAILABLE : - (PlatformVoiceClient.getReadyState() != EnumVoiceChannelReadyState.DEVICE_INITIALIZED ? - EnumVoiceChannelStatus.CONNECTING : EnumVoiceChannelStatus.CONNECTED); + if (voiceClient != null) { + return voiceClient.getVoiceStatus(); + } else { + return EnumVoiceChannelStatus.UNAVAILABLE; + } } private static boolean talkStatus = false; @@ -350,60 +252,29 @@ public class VoiceClientController { } public static String getVoiceUsername(EaglercraftUUID uuid) { - if(uuid == null) { - return "null"; + if (voiceClient != null) { + return voiceClient.getVoiceUsername(uuid); + } else { + return uuid.toString(); } - String ret = uuidToNameLookup.get(uuid); - return ret == null ? uuid.toString() : ret; } public static void sendPacketICE(EaglercraftUUID peerId, String candidate) { - if(packetSendCallback != null) { - packetSendCallback.accept(new CPacketVoiceSignalICEEAG(peerId.msb, peerId.lsb, candidate)); + if (voiceClient != null) { + voiceClient.sendPacketICE(peerId, candidate); } } public static void sendPacketDesc(EaglercraftUUID peerId, String desc) { - if(packetSendCallback != null) { - packetSendCallback.accept(new CPacketVoiceSignalDescEAG(peerId.msb, peerId.lsb, desc)); - } - } - - public static void sendPacketDisconnect() { - if(packetSendCallback != null) { - if(protocolVersion <= 3) { - packetSendCallback.accept(new CPacketVoiceSignalDisconnectV3EAG()); - }else { - packetSendCallback.accept(new CPacketVoiceSignalDisconnectV4EAG()); - } + if (voiceClient != null) { + voiceClient.sendPacketDesc(peerId, desc); } } public static void sendPacketDisconnectPeer(EaglercraftUUID peerId) { - if(packetSendCallback != null) { - if(protocolVersion <= 3) { - packetSendCallback.accept(new CPacketVoiceSignalDisconnectV3EAG(true, peerId.msb, peerId.lsb)); - }else { - packetSendCallback.accept(new CPacketVoiceSignalDisconnectPeerV4EAG(peerId.msb, peerId.lsb)); - } + if (voiceClient != null) { + voiceClient.sendPacketDisconnectPeer(peerId); } } - public static void sendPacketConnect() { - if(packetSendCallback != null) { - packetSendCallback.accept(new CPacketVoiceSignalConnectEAG()); - } - } - - public static void sendPacketRequest(EaglercraftUUID peerId) { - if(packetSendCallback != null) { - packetSendCallback.accept(new CPacketVoiceSignalRequestEAG(peerId.msb, peerId.lsb)); - } - } - - private static void sendPacketRequestIfNeeded(EaglercraftUUID uuid) { - if (getVoiceStatus() == EnumVoiceChannelStatus.DISCONNECTED || getVoiceStatus() == EnumVoiceChannelStatus.UNAVAILABLE) return; - if(uuid.equals(EaglerProfile.getPlayerUUID())) return; - if (!getVoiceListening().contains(uuid)) sendPacketRequest(uuid); - } } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceClientInstance.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceClientInstance.java new file mode 100755 index 00000000..3eaaacd8 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceClientInstance.java @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2025 lax1dude, ayunami2000. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.voice; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformVoiceClient; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketVoiceSignalConnectEAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketVoiceSignalDescEAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketVoiceSignalDisconnectPeerV4EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketVoiceSignalDisconnectV3EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketVoiceSignalDisconnectV4EAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketVoiceSignalICEEAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketVoiceSignalRequestEAG; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketVoiceSignalGlobalEAG; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; + +public class VoiceClientInstance { + + private final int protocolVers; + private final Consumer packetSendCallback; + private final Map voicePlayers = new HashMap<>(32); + private EnumVoiceChannelType voiceChannel = EnumVoiceChannelType.NONE; + private long lastUpdate = 0l; + + protected VoiceClientInstance(int protocolVers, Consumer packetSendCallback) { + this.protocolVers = protocolVers; + this.packetSendCallback = packetSendCallback; + } + + public void initialize(EnumVoiceChannelType voiceChannel) { + setVoiceChannel(voiceChannel); + } + + public EnumVoiceChannelType getVoiceChannel() { + return voiceChannel; + } + + public void setVoiceChannel(EnumVoiceChannelType channel) { + VoiceClientController.lastVoiceChannel = channel; + if (voiceChannel == channel) return; + if (channel != EnumVoiceChannelType.NONE) { + PlatformVoiceClient.initializeDevices(); + } + if (channel == EnumVoiceChannelType.NONE) { + release(); + packetSendCallback.accept(new CPacketVoiceSignalDisconnectV4EAG()); + } else { + if (voiceChannel == EnumVoiceChannelType.PROXIMITY && channel == EnumVoiceChannelType.GLOBAL) { + for (VoicePlayerState state : voicePlayers.values()) { + if(!state.nearby) { + state.tryRequest(EagRuntime.steadyTimeMillis()); + } + PlatformVoiceClient.makePeerGlobal(state.uuid); + } + } else if (voiceChannel == EnumVoiceChannelType.GLOBAL && channel == EnumVoiceChannelType.PROXIMITY) { + for (VoicePlayerState state : new ArrayList<>(voicePlayers.values())) { + recheckNearby(state); + if(!state.nearby) { + PlatformVoiceClient.signalDisconnect(state.uuid, false); + } + PlatformVoiceClient.makePeerProximity(state.uuid); + } + } else if (voiceChannel == EnumVoiceChannelType.NONE) { + packetSendCallback.accept(new CPacketVoiceSignalConnectEAG()); + } + } + voiceChannel = channel; + } + + public void handleVoiceSignalPacketTypeGlobal(Collection voicePlayers) { + if (voiceChannel != EnumVoiceChannelType.NONE) { + EaglercraftUUID self = Minecraft.getMinecraft().getSession().getProfile().getId(); + Set playersLost = new HashSet<>(this.voicePlayers.keySet()); + for (SPacketVoiceSignalGlobalEAG.UserData userData : voicePlayers) { + EaglercraftUUID uuid = new EaglercraftUUID(userData.uuidMost, userData.uuidLeast); + if (!uuid.equals(self)) { + if (!playersLost.remove(uuid)) { + announcePlayer(uuid); + } + this.voicePlayers.get(uuid).name = userData.username; + } + } + if (!playersLost.isEmpty()) { + for (EaglercraftUUID uuid : playersLost) { + dropPlayer(uuid); + } + } + } + } + + private void announcePlayer(EaglercraftUUID uuid) { + VoicePlayerState voiceState = new VoicePlayerState(this, uuid); + voicePlayers.put(uuid, voiceState); + if (voiceChannel == EnumVoiceChannelType.GLOBAL) { + voiceState.tryRequest(EagRuntime.steadyTimeMillis()); + } else if (voiceChannel == EnumVoiceChannelType.PROXIMITY) { + recheckNearby(voiceState); + if(voiceState.nearby) { + voiceState.tryRequest(EagRuntime.steadyTimeMillis()); + } + } + } + + private void recheckNearby(VoicePlayerState voiceState) { + Minecraft mc = Minecraft.getMinecraft(); + if(mc.theWorld != null) { + EntityPlayer player = mc.theWorld.getPlayerEntityByUUID(voiceState.uuid); + if(player != null) { + voiceState.nearby = goddamnManhattanDistance(mc, player); + if(voiceState.nearby) { + PlatformVoiceClient.updateVoicePosition(voiceState.uuid, player.posX, player.posY, player.posZ); + } + return; + } + } + voiceState.nearby = false; + } + + // Must perform these ham-fisted manhattan distance calculations to work with old clients + private boolean goddamnManhattanDistance(Minecraft mc, Entity player) { + final int prox = 22; + return Math.abs(mc.thePlayer.posX - player.posX) <= prox && Math.abs(mc.thePlayer.posY - player.posY) <= prox + && Math.abs(mc.thePlayer.posZ - player.posZ) <= prox; + } + + private void dropPlayer(EaglercraftUUID uuid) { + voicePlayers.remove(uuid); + PlatformVoiceClient.signalDisconnect(uuid, true); + } + + public void handleVoiceSignalPacketTypeConnectAnnounce(EaglercraftUUID user) { + if (voiceChannel != EnumVoiceChannelType.NONE) { + if (!voicePlayers.containsKey(user)) { + // Backwards compat with old servers :( + announcePlayer(user); + } + } + } + + public void handleVoiceSignalPacketTypeConnect(EaglercraftUUID user, boolean offer) { + if (voiceChannel != EnumVoiceChannelType.NONE) { + PlatformVoiceClient.signalConnect(user, offer); + } + } + + public void handleVoiceSignalPacketTypeICECandidate(EaglercraftUUID user, String ice) { + if (voiceChannel != EnumVoiceChannelType.NONE) { + PlatformVoiceClient.signalICECandidate(user, ice); + } + } + + public void handleVoiceSignalPacketTypeDescription(EaglercraftUUID user, String desc) { + if (voiceChannel != EnumVoiceChannelType.NONE) { + PlatformVoiceClient.signalDescription(user, desc); + } + } + + public void handleVoiceSignalPacketTypeDisconnect(EaglercraftUUID user) { + if (voiceChannel != EnumVoiceChannelType.NONE) { + VoicePlayerState state = voicePlayers.get(user); + if (state != null) { + state.handleDisconnect(); + } + PlatformVoiceClient.signalDisconnect(user, true); + } + } + + public void sendPacketRequest(VoicePlayerState voicePlayerState) { + if (voiceChannel != EnumVoiceChannelType.NONE) { + packetSendCallback.accept(new CPacketVoiceSignalRequestEAG(voicePlayerState.uuid.msb, voicePlayerState.uuid.lsb)); + } + } + + public void sendPacketDesc(EaglercraftUUID peerId, String desc) { + if (voiceChannel != EnumVoiceChannelType.NONE) { + packetSendCallback.accept(new CPacketVoiceSignalDescEAG(peerId.msb, peerId.lsb, desc)); + } + } + + public void sendPacketICE(EaglercraftUUID peerId, String candidate) { + if (voiceChannel != EnumVoiceChannelType.NONE) { + packetSendCallback.accept(new CPacketVoiceSignalICEEAG(peerId.msb, peerId.lsb, candidate)); + } + } + + public void sendPacketDisconnectPeer(EaglercraftUUID peerId) { + if (voiceChannel != EnumVoiceChannelType.NONE) { + VoicePlayerState state = voicePlayers.get(peerId); + if (state != null) { + state.handleDisconnect(); + } + if (protocolVers >= 4) { + packetSendCallback.accept(new CPacketVoiceSignalDisconnectPeerV4EAG(peerId.msb, peerId.lsb)); + } else { + packetSendCallback.accept(new CPacketVoiceSignalDisconnectV3EAG(true, peerId.msb, peerId.lsb)); + } + } + } + + public String getVoiceUsername(EaglercraftUUID uuid) { + VoicePlayerState state = voicePlayers.get(uuid); + if (state != null) { + return state.name; + } else { + return uuid.toString(); + } + } + + public EnumVoiceChannelStatus getVoiceStatus() { + if (voiceChannel != EnumVoiceChannelType.NONE) { + return PlatformVoiceClient.getReadyState() != EnumVoiceChannelReadyState.DEVICE_INITIALIZED ? + EnumVoiceChannelStatus.CONNECTING : EnumVoiceChannelStatus.CONNECTED; + } else { + return EnumVoiceChannelStatus.DISCONNECTED; + } + } + + public void tickVoiceClient() { + if (voiceChannel == EnumVoiceChannelType.GLOBAL) { + long millis = EagRuntime.steadyTimeMillis(); + if (millis - lastUpdate > 1500l) { + lastUpdate = millis; + for (VoicePlayerState state : voicePlayers.values()) { + state.tryRequest(millis); + } + } + } else if (voiceChannel == EnumVoiceChannelType.PROXIMITY) { + long millis = EagRuntime.steadyTimeMillis(); + if (millis - lastUpdate > 100l) { + lastUpdate = millis; + for (VoicePlayerState state : voicePlayers.values()) { + boolean old = state.nearby; + recheckNearby(state); + if (state.nearby) { + state.tryRequest(millis); + } else if (old) { + PlatformVoiceClient.signalDisconnect(state.uuid, false); + } + } + } + } + } + + public void release() { + for (VoicePlayerState state : new ArrayList<>(voicePlayers.values())) { + dropPlayer(state.uuid); + } + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoicePlayerState.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoicePlayerState.java new file mode 100755 index 00000000..08b21228 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoicePlayerState.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025 lax1dude, ayunami2000. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.voice; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; + +public class VoicePlayerState { + + public final VoiceClientInstance client; + public final EaglercraftUUID uuid; + public long lastRequest = -15000l; + public String name = null; + public boolean nearby = false; + + public VoicePlayerState(VoiceClientInstance client, EaglercraftUUID uuid) { + this.client = client; + this.uuid = uuid; + } + + public void tryRequest(long millis) { + if (!VoiceClientController.getVoiceListening().contains(uuid)) { + if (millis - lastRequest > 4000l) { + lastRequest = millis; + client.sendPacketRequest(this); + } + } + } + + public void handleDisconnect() { + lastRequest = 0l; + } + + public boolean isListening() { + return VoiceClientController.getVoiceListening().contains(uuid); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenPhishingWarning.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenPhishingWarning.java index 0552ce4b..42dac88e 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenPhishingWarning.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenPhishingWarning.java @@ -40,17 +40,17 @@ public class GuiScreenPhishingWarning extends GuiScreen { public void initGui() { this.buttonList.clear(); - this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 134, I18n.format("webviewPhishingWaring.continue"))); + this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 134, I18n.format("webviewPhishingWarning.continue"))); } public void drawScreen(int mx, int my, float pt) { this.drawDefaultBackground(); - this.drawCenteredString(fontRendererObj, EnumChatFormatting.BOLD + I18n.format("webviewPhishingWaring.title"), this.width / 2, 70, 0xFF4444); - this.drawCenteredString(fontRendererObj, I18n.format("webviewPhishingWaring.text0"), this.width / 2, 90, 16777215); - this.drawCenteredString(fontRendererObj, I18n.format("webviewPhishingWaring.text1"), this.width / 2, 102, 16777215); - this.drawCenteredString(fontRendererObj, I18n.format("webviewPhishingWaring.text2"), this.width / 2, 114, 16777215); + this.drawCenteredString(fontRendererObj, EnumChatFormatting.BOLD + I18n.format("webviewPhishingWarning.title"), this.width / 2, 70, 0xFF4444); + this.drawCenteredString(fontRendererObj, I18n.format("webviewPhishingWarning.text0"), this.width / 2, 90, 16777215); + this.drawCenteredString(fontRendererObj, I18n.format("webviewPhishingWarning.text1"), this.width / 2, 102, 16777215); + this.drawCenteredString(fontRendererObj, I18n.format("webviewPhishingWarning.text2"), this.width / 2, 114, 16777215); - String dontShowAgain = I18n.format("webviewPhishingWaring.dontShowAgain"); + String dontShowAgain = I18n.format("webviewPhishingWarning.dontShowAgain"); int w = fontRendererObj.getStringWidth(dontShowAgain) + 20; int ww = (this.width - w) / 2; this.drawString(fontRendererObj, dontShowAgain, ww + 20, 137, 0xCCCCCC); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenRecieveServerInfo.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenRecieveServerInfo.java index 3c152332..49c83b8c 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenRecieveServerInfo.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenRecieveServerInfo.java @@ -23,6 +23,7 @@ import java.util.Arrays; import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; import net.lax1dude.eaglercraft.v1_8.EaglerZLIB; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; import net.lax1dude.eaglercraft.v1_8.IOUtils; import net.lax1dude.eaglercraft.v1_8.crypto.SHA1Digest; import net.lax1dude.eaglercraft.v1_8.internal.WebViewOptions; @@ -34,6 +35,7 @@ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketRequestServerInfoV4EAG; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.network.NetHandlerPlayClient; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.resources.I18n; @@ -44,13 +46,25 @@ public class GuiScreenRecieveServerInfo extends GuiScreen { protected final GuiScreen parent; protected final byte[] expectHash; + protected final IDisplayWebviewProc proc; protected int timer; protected int timer2; protected String statusString = "recieveServerInfo.checkingCache"; + public static interface IDisplayWebviewProc { + GuiScreen display(GuiScreen parent, byte[] blob, EaglercraftUUID permissionsOriginUUID); + } + public GuiScreenRecieveServerInfo(GuiScreen parent, byte[] expectHash) { this.parent = parent; this.expectHash = expectHash; + this.proc = GuiScreenServerInfo::createForCurrentState; + } + + public GuiScreenRecieveServerInfo(GuiScreen parent, byte[] expectHash, IDisplayWebviewProc proc) { + this.parent = parent; + this.expectHash = expectHash; + this.proc = proc; } public void initGui() { @@ -105,28 +119,26 @@ public class GuiScreenRecieveServerInfo extends GuiScreen { mc.displayGuiScreen(parent); return; } + NetHandlerPlayClient sendQueue = mc.thePlayer.sendQueue; ++timer; if(timer == 1) { byte[] data = ServerInfoCache.loadFromCache(expectHash); if(data != null) { - mc.displayGuiScreen(GuiScreenServerInfo.createForCurrentState(parent, data, WebViewOptions.getEmbedOriginUUID(expectHash))); + mc.displayGuiScreen(proc.display(parent, data, WebViewOptions.getEmbedOriginUUID(expectHash))); }else { - byte[] b = mc.thePlayer.sendQueue.cachedServerInfoData; - if(b != null) { + byte[] b = sendQueue.cachedServerInfoData; + if(b != null && Arrays.equals(expectHash, sendQueue.cachedServerInfoHash)) { if(b.length == 0) { mc.displayGuiScreen(new GuiScreenGenericErrorMessage("serverInfoFailure.title", "serverInfoFailure.desc", parent)); }else { ServerInfoCache.storeInCache(expectHash, b); - mc.displayGuiScreen(GuiScreenServerInfo.createForCurrentState(parent, b, WebViewOptions.getEmbedOriginUUID(expectHash))); + mc.displayGuiScreen(proc.display(parent, b, WebViewOptions.getEmbedOriginUUID(expectHash))); } }else { statusString = "recieveServerInfo.contactingServer"; - if(!mc.thePlayer.sendQueue.hasRequestedServerInfo) { - if(!ServerInfoCache.hasLastChunk || !Arrays.equals(ServerInfoCache.chunkRecieveHash, expectHash)) { - ServerInfoCache.clearDownload(); - mc.thePlayer.sendQueue.sendEaglerMessage(new CPacketRequestServerInfoV4EAG(expectHash)); - mc.thePlayer.sendQueue.hasRequestedServerInfo = true; - } + if(!ServerInfoCache.hasLastChunk || !Arrays.equals(ServerInfoCache.chunkRecieveHash, expectHash)) { + ServerInfoCache.clearDownload(); + sendQueue.sendEaglerMessage(new CPacketRequestServerInfoV4EAG(expectHash)); } } } @@ -144,7 +156,8 @@ public class GuiScreenRecieveServerInfo extends GuiScreen { } if(i != ServerInfoCache.chunkCurrentSize) { logger.error("An unknown error occured!"); - mc.thePlayer.sendQueue.cachedServerInfoData = new byte[0]; + sendQueue.cachedServerInfoHash = expectHash; + sendQueue.cachedServerInfoData = new byte[0]; mc.displayGuiScreen(new GuiScreenGenericErrorMessage("serverInfoFailure.title", "serverInfoFailure.desc", parent)); return; } @@ -154,14 +167,16 @@ public class GuiScreenRecieveServerInfo extends GuiScreen { int finalSize = (new DataInputStream(bis)).readInt(); if(finalSize < 0) { logger.error("The response data was corrupt, decompressed size is negative!"); - mc.thePlayer.sendQueue.cachedServerInfoData = new byte[0]; + sendQueue.cachedServerInfoHash = expectHash; + sendQueue.cachedServerInfoData = new byte[0]; mc.displayGuiScreen(new GuiScreenGenericErrorMessage("serverInfoFailure.title", "serverInfoFailure.desc", parent)); return; } if(finalSize > ServerInfoCache.CACHE_MAX_SIZE * 2) { - logger.error("Failed to decompress/verify server info response! Size is massive, {} " + finalSize + " bytes reported!"); + logger.error("Failed to decompress/verify server info response! Size is massive, {} bytes reported!", finalSize); logger.error("Aborting decompression. Rejoin the server to try again."); - mc.thePlayer.sendQueue.cachedServerInfoData = new byte[0]; + sendQueue.cachedServerInfoHash = expectHash; + sendQueue.cachedServerInfoData = new byte[0]; mc.displayGuiScreen(new GuiScreenGenericErrorMessage("serverInfoFailure.title", "serverInfoFailure.desc", parent)); return; } @@ -175,17 +190,20 @@ public class GuiScreenRecieveServerInfo extends GuiScreen { digest.doFinal(csum, 0); if(Arrays.equals(csum, expectHash)) { ServerInfoCache.storeInCache(csum, decompressed); - mc.thePlayer.sendQueue.cachedServerInfoData = decompressed; - mc.displayGuiScreen(GuiScreenServerInfo.createForCurrentState(parent, decompressed, WebViewOptions.getEmbedOriginUUID(expectHash))); + sendQueue.cachedServerInfoHash = expectHash; + sendQueue.cachedServerInfoData = decompressed; + mc.displayGuiScreen(proc.display(parent, decompressed, WebViewOptions.getEmbedOriginUUID(expectHash))); }else { logger.error("The data recieved from the server did not have the correct SHA1 checksum! Rejoin the server to try again."); - mc.thePlayer.sendQueue.cachedServerInfoData = new byte[0]; + sendQueue.cachedServerInfoHash = expectHash; + sendQueue.cachedServerInfoData = new byte[0]; mc.displayGuiScreen(new GuiScreenGenericErrorMessage("serverInfoFailure.title", "serverInfoFailure.desc", parent)); } }catch(IOException ex) { logger.error("Failed to decompress/verify server info response! Rejoin the server to try again."); logger.error(ex); - mc.thePlayer.sendQueue.cachedServerInfoData = new byte[0]; + sendQueue.cachedServerInfoHash = expectHash; + sendQueue.cachedServerInfoData = new byte[0]; mc.displayGuiScreen(new GuiScreenGenericErrorMessage("serverInfoFailure.title", "serverInfoFailure.desc", parent)); } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenRequestDisplay.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenRequestDisplay.java new file mode 100755 index 00000000..3aa9ce13 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenRequestDisplay.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * 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. + * + */ + +package net.lax1dude.eaglercraft.v1_8.webview; + +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.network.NetHandlerPlayClient; +import net.minecraft.client.resources.I18n; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.ResourceLocation; + +public class GuiScreenRequestDisplay extends GuiScreen { + + private static final ResourceLocation beaconGuiTexture = new ResourceLocation("textures/gui/container/beacon.png"); + + private GuiScreen cont; + private GuiScreen back; + private NetHandlerPlayClient netHandler; + private boolean mouseOverCheck; + private boolean hasCheckedBox; + + public GuiScreenRequestDisplay(GuiScreen cont, GuiScreen back, NetHandlerPlayClient netHandler) { + this.cont = cont; + this.back = back; + this.netHandler = netHandler; + } + + public void initGui() { + this.buttonList.clear(); + this.buttonList.add(new GuiButton(0, this.width / 2 + 2, this.height / 6 + 122, 148, 20, I18n.format("webviewPhishingWarning.continue"))); + this.buttonList.add(new GuiButton(1, this.width / 2 - 150, this.height / 6 + 122, 148, 20, I18n.format("gui.cancel"))); + } + + public void drawScreen(int mx, int my, float pt) { + this.drawDefaultBackground(); + this.drawCenteredString(fontRendererObj, EnumChatFormatting.BOLD + I18n.format("webviewDisplayWarning.title"), this.width / 2, 70, 0xFF4444); + this.drawCenteredString(fontRendererObj, I18n.format("webviewDisplayWarning.text0"), this.width / 2, 90, 16777215); + this.drawCenteredString(fontRendererObj, I18n.format("webviewDisplayWarning.text1"), this.width / 2, 102, 16777215); + + String dontShowAgain = I18n.format("webviewPhishingWarning.dontShowAgain"); + int w = fontRendererObj.getStringWidth(dontShowAgain) + 20; + int ww = (this.width - w) / 2; + this.drawString(fontRendererObj, dontShowAgain, ww + 20, 125, 0xCCCCCC); + + mouseOverCheck = ww < mx && ww + 17 > mx && 121 < my && 138 > my; + + if(mouseOverCheck) { + GlStateManager.color(0.7f, 0.7f, 1.0f, 1.0f); + }else { + GlStateManager.color(0.6f, 0.6f, 0.6f, 1.0f); + } + + mc.getTextureManager().bindTexture(beaconGuiTexture); + + GlStateManager.pushMatrix(); + GlStateManager.scale(0.75f, 0.75f, 0.75f); + drawTexturedModalRect(ww * 4 / 3, 121 * 4 / 3, 22, 219, 22, 22); + GlStateManager.popMatrix(); + + if(hasCheckedBox) { + GlStateManager.pushMatrix(); + GlStateManager.color(1.1f, 1.1f, 1.1f, 1.0f); + GlStateManager.translate(0.5f, 0.5f, 0.0f); + drawTexturedModalRect(ww, 121, 90, 222, 16, 16); + GlStateManager.popMatrix(); + } + + super.drawScreen(mx, my, pt); + } + + protected void actionPerformed(GuiButton par1GuiButton) { + if(par1GuiButton.id == 0) { + if(hasCheckedBox) { + netHandler.allowedDisplayWebview = true; + netHandler.allowedDisplayWebviewYes = true; + } + mc.displayGuiScreen(cont); + }else if (par1GuiButton.id == 1) { + if(hasCheckedBox) { + netHandler.allowedDisplayWebview = true; + netHandler.allowedDisplayWebviewYes = false; + } + mc.displayGuiScreen(back); + } + } + + @Override + protected void mouseClicked(int mx, int my, int btn) { + if(btn == 0 && mouseOverCheck) { + hasCheckedBox = !hasCheckedBox; + mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + return; + } + super.mouseClicked(mx, my, btn); + } + +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenServerInfo.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenServerInfo.java index 4e74218c..2cea2cf1 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenServerInfo.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenServerInfo.java @@ -26,6 +26,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.WebViewOptions; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.minecraft.GuiScreenGenericErrorMessage; +import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketDisplayWebViewURLV5EAG; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.ScaledResolution; @@ -91,6 +92,54 @@ public class GuiScreenServerInfo extends GuiScreen { opts.fallbackTitle = PauseMenuCustomizeState.serverInfoEmbedTitle; } + public static GuiScreen createForDisplayRequest(GuiScreen parent, int perms, String title, String url) { + URI urlObj; + try { + urlObj = new URI(url); + }catch(URISyntaxException ex) { + logger.error("Refusing to iframe an invalid URL: {}", url); + logger.error(ex); + return new GuiScreenGenericErrorMessage("webviewInvalidURL.title", "webviewInvalidURL.desc", parent); + } + return createForDisplayRequest(parent, perms, title, urlObj); + } + + public static GuiScreen createForDisplayRequest(GuiScreen parent, int perms, String title, URI url) { + boolean support = WebViewOverlayController.supported(); + boolean fallbackSupport = WebViewOverlayController.fallbackSupported(); + if(!support && !fallbackSupport) { + return new GuiScreenGenericErrorMessage("webviewNotSupported.title", "webviewNotSupported.desc", parent); + } + WebViewOptions opts = new WebViewOptions(); + opts.contentMode = EnumWebViewContentMode.URL_BASED; + opts.url = url; + setupState(opts, perms, title); + opts.permissionsOriginUUID = WebViewOptions.getURLOriginUUID(url); + return support ? new GuiScreenServerInfo(parent, opts) : new GuiScreenServerInfoDesktop(parent, opts); + } + + public static GuiScreen createForDisplayRequest(GuiScreen parent, int perms, String title, byte[] blob, + EaglercraftUUID permissionsOriginUUID) { + boolean support = WebViewOverlayController.supported(); + boolean fallbackSupport = WebViewOverlayController.fallbackSupported(); + if(!support && !fallbackSupport) { + return new GuiScreenGenericErrorMessage("webviewNotSupported.title", "webviewNotSupported.desc", parent); + } + WebViewOptions opts = new WebViewOptions(); + opts.contentMode = EnumWebViewContentMode.BLOB_BASED; + opts.blob = blob; + setupState(opts, perms, title); + opts.permissionsOriginUUID = permissionsOriginUUID; + return support ? new GuiScreenServerInfo(parent, opts) : new GuiScreenServerInfoDesktop(parent, opts); + } + + public static void setupState(WebViewOptions opts, int perms, String title) { + opts.scriptEnabled = (perms & SPacketDisplayWebViewURLV5EAG.FLAG_PERMS_JAVASCRIPT) != 0; + opts.strictCSPEnable = (perms & SPacketDisplayWebViewURLV5EAG.FLAG_PERMS_STRICT_CSP) != 0; + opts.serverMessageAPIEnabled = (perms & SPacketDisplayWebViewURLV5EAG.FLAG_PERMS_MESSAGE_API) != 0; + opts.fallbackTitle = title; + } + public void initGui() { ScaledResolution res = mc.scaledResolution; if(!isShowing) { @@ -118,8 +167,7 @@ public class GuiScreenServerInfo extends GuiScreen { public void drawScreen(int mx, int my, float pt) { drawDefaultBackground(); - drawCenteredString(fontRendererObj, PauseMenuCustomizeState.serverInfoEmbedTitle == null ? "Server Info" - : PauseMenuCustomizeState.serverInfoEmbedTitle, width / 2, 13, 0xFFFFFF); + drawCenteredString(fontRendererObj, opts.fallbackTitle == null ? "Server Info" : opts.fallbackTitle, width / 2, 13, 0xFFFFFF); super.drawScreen(mx, my, pt); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenServerInfoDesktop.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenServerInfoDesktop.java index 2f11bae2..5963d39d 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenServerInfoDesktop.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/webview/GuiScreenServerInfoDesktop.java @@ -17,7 +17,6 @@ package net.lax1dude.eaglercraft.v1_8.webview; import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.PauseMenuCustomizeState; import net.lax1dude.eaglercraft.v1_8.internal.WebViewOptions; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; @@ -77,7 +76,8 @@ public class GuiScreenServerInfoDesktop extends GuiScreen { public void drawScreen(int mx, int my, float pt) { drawDefaultBackground(); - drawCenteredString(fontRendererObj, PauseMenuCustomizeState.serverInfoEmbedTitle, this.width / 2, 70, 16777215); + drawCenteredString(fontRendererObj, opts.fallbackTitle == null ? "Server Info" : opts.fallbackTitle, + this.width / 2, 70, 16777215); drawCenteredString(fontRendererObj, I18n.format("fallbackWebViewScreen.text0"), this.width / 2, 90, 11184810); String link = WebViewOverlayController.fallbackRunning() ? WebViewOverlayController.getFallbackURL() : I18n.format(hasStarted ? "fallbackWebViewScreen.exited" : "fallbackWebViewScreen.startingUp"); diff --git a/src/main/java/org/apache/commons/lang3/AnnotationUtils.java b/src/main/java/org/apache/commons/lang3/AnnotationUtils.java deleted file mode 100755 index d67de114..00000000 --- a/src/main/java/org/apache/commons/lang3/AnnotationUtils.java +++ /dev/null @@ -1,377 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import java.lang.annotation.Annotation; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Arrays; - -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - -import net.lax1dude.eaglercraft.v1_8.HString; - -/** - *

- * Helper methods for working with {@link Annotation} instances. - *

- * - *

- * This class contains various utility methods that make working with - * annotations simpler. - *

- * - *

- * {@link Annotation} instances are always proxy objects; unfortunately dynamic - * proxies cannot be depended upon to know how to implement certain methods in - * the same manner as would be done by "natural" {@link Annotation}s. The - * methods presented in this class can be used to avoid that possibility. It is - * of course also possible for dynamic proxies to actually delegate their e.g. - * {@link Annotation#equals(Object)}/{@link Annotation#hashCode()}/ - * {@link Annotation#toString()} implementations to {@link AnnotationUtils}. - *

- * - *

- * #ThreadSafe# - *

- * - * @since 3.0 - */ -public class AnnotationUtils { - - /** - * A style that prints annotations as recommended. - */ - private static final ToStringStyle TO_STRING_STYLE = new ToStringStyle() { - /** Serialization version */ - private static final long serialVersionUID = 1L; - - { - setDefaultFullDetail(true); - setArrayContentDetail(true); - setUseClassName(true); - setUseShortClassName(true); - setUseIdentityHashCode(false); - setContentStart("("); - setContentEnd(")"); - setFieldSeparator(", "); - setArrayStart("["); - setArrayEnd("]"); - } - - /** - * {@inheritDoc} - */ - @Override - protected String getShortClassName(final Class cls) { - return cls.getSimpleName(); - } - - /** - * {@inheritDoc} - */ - @Override - protected void appendDetail(final StringBuffer buffer, final String fieldName, Object value) { - if (value instanceof Annotation) { - value = AnnotationUtils.toString((Annotation) value); - } - super.appendDetail(buffer, fieldName, value); - } - - }; - - /** - *

- * {@code AnnotationUtils} instances should NOT be constructed in standard - * programming. Instead, the class should be used statically. - *

- * - *

- * This constructor is public to permit tools that require a JavaBean instance - * to operate. - *

- */ - public AnnotationUtils() { - } - - // ----------------------------------------------------------------------- - /** - *

- * Checks if two annotations are equal using the criteria for equality presented - * in the {@link Annotation#equals(Object)} API docs. - *

- * - * @param a1 the first Annotation to compare, {@code null} returns {@code false} - * unless both are {@code null} - * @param a2 the second Annotation to compare, {@code null} returns - * {@code false} unless both are {@code null} - * @return {@code true} if the two annotations are {@code equal} or both - * {@code null} - */ - public static boolean equals(final Annotation a1, final Annotation a2) { - if (a1 == a2) { - return true; - } - if (a1 == null || a2 == null) { - return false; - } - final Class type1 = a1.annotationType(); - final Class type2 = a2.annotationType(); - Validate.notNull(type1, "Annotation %s with null annotationType()", a1); - Validate.notNull(type2, "Annotation %s with null annotationType()", a2); - if (!type1.equals(type2)) { - return false; - } - try { - for (final Method m : type1.getDeclaredMethods()) { - if (m.getParameterTypes().length == 0 && isValidAnnotationMemberType(m.getReturnType())) { - final Object v1 = m.invoke(a1); - final Object v2 = m.invoke(a2); - if (!memberEquals(m.getReturnType(), v1, v2)) { - return false; - } - } - } - } catch (final IllegalAccessException | InvocationTargetException ex) { - return false; - } - return true; - } - - /** - *

- * Generate a hash code for the given annotation using the algorithm presented - * in the {@link Annotation#hashCode()} API docs. - *

- * - * @param a the Annotation for a hash code calculation is desired, not - * {@code null} - * @return the calculated hash code - * @throws RuntimeException if an {@code Exception} is encountered during - * annotation member access - * @throws IllegalStateException if an annotation method invocation returns - * {@code null} - */ - public static int hashCode(final Annotation a) { - int result = 0; - final Class type = a.annotationType(); - for (final Method m : type.getDeclaredMethods()) { - try { - final Object value = m.invoke(a); - if (value == null) { - throw new IllegalStateException(HString.format("Annotation method %s returned null", m)); - } - result += hashMember(m.getName(), value); - } catch (final RuntimeException ex) { - throw ex; - } catch (final Exception ex) { - throw new RuntimeException(ex); - } - } - return result; - } - - /** - *

- * Generate a string representation of an Annotation, as suggested by - * {@link Annotation#toString()}. - *

- * - * @param a the annotation of which a string representation is desired - * @return the standard string representation of an annotation, not {@code null} - */ - public static String toString(final Annotation a) { - final ToStringBuilder builder = new ToStringBuilder(a, TO_STRING_STYLE); - for (final Method m : a.annotationType().getDeclaredMethods()) { - if (m.getParameterTypes().length > 0) { - continue; // wtf? - } - try { - builder.append(m.getName(), m.invoke(a)); - } catch (final RuntimeException ex) { - throw ex; - } catch (final Exception ex) { - throw new RuntimeException(ex); - } - } - return builder.build(); - } - - /** - *

- * Checks if the specified type is permitted as an annotation member. - *

- * - *

- * The Java language specification only permits certain types to be used in - * annotations. These include {@link String}, {@link Class}, primitive types, - * {@link Annotation}, {@link Enum}, and single-dimensional arrays of these - * types. - *

- * - * @param type the type to check, {@code null} - * @return {@code true} if the type is a valid type to use in an annotation - */ - public static boolean isValidAnnotationMemberType(Class type) { - if (type == null) { - return false; - } - if (type.isArray()) { - type = type.getComponentType(); - } - return type.isPrimitive() || type.isEnum() || type.isAnnotation() || String.class.equals(type) - || Class.class.equals(type); - } - - // besides modularity, this has the advantage of autoboxing primitives: - /** - * Helper method for generating a hash code for a member of an annotation. - * - * @param name the name of the member - * @param value the value of the member - * @return a hash code for this member - */ - private static int hashMember(final String name, final Object value) { - final int part1 = name.hashCode() * 127; - if (value.getClass().isArray()) { - return part1 ^ arrayMemberHash(value.getClass().getComponentType(), value); - } - if (value instanceof Annotation) { - return part1 ^ hashCode((Annotation) value); - } - return part1 ^ value.hashCode(); - } - - /** - * Helper method for checking whether two objects of the given type are equal. - * This method is used to compare the parameters of two annotation instances. - * - * @param type the type of the objects to be compared - * @param o1 the first object - * @param o2 the second object - * @return a flag whether these objects are equal - */ - private static boolean memberEquals(final Class type, final Object o1, final Object o2) { - if (o1 == o2) { - return true; - } - if (o1 == null || o2 == null) { - return false; - } - if (type.isArray()) { - return arrayMemberEquals(type.getComponentType(), o1, o2); - } - if (type.isAnnotation()) { - return equals((Annotation) o1, (Annotation) o2); - } - return o1.equals(o2); - } - - /** - * Helper method for comparing two objects of an array type. - * - * @param componentType the component type of the array - * @param o1 the first object - * @param o2 the second object - * @return a flag whether these objects are equal - */ - private static boolean arrayMemberEquals(final Class componentType, final Object o1, final Object o2) { - if (componentType.isAnnotation()) { - return annotationArrayMemberEquals((Annotation[]) o1, (Annotation[]) o2); - } - if (componentType.equals(Byte.TYPE)) { - return Arrays.equals((byte[]) o1, (byte[]) o2); - } - if (componentType.equals(Short.TYPE)) { - return Arrays.equals((short[]) o1, (short[]) o2); - } - if (componentType.equals(Integer.TYPE)) { - return Arrays.equals((int[]) o1, (int[]) o2); - } - if (componentType.equals(Character.TYPE)) { - return Arrays.equals((char[]) o1, (char[]) o2); - } - if (componentType.equals(Long.TYPE)) { - return Arrays.equals((long[]) o1, (long[]) o2); - } - if (componentType.equals(Float.TYPE)) { - return Arrays.equals((float[]) o1, (float[]) o2); - } - if (componentType.equals(Double.TYPE)) { - return Arrays.equals((double[]) o1, (double[]) o2); - } - if (componentType.equals(Boolean.TYPE)) { - return Arrays.equals((boolean[]) o1, (boolean[]) o2); - } - return Arrays.equals((Object[]) o1, (Object[]) o2); - } - - /** - * Helper method for comparing two arrays of annotations. - * - * @param a1 the first array - * @param a2 the second array - * @return a flag whether these arrays are equal - */ - private static boolean annotationArrayMemberEquals(final Annotation[] a1, final Annotation[] a2) { - if (a1.length != a2.length) { - return false; - } - for (int i = 0; i < a1.length; i++) { - if (!equals(a1[i], a2[i])) { - return false; - } - } - return true; - } - - /** - * Helper method for generating a hash code for an array. - * - * @param componentType the component type of the array - * @param o the array - * @return a hash code for the specified array - */ - private static int arrayMemberHash(final Class componentType, final Object o) { - if (componentType.equals(Byte.TYPE)) { - return Arrays.hashCode((byte[]) o); - } - if (componentType.equals(Short.TYPE)) { - return Arrays.hashCode((short[]) o); - } - if (componentType.equals(Integer.TYPE)) { - return Arrays.hashCode((int[]) o); - } - if (componentType.equals(Character.TYPE)) { - return Arrays.hashCode((char[]) o); - } - if (componentType.equals(Long.TYPE)) { - return Arrays.hashCode((long[]) o); - } - if (componentType.equals(Float.TYPE)) { - return Arrays.hashCode((float[]) o); - } - if (componentType.equals(Double.TYPE)) { - return Arrays.hashCode((double[]) o); - } - if (componentType.equals(Boolean.TYPE)) { - return Arrays.hashCode((boolean[]) o); - } - return Arrays.hashCode((Object[]) o); - } -} diff --git a/src/main/java/org/apache/commons/lang3/ArchUtils.java b/src/main/java/org/apache/commons/lang3/ArchUtils.java deleted file mode 100755 index 97775734..00000000 --- a/src/main/java/org/apache/commons/lang3/ArchUtils.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Stream; - -import org.apache.commons.lang3.arch.Processor; - -/** - * An utility class for the os.arch System Property. The class defines methods - * for identifying the architecture of the current JVM. - *

- * Important: The os.arch System Property returns the architecture used by the - * JVM not of the operating system. - *

- * - * @since 3.6 - */ -public class ArchUtils { - - private static final Map ARCH_TO_PROCESSOR; - - static { - ARCH_TO_PROCESSOR = new HashMap<>(); - init(); - } - - private static void init() { - init_X86_32Bit(); - init_X86_64Bit(); - init_IA64_32Bit(); - init_IA64_64Bit(); - init_PPC_32Bit(); - init_PPC_64Bit(); - } - - private static void init_X86_32Bit() { - final Processor processor = new Processor(Processor.Arch.BIT_32, Processor.Type.X86); - addProcessors(processor, "x86", "i386", "i486", "i586", "i686", "pentium"); - } - - private static void init_X86_64Bit() { - final Processor processor = new Processor(Processor.Arch.BIT_64, Processor.Type.X86); - addProcessors(processor, "x86_64", "amd64", "em64t", "universal"); - } - - private static void init_IA64_32Bit() { - final Processor processor = new Processor(Processor.Arch.BIT_32, Processor.Type.IA_64); - addProcessors(processor, "ia64_32", "ia64n"); - } - - private static void init_IA64_64Bit() { - final Processor processor = new Processor(Processor.Arch.BIT_64, Processor.Type.IA_64); - addProcessors(processor, "ia64", "ia64w"); - } - - private static void init_PPC_32Bit() { - final Processor processor = new Processor(Processor.Arch.BIT_32, Processor.Type.PPC); - addProcessors(processor, "ppc", "power", "powerpc", "power_pc", "power_rs"); - } - - private static void init_PPC_64Bit() { - final Processor processor = new Processor(Processor.Arch.BIT_64, Processor.Type.PPC); - addProcessors(processor, "ppc64", "power64", "powerpc64", "power_pc64", "power_rs64"); - } - - /** - * Adds the given {@link Processor} with the given key {@link String} to the - * map. - * - * @param key The key as {@link String}. - * @param processor The {@link Processor} to add. - * @throws IllegalStateException If the key already exists. - */ - private static void addProcessor(final String key, final Processor processor) { - if (ARCH_TO_PROCESSOR.containsKey(key)) { - throw new IllegalStateException("Key " + key + " already exists in processor map"); - } - ARCH_TO_PROCESSOR.put(key, processor); - } - - /** - * Adds the given {@link Processor} with the given keys to the map. - * - * @param keys The keys. - * @param processor The {@link Processor} to add. - * @throws IllegalStateException If the key already exists. - */ - private static void addProcessors(final Processor processor, final String... keys) { - Stream.of(keys).forEach(e -> addProcessor(e, processor)); - } - - /** - * Returns a {@link Processor} object of the current JVM. - * - *

- * Important: The os.arch System Property returns the architecture used by the - * JVM not of the operating system. - *

- * - * @return A {@link Processor} when supported, else {@code null}. - */ - public static Processor getProcessor() { - return getProcessor(SystemUtils.OS_ARCH); - } - - /** - * Returns a {@link Processor} object the given value {@link String}. The - * {@link String} must be like a value returned by the os.arch System Property. - * - * @param value A {@link String} like a value returned by the os.arch System - * Property. - * @return A {@link Processor} when it exists, else {@code null}. - */ - public static Processor getProcessor(final String value) { - return ARCH_TO_PROCESSOR.get(value); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/ArraySorter.java b/src/main/java/org/apache/commons/lang3/ArraySorter.java deleted file mode 100755 index 2d7c86fa..00000000 --- a/src/main/java/org/apache/commons/lang3/ArraySorter.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import java.util.Arrays; -import java.util.Comparator; - -/** - * Sorts and returns arrays in the fluent style. - * - * @since 3.12.0 - */ -public class ArraySorter { - - /** - * Sorts and returns the given array. - * - * @param array the array to sort. - * @return the given array. - * @see Arrays#sort(byte[]) - */ - public static byte[] sort(final byte[] array) { - Arrays.sort(array); - return array; - } - - /** - * Sorts and returns the given array. - * - * @param array the array to sort. - * @return the given array. - * @see Arrays#sort(char[]) - */ - public static char[] sort(final char[] array) { - Arrays.sort(array); - return array; - } - - /** - * Sorts and returns the given array. - * - * @param array the array to sort. - * @return the given array. - * @see Arrays#sort(double[]) - */ - public static double[] sort(final double[] array) { - Arrays.sort(array); - return array; - } - - /** - * Sorts and returns the given array. - * - * @param array the array to sort. - * @return the given array. - * @see Arrays#sort(float[]) - */ - public static float[] sort(final float[] array) { - Arrays.sort(array); - return array; - } - - /** - * Sorts and returns the given array. - * - * @param array the array to sort. - * @return the given array. - * @see Arrays#sort(int[]) - */ - public static int[] sort(final int[] array) { - Arrays.sort(array); - return array; - } - - /** - * Sorts and returns the given array. - * - * @param array the array to sort. - * @return the given array. - * @see Arrays#sort(long[]) - */ - public static long[] sort(final long[] array) { - Arrays.sort(array); - return array; - } - - /** - * Sorts and returns the given array. - * - * @param array the array to sort. - * @return the given array. - * @see Arrays#sort(short[]) - */ - public static short[] sort(final short[] array) { - Arrays.sort(array); - return array; - } - - /** - * Sorts and returns the given array. - * - * @param the array type. - * @param array the array to sort. - * @return the given array. - * @see Arrays#sort(Object[]) - */ - public static T[] sort(final T[] array) { - Arrays.sort(array); - return array; - } - - /** - * Sorts and returns the given array. - * - * @param the array type. - * @param array the array to sort. - * @param comparator the comparator to determine the order of the array. A - * {@code null} value uses the elements' {@link Comparable - * natural ordering}. - * @return the given array. - * @see Arrays#sort(Object[]) - */ - public static T[] sort(final T[] array, final Comparator comparator) { - Arrays.sort(array, comparator); - return array; - } - -} diff --git a/src/main/java/org/apache/commons/lang3/BitField.java b/src/main/java/org/apache/commons/lang3/BitField.java deleted file mode 100755 index e4d430ff..00000000 --- a/src/main/java/org/apache/commons/lang3/BitField.java +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -/** - *

- * Supports operations on bit-mapped fields. Instances of this class can be used - * to store a flag or data within an {@code int}, {@code short} or {@code byte}. - *

- * - *

- * Each {@code BitField} is constructed with a mask value, which indicates the - * bits that will be used to store and retrieve the data for that field. For - * instance, the mask {@code 0xFF} indicates the least-significant byte should - * be used to store the data. - *

- * - *

- * As an example, consider a car painting machine that accepts paint - * instructions as integers. Bit fields can be used to encode this: - *

- * - *
- * // blue, green and red are 1 byte values (0-255) stored in the three least
- * // significant bytes
- * BitField blue = new BitField(0xFF);
- * BitField green = new BitField(0xFF00);
- * BitField red = new BitField(0xFF0000);
- *
- * // anyColor is a flag triggered if any color is used
- * BitField anyColor = new BitField(0xFFFFFF);
- *
- * // isMetallic is a single bit flag
- * BitField isMetallic = new BitField(0x1000000);
- * 
- * - *

- * Using these {@code BitField} instances, a paint instruction can be encoded - * into an integer: - *

- * - *
- * int paintInstruction = 0;
- * paintInstruction = red.setValue(paintInstruction, 35);
- * paintInstruction = green.setValue(paintInstruction, 100);
- * paintInstruction = blue.setValue(paintInstruction, 255);
- * 
- * - *

- * Flags and data can be retrieved from the integer: - *

- * - *
- * // Prints true if red, green or blue is non-zero
- * System.out.println(anyColor.isSet(paintInstruction)); // prints true
- *
- * // Prints value of red, green and blue
- * System.out.println(red.getValue(paintInstruction)); // prints 35
- * System.out.println(green.getValue(paintInstruction)); // prints 100
- * System.out.println(blue.getValue(paintInstruction)); // prints 255
- *
- * // Prints true if isMetallic was set
- * System.out.println(isMetallic.isSet(paintInstruction)); // prints false
- * 
- * - * @since 2.0 - */ -public class BitField { - - private final int _mask; - private final int _shift_count; - - /** - *

- * Creates a BitField instance. - *

- * - * @param mask the mask specifying which bits apply to this BitField. Bits that - * are set in this mask are the bits that this BitField operates on - */ - public BitField(final int mask) { - _mask = mask; - _shift_count = mask == 0 ? 0 : Integer.numberOfTrailingZeros(mask); - } - - /** - *

- * Obtains the value for the specified BitField, appropriately shifted right. - *

- * - *

- * Many users of a BitField will want to treat the specified bits as an int - * value, and will not want to be aware that the value is stored as a BitField - * (and so shifted left so many bits). - *

- * - * @see #setValue(int,int) - * @param holder the int data containing the bits we're interested in - * @return the selected bits, shifted right appropriately - */ - public int getValue(final int holder) { - return getRawValue(holder) >> _shift_count; - } - - /** - *

- * Obtains the value for the specified BitField, appropriately shifted right, as - * a short. - *

- * - *

- * Many users of a BitField will want to treat the specified bits as an int - * value, and will not want to be aware that the value is stored as a BitField - * (and so shifted left so many bits). - *

- * - * @see #setShortValue(short,short) - * @param holder the short data containing the bits we're interested in - * @return the selected bits, shifted right appropriately - */ - public short getShortValue(final short holder) { - return (short) getValue(holder); - } - - /** - *

- * Obtains the value for the specified BitField, unshifted. - *

- * - * @param holder the int data containing the bits we're interested in - * @return the selected bits - */ - public int getRawValue(final int holder) { - return holder & _mask; - } - - /** - *

- * Obtains the value for the specified BitField, unshifted. - *

- * - * @param holder the short data containing the bits we're interested in - * @return the selected bits - */ - public short getShortRawValue(final short holder) { - return (short) getRawValue(holder); - } - - /** - *

- * Returns whether the field is set or not. - *

- * - *

- * This is most commonly used for a single-bit field, which is often used to - * represent a boolean value; the results of using it for a multi-bit field is - * to determine whether *any* of its bits are set. - *

- * - * @param holder the int data containing the bits we're interested in - * @return {@code true} if any of the bits are set, else {@code false} - */ - public boolean isSet(final int holder) { - return (holder & _mask) != 0; - } - - /** - *

- * Returns whether all of the bits are set or not. - *

- * - *

- * This is a stricter test than {@link #isSet(int)}, in that all of the bits in - * a multi-bit set must be set for this method to return {@code true}. - *

- * - * @param holder the int data containing the bits we're interested in - * @return {@code true} if all of the bits are set, else {@code false} - */ - public boolean isAllSet(final int holder) { - return (holder & _mask) == _mask; - } - - /** - *

- * Replaces the bits with new values. - *

- * - * @see #getValue(int) - * @param holder the int data containing the bits we're interested in - * @param value the new value for the specified bits - * @return the value of holder with the bits from the value parameter replacing - * the old bits - */ - public int setValue(final int holder, final int value) { - return (holder & ~_mask) | ((value << _shift_count) & _mask); - } - - /** - *

- * Replaces the bits with new values. - *

- * - * @see #getShortValue(short) - * @param holder the short data containing the bits we're interested in - * @param value the new value for the specified bits - * @return the value of holder with the bits from the value parameter replacing - * the old bits - */ - public short setShortValue(final short holder, final short value) { - return (short) setValue(holder, value); - } - - /** - *

- * Clears the bits. - *

- * - * @param holder the int data containing the bits we're interested in - * @return the value of holder with the specified bits cleared (set to - * {@code 0}) - */ - public int clear(final int holder) { - return holder & ~_mask; - } - - /** - *

- * Clears the bits. - *

- * - * @param holder the short data containing the bits we're interested in - * @return the value of holder with the specified bits cleared (set to - * {@code 0}) - */ - public short clearShort(final short holder) { - return (short) clear(holder); - } - - /** - *

- * Clears the bits. - *

- * - * @param holder the byte data containing the bits we're interested in - * - * @return the value of holder with the specified bits cleared (set to - * {@code 0}) - */ - public byte clearByte(final byte holder) { - return (byte) clear(holder); - } - - /** - *

- * Sets the bits. - *

- * - * @param holder the int data containing the bits we're interested in - * @return the value of holder with the specified bits set to {@code 1} - */ - public int set(final int holder) { - return holder | _mask; - } - - /** - *

- * Sets the bits. - *

- * - * @param holder the short data containing the bits we're interested in - * @return the value of holder with the specified bits set to {@code 1} - */ - public short setShort(final short holder) { - return (short) set(holder); - } - - /** - *

- * Sets the bits. - *

- * - * @param holder the byte data containing the bits we're interested in - * - * @return the value of holder with the specified bits set to {@code 1} - */ - public byte setByte(final byte holder) { - return (byte) set(holder); - } - - /** - *

- * Sets a boolean BitField. - *

- * - * @param holder the int data containing the bits we're interested in - * @param flag indicating whether to set or clear the bits - * @return the value of holder with the specified bits set or cleared - */ - public int setBoolean(final int holder, final boolean flag) { - return flag ? set(holder) : clear(holder); - } - - /** - *

- * Sets a boolean BitField. - *

- * - * @param holder the short data containing the bits we're interested in - * @param flag indicating whether to set or clear the bits - * @return the value of holder with the specified bits set or cleared - */ - public short setShortBoolean(final short holder, final boolean flag) { - return flag ? setShort(holder) : clearShort(holder); - } - - /** - *

- * Sets a boolean BitField. - *

- * - * @param holder the byte data containing the bits we're interested in - * @param flag indicating whether to set or clear the bits - * @return the value of holder with the specified bits set or cleared - */ - public byte setByteBoolean(final byte holder, final boolean flag) { - return flag ? setByte(holder) : clearByte(holder); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/BooleanUtils.java b/src/main/java/org/apache/commons/lang3/BooleanUtils.java deleted file mode 100755 index 541a8660..00000000 --- a/src/main/java/org/apache/commons/lang3/BooleanUtils.java +++ /dev/null @@ -1,1167 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import org.apache.commons.lang3.math.NumberUtils; - -/** - *

- * Operations on boolean primitives and Boolean objects. - *

- * - *

- * This class tries to handle {@code null} input gracefully. An exception will - * not be thrown for a {@code null} input. Each method documents its behavior in - * more detail. - *

- * - *

- * #ThreadSafe# - *

- * - * @since 2.0 - */ -public class BooleanUtils { - /** - * The false String {@code "false"}. - * - * @since 3.12.0 - */ - public static final String FALSE = "false"; - - /** - * The no String {@code "no"}. - * - * @since 3.12.0 - */ - public static final String NO = "no"; - - /** - * The off String {@code "off"}. - * - * @since 3.12.0 - */ - public static final String OFF = "off"; - - /** - * The on String {@code "on"}. - * - * @since 3.12.0 - */ - public static final String ON = "on"; - - /** - * The true String {@code "true"}. - * - * @since 3.12.0 - */ - public static final String TRUE = "true"; - - /** - * The yes String {@code "yes"}. - * - * @since 3.12.0 - */ - public static final String YES = "yes"; - - /** - *

- * Performs an 'and' operation on a set of booleans. - *

- * - *
-	 *   BooleanUtils.and(true, true)         = true
-	 *   BooleanUtils.and(false, false)       = false
-	 *   BooleanUtils.and(true, false)        = false
-	 *   BooleanUtils.and(true, true, false)  = false
-	 *   BooleanUtils.and(true, true, true)   = true
-	 * 
- * - * @param array an array of {@code boolean}s - * @return the result of the logical 'and' operation. That is {@code false} if - * any of the parameters is {@code false} and {@code true} otherwise. - * @throws NullPointerException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty. - * @since 3.0.1 - */ - public static boolean and(final boolean... array) { - ObjectUtils.requireNonEmpty(array, "array"); - for (final boolean element : array) { - if (!element) { - return false; - } - } - return true; - } - - /** - * Returns a new array of possible values (like an enum would). - * - * @return a new array of possible values (like an enum would). - * @since 3.12.0 - */ - public static Boolean[] booleanValues() { - return new Boolean[] { Boolean.FALSE, Boolean.TRUE }; - } - - /** - *

- * Compares two {@code boolean} values. This is the same functionality as - * provided in Java 7. - *

- * - * @param x the first {@code boolean} to compare - * @param y the second {@code boolean} to compare - * @return the value {@code 0} if {@code x == y}; a value less than {@code 0} if - * {@code !x && y}; and a value greater than {@code 0} if - * {@code x && !y} - * @since 3.4 - */ - public static int compare(final boolean x, final boolean y) { - if (x == y) { - return 0; - } - return x ? 1 : -1; - } - - /** - *

- * Checks if a {@code Boolean} value is {@code false}, handling {@code null} by - * returning {@code false}. - *

- * - *
-	 *   BooleanUtils.isFalse(Boolean.TRUE)  = false
-	 *   BooleanUtils.isFalse(Boolean.FALSE) = true
-	 *   BooleanUtils.isFalse(null)          = false
-	 * 
- * - * @param bool the boolean to check, null returns {@code false} - * @return {@code true} only if the input is non-{@code null} and {@code false} - * @since 2.1 - */ - public static boolean isFalse(final Boolean bool) { - return Boolean.FALSE.equals(bool); - } - - /** - *

- * Checks if a {@code Boolean} value is not {@code false}, handling - * {@code null} by returning {@code true}. - *

- * - *
-	 *   BooleanUtils.isNotFalse(Boolean.TRUE)  = true
-	 *   BooleanUtils.isNotFalse(Boolean.FALSE) = false
-	 *   BooleanUtils.isNotFalse(null)          = true
-	 * 
- * - * @param bool the boolean to check, null returns {@code true} - * @return {@code true} if the input is {@code null} or {@code true} - * @since 2.3 - */ - public static boolean isNotFalse(final Boolean bool) { - return !isFalse(bool); - } - - /** - *

- * Checks if a {@code Boolean} value is not {@code true}, handling - * {@code null} by returning {@code true}. - *

- * - *
-	 *   BooleanUtils.isNotTrue(Boolean.TRUE)  = false
-	 *   BooleanUtils.isNotTrue(Boolean.FALSE) = true
-	 *   BooleanUtils.isNotTrue(null)          = true
-	 * 
- * - * @param bool the boolean to check, null returns {@code true} - * @return {@code true} if the input is null or false - * @since 2.3 - */ - public static boolean isNotTrue(final Boolean bool) { - return !isTrue(bool); - } - - /** - *

- * Checks if a {@code Boolean} value is {@code true}, handling {@code null} by - * returning {@code false}. - *

- * - *
-	 *   BooleanUtils.isTrue(Boolean.TRUE)  = true
-	 *   BooleanUtils.isTrue(Boolean.FALSE) = false
-	 *   BooleanUtils.isTrue(null)          = false
-	 * 
- * - * @param bool the boolean to check, {@code null} returns {@code false} - * @return {@code true} only if the input is non-null and true - * @since 2.1 - */ - public static boolean isTrue(final Boolean bool) { - return Boolean.TRUE.equals(bool); - } - - /** - *

- * Negates the specified boolean. - *

- * - *

- * If {@code null} is passed in, {@code null} will be returned. - *

- * - *

- * NOTE: This returns {@code null} and will throw a {@code NullPointerException} - * if unboxed to a boolean. - *

- * - *
-	 * BooleanUtils.negate(Boolean.TRUE) = Boolean.FALSE;
-	 * BooleanUtils.negate(Boolean.FALSE) = Boolean.TRUE;
-	 * BooleanUtils.negate(null) = null;
-	 * 
- * - * @param bool the Boolean to negate, may be null - * @return the negated Boolean, or {@code null} if {@code null} input - */ - public static Boolean negate(final Boolean bool) { - if (bool == null) { - return null; - } - return bool.booleanValue() ? Boolean.FALSE : Boolean.TRUE; - } - - /** - *

- * Performs an 'or' operation on a set of booleans. - *

- * - *
-	 *   BooleanUtils.or(true, true)          = true
-	 *   BooleanUtils.or(false, false)        = false
-	 *   BooleanUtils.or(true, false)         = true
-	 *   BooleanUtils.or(true, true, false)   = true
-	 *   BooleanUtils.or(true, true, true)    = true
-	 *   BooleanUtils.or(false, false, false) = false
-	 * 
- * - * @param array an array of {@code boolean}s - * @return {@code true} if any of the arguments is {@code true}, and it returns - * {@code false} otherwise. - * @throws NullPointerException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty. - * @since 3.0.1 - */ - public static boolean or(final boolean... array) { - ObjectUtils.requireNonEmpty(array, "array"); - for (final boolean element : array) { - if (element) { - return true; - } - } - return false; - } - - /** - * Returns a new array of possible values (like an enum would). - * - * @return a new array of possible values (like an enum would). - * @since 3.12.0 - */ - public static boolean[] primitiveValues() { - return new boolean[] { false, true }; - } - - /** - *

- * Converts a Boolean to a boolean handling {@code null} by returning - * {@code false}. - *

- * - *
-	 *   BooleanUtils.toBoolean(Boolean.TRUE)  = true
-	 *   BooleanUtils.toBoolean(Boolean.FALSE) = false
-	 *   BooleanUtils.toBoolean(null)          = false
-	 * 
- * - * @param bool the boolean to convert - * @return {@code true} or {@code false}, {@code null} returns {@code false} - */ - public static boolean toBoolean(final Boolean bool) { - return bool != null && bool.booleanValue(); - } - - /** - *

- * Converts an int to a boolean using the convention that {@code zero} is - * {@code false}, everything else is {@code true}. - *

- * - *
-	 *   BooleanUtils.toBoolean(0) = false
-	 *   BooleanUtils.toBoolean(1) = true
-	 *   BooleanUtils.toBoolean(2) = true
-	 * 
- * - * @param value the int to convert - * @return {@code true} if non-zero, {@code false} if zero - */ - public static boolean toBoolean(final int value) { - return value != 0; - } - - /** - *

- * Converts an int to a boolean specifying the conversion values. - *

- * - *

- * If the {@code trueValue} and {@code falseValue} are the same number then the - * return value will be {@code true} in case {@code value} matches it. - *

- * - *
-	 *   BooleanUtils.toBoolean(0, 1, 0) = false
-	 *   BooleanUtils.toBoolean(1, 1, 0) = true
-	 *   BooleanUtils.toBoolean(1, 1, 1) = true
-	 *   BooleanUtils.toBoolean(2, 1, 2) = false
-	 *   BooleanUtils.toBoolean(2, 2, 0) = true
-	 * 
- * - * @param value the {@code Integer} to convert - * @param trueValue the value to match for {@code true} - * @param falseValue the value to match for {@code false} - * @return {@code true} or {@code false} - * @throws IllegalArgumentException if {@code value} does not match neither - * {@code trueValue} no {@code falseValue} - */ - public static boolean toBoolean(final int value, final int trueValue, final int falseValue) { - if (value == trueValue) { - return true; - } - if (value == falseValue) { - return false; - } - throw new IllegalArgumentException("The Integer did not match either specified value"); - } - - /** - *

- * Converts an Integer to a boolean specifying the conversion values. - *

- * - *
-	 *   BooleanUtils.toBoolean(Integer.valueOf(0), Integer.valueOf(1), Integer.valueOf(0)) = false
-	 *   BooleanUtils.toBoolean(Integer.valueOf(1), Integer.valueOf(1), Integer.valueOf(0)) = true
-	 *   BooleanUtils.toBoolean(Integer.valueOf(2), Integer.valueOf(1), Integer.valueOf(2)) = false
-	 *   BooleanUtils.toBoolean(Integer.valueOf(2), Integer.valueOf(2), Integer.valueOf(0)) = true
-	 *   BooleanUtils.toBoolean(null, null, Integer.valueOf(0))                     = true
-	 * 
- * - * @param value the Integer to convert - * @param trueValue the value to match for {@code true}, may be {@code null} - * @param falseValue the value to match for {@code false}, may be {@code null} - * @return {@code true} or {@code false} - * @throws IllegalArgumentException if no match - */ - public static boolean toBoolean(final Integer value, final Integer trueValue, final Integer falseValue) { - if (value == null) { - if (trueValue == null) { - return true; - } - if (falseValue == null) { - return false; - } - } else if (value.equals(trueValue)) { - return true; - } else if (value.equals(falseValue)) { - return false; - } - throw new IllegalArgumentException("The Integer did not match either specified value"); - } - - /** - *

- * Converts a String to a boolean (optimised for performance). - *

- * - *

- * {@code 'true'}, {@code 'on'}, {@code 'y'}, {@code 't'} or {@code 'yes'} (case - * insensitive) will return {@code true}. Otherwise, {@code false} is returned. - *

- * - *

- * This method performs 4 times faster (JDK1.4) than - * {@code Boolean.valueOf(String)}. However, this method accepts 'on' and 'yes', - * 't', 'y' as true values. - * - *

-	 *   BooleanUtils.toBoolean(null)    = false
-	 *   BooleanUtils.toBoolean("true")  = true
-	 *   BooleanUtils.toBoolean("TRUE")  = true
-	 *   BooleanUtils.toBoolean("tRUe")  = true
-	 *   BooleanUtils.toBoolean("on")    = true
-	 *   BooleanUtils.toBoolean("yes")   = true
-	 *   BooleanUtils.toBoolean("false") = false
-	 *   BooleanUtils.toBoolean("x gti") = false
-	 *   BooleanUtils.toBoolean("y") = true
-	 *   BooleanUtils.toBoolean("n") = false
-	 *   BooleanUtils.toBoolean("t") = true
-	 *   BooleanUtils.toBoolean("f") = false
-	 * 
- * - * @param str the String to check - * @return the boolean value of the string, {@code false} if no match or the - * String is null - */ - public static boolean toBoolean(final String str) { - return toBooleanObject(str) == Boolean.TRUE; - } - - /** - *

- * Converts a String to a Boolean throwing an exception if no match found. - *

- * - *
-	 *   BooleanUtils.toBoolean("true", "true", "false")  = true
-	 *   BooleanUtils.toBoolean("false", "true", "false") = false
-	 * 
- * - * @param str the String to check - * @param trueString the String to match for {@code true} (case sensitive), may - * be {@code null} - * @param falseString the String to match for {@code false} (case sensitive), - * may be {@code null} - * @return the boolean value of the string - * @throws IllegalArgumentException if the String doesn't match - */ - public static boolean toBoolean(final String str, final String trueString, final String falseString) { - if (str == trueString) { - return true; - } else if (str == falseString) { - return false; - } else if (str != null) { - if (str.equals(trueString)) { - return true; - } else if (str.equals(falseString)) { - return false; - } - } - throw new IllegalArgumentException("The String did not match either specified value"); - } - - /** - *

- * Converts a Boolean to a boolean handling {@code null}. - *

- * - *
-	 *   BooleanUtils.toBooleanDefaultIfNull(Boolean.TRUE, false)  = true
-	 *   BooleanUtils.toBooleanDefaultIfNull(Boolean.TRUE, true)   = true
-	 *   BooleanUtils.toBooleanDefaultIfNull(Boolean.FALSE, true)  = false
-	 *   BooleanUtils.toBooleanDefaultIfNull(Boolean.FALSE, false) = false
-	 *   BooleanUtils.toBooleanDefaultIfNull(null, true)           = true
-	 *   BooleanUtils.toBooleanDefaultIfNull(null, false)          = false
-	 * 
- * - * @param bool the boolean object to convert to primitive - * @param valueIfNull the boolean value to return if the parameter {@code bool} - * is {@code null} - * @return {@code true} or {@code false} - */ - public static boolean toBooleanDefaultIfNull(final Boolean bool, final boolean valueIfNull) { - if (bool == null) { - return valueIfNull; - } - return bool.booleanValue(); - } - - /** - *

- * Converts an int to a Boolean using the convention that {@code zero} is - * {@code false}, everything else is {@code true}. - *

- * - *
-	 *   BooleanUtils.toBoolean(0) = Boolean.FALSE
-	 *   BooleanUtils.toBoolean(1) = Boolean.TRUE
-	 *   BooleanUtils.toBoolean(2) = Boolean.TRUE
-	 * 
- * - * @param value the int to convert - * @return Boolean.TRUE if non-zero, Boolean.FALSE if zero, {@code null} if - * {@code null} - */ - public static Boolean toBooleanObject(final int value) { - return value == 0 ? Boolean.FALSE : Boolean.TRUE; - } - - /** - *

- * Converts an int to a Boolean specifying the conversion values. - *

- * - *

- * NOTE: This method may return {@code null} and may throw a - * {@code NullPointerException} if unboxed to a {@code boolean}. - *

- * - *

- * The checks are done first for the {@code trueValue}, then for the - * {@code falseValue} and finally for the {@code nullValue}. - *

- * - *
-	 *   BooleanUtils.toBooleanObject(0, 0, 2, 3) = Boolean.TRUE
-	 *   BooleanUtils.toBooleanObject(0, 0, 0, 3) = Boolean.TRUE
-	 *   BooleanUtils.toBooleanObject(0, 0, 0, 0) = Boolean.TRUE
-	 *   BooleanUtils.toBooleanObject(2, 1, 2, 3) = Boolean.FALSE
-	 *   BooleanUtils.toBooleanObject(2, 1, 2, 2) = Boolean.FALSE
-	 *   BooleanUtils.toBooleanObject(3, 1, 2, 3) = null
-	 * 
- * - * @param value the Integer to convert - * @param trueValue the value to match for {@code true} - * @param falseValue the value to match for {@code false} - * @param nullValue the value to to match for {@code null} - * @return Boolean.TRUE, Boolean.FALSE, or {@code null} - * @throws IllegalArgumentException if no match - */ - public static Boolean toBooleanObject(final int value, final int trueValue, final int falseValue, - final int nullValue) { - if (value == trueValue) { - return Boolean.TRUE; - } - if (value == falseValue) { - return Boolean.FALSE; - } - if (value == nullValue) { - return null; - } - throw new IllegalArgumentException("The Integer did not match any specified value"); - } - - /** - *

- * Converts an Integer to a Boolean using the convention that {@code zero} is - * {@code false}, every other numeric value is {@code true}. - *

- * - *

- * {@code null} will be converted to {@code null}. - *

- * - *

- * NOTE: This method may return {@code null} and may throw a - * {@code NullPointerException} if unboxed to a {@code boolean}. - *

- * - *
-	 *   BooleanUtils.toBooleanObject(Integer.valueOf(0))    = Boolean.FALSE
-	 *   BooleanUtils.toBooleanObject(Integer.valueOf(1))    = Boolean.TRUE
-	 *   BooleanUtils.toBooleanObject(Integer.valueOf(null)) = null
-	 * 
- * - * @param value the Integer to convert - * @return Boolean.TRUE if non-zero, Boolean.FALSE if zero, {@code null} if - * {@code null} input - */ - public static Boolean toBooleanObject(final Integer value) { - if (value == null) { - return null; - } - return value.intValue() == 0 ? Boolean.FALSE : Boolean.TRUE; - } - - /** - *

- * Converts an Integer to a Boolean specifying the conversion values. - *

- * - *

- * NOTE: This method may return {@code null} and may throw a - * {@code NullPointerException} if unboxed to a {@code boolean}. - *

- * - *

- * The checks are done first for the {@code trueValue}, then for the - * {@code falseValue} and finally for the {@code nullValue}. - *

- ** - *
-	 *   BooleanUtils.toBooleanObject(Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(2), Integer.valueOf(3)) = Boolean.TRUE
-	 *   BooleanUtils.toBooleanObject(Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(3)) = Boolean.TRUE
-	 *   BooleanUtils.toBooleanObject(Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0)) = Boolean.TRUE
-	 *   BooleanUtils.toBooleanObject(Integer.valueOf(2), Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3)) = Boolean.FALSE
-	 *   BooleanUtils.toBooleanObject(Integer.valueOf(2), Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(2)) = Boolean.FALSE
-	 *   BooleanUtils.toBooleanObject(Integer.valueOf(3), Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3)) = null
-	 * 
- * - * @param value the Integer to convert - * @param trueValue the value to match for {@code true}, may be {@code null} - * @param falseValue the value to match for {@code false}, may be {@code null} - * @param nullValue the value to to match for {@code null}, may be {@code null} - * @return Boolean.TRUE, Boolean.FALSE, or {@code null} - * @throws IllegalArgumentException if no match - */ - public static Boolean toBooleanObject(final Integer value, final Integer trueValue, final Integer falseValue, - final Integer nullValue) { - if (value == null) { - if (trueValue == null) { - return Boolean.TRUE; - } - if (falseValue == null) { - return Boolean.FALSE; - } - if (nullValue == null) { - return null; - } - } else if (value.equals(trueValue)) { - return Boolean.TRUE; - } else if (value.equals(falseValue)) { - return Boolean.FALSE; - } else if (value.equals(nullValue)) { - return null; - } - throw new IllegalArgumentException("The Integer did not match any specified value"); - } - - /** - *

- * Converts a String to a Boolean. - *

- * - *

- * {@code 'true'}, {@code 'on'}, {@code 'y'}, {@code 't'}, {@code 'yes'} or - * {@code '1'} (case insensitive) will return {@code true}. {@code 'false'}, - * {@code 'off'}, {@code 'n'}, {@code 'f'}, {@code 'no'} or {@code '0'} (case - * insensitive) will return {@code false}. Otherwise, {@code null} is returned. - *

- * - *

- * NOTE: This method may return {@code null} and may throw a - * {@code NullPointerException} if unboxed to a {@code boolean}. - *

- * - *
-	 *   // N.B. case is not significant
-	 *   BooleanUtils.toBooleanObject(null)    = null
-	 *   BooleanUtils.toBooleanObject("true")  = Boolean.TRUE
-	 *   BooleanUtils.toBooleanObject("T")     = Boolean.TRUE // i.e. T[RUE]
-	 *   BooleanUtils.toBooleanObject("false") = Boolean.FALSE
-	 *   BooleanUtils.toBooleanObject("f")     = Boolean.FALSE // i.e. f[alse]
-	 *   BooleanUtils.toBooleanObject("No")    = Boolean.FALSE
-	 *   BooleanUtils.toBooleanObject("n")     = Boolean.FALSE // i.e. n[o]
-	 *   BooleanUtils.toBooleanObject("on")    = Boolean.TRUE
-	 *   BooleanUtils.toBooleanObject("ON")    = Boolean.TRUE
-	 *   BooleanUtils.toBooleanObject("off")   = Boolean.FALSE
-	 *   BooleanUtils.toBooleanObject("oFf")   = Boolean.FALSE
-	 *   BooleanUtils.toBooleanObject("yes")   = Boolean.TRUE
-	 *   BooleanUtils.toBooleanObject("Y")     = Boolean.TRUE // i.e. Y[ES]
-	 *   BooleanUtils.toBooleanObject("1")     = Boolean.TRUE
-	 *   BooleanUtils.toBooleanObject("0")     = Boolean.FALSE
-	 *   BooleanUtils.toBooleanObject("blue")  = null
-	 *   BooleanUtils.toBooleanObject("true ") = null // trailing space (too long)
-	 *   BooleanUtils.toBooleanObject("ono")   = null // does not match on or no
-	 * 
- * - * @param str the String to check; upper and lower case are treated as the same - * @return the Boolean value of the string, {@code null} if no match or - * {@code null} input - */ - public static Boolean toBooleanObject(final String str) { - // Previously used equalsIgnoreCase, which was fast for interned 'true'. - // Non interned 'true' matched 15 times slower. - // - // Optimisation provides same performance as before for interned 'true'. - // Similar performance for null, 'false', and other strings not length 2/3/4. - // 'true'/'TRUE' match 4 times slower, 'tRUE'/'True' 7 times slower. - if (str == TRUE) { - return Boolean.TRUE; - } - if (str == null) { - return null; - } - switch (str.length()) { - case 1: { - final char ch0 = str.charAt(0); - if (ch0 == 'y' || ch0 == 'Y' || ch0 == 't' || ch0 == 'T' || ch0 == '1') { - return Boolean.TRUE; - } - if (ch0 == 'n' || ch0 == 'N' || ch0 == 'f' || ch0 == 'F' || ch0 == '0') { - return Boolean.FALSE; - } - break; - } - case 2: { - final char ch0 = str.charAt(0); - final char ch1 = str.charAt(1); - if ((ch0 == 'o' || ch0 == 'O') && (ch1 == 'n' || ch1 == 'N')) { - return Boolean.TRUE; - } - if ((ch0 == 'n' || ch0 == 'N') && (ch1 == 'o' || ch1 == 'O')) { - return Boolean.FALSE; - } - break; - } - case 3: { - final char ch0 = str.charAt(0); - final char ch1 = str.charAt(1); - final char ch2 = str.charAt(2); - if ((ch0 == 'y' || ch0 == 'Y') && (ch1 == 'e' || ch1 == 'E') && (ch2 == 's' || ch2 == 'S')) { - return Boolean.TRUE; - } - if ((ch0 == 'o' || ch0 == 'O') && (ch1 == 'f' || ch1 == 'F') && (ch2 == 'f' || ch2 == 'F')) { - return Boolean.FALSE; - } - break; - } - case 4: { - final char ch0 = str.charAt(0); - final char ch1 = str.charAt(1); - final char ch2 = str.charAt(2); - final char ch3 = str.charAt(3); - if ((ch0 == 't' || ch0 == 'T') && (ch1 == 'r' || ch1 == 'R') && (ch2 == 'u' || ch2 == 'U') - && (ch3 == 'e' || ch3 == 'E')) { - return Boolean.TRUE; - } - break; - } - case 5: { - final char ch0 = str.charAt(0); - final char ch1 = str.charAt(1); - final char ch2 = str.charAt(2); - final char ch3 = str.charAt(3); - final char ch4 = str.charAt(4); - if ((ch0 == 'f' || ch0 == 'F') && (ch1 == 'a' || ch1 == 'A') && (ch2 == 'l' || ch2 == 'L') - && (ch3 == 's' || ch3 == 'S') && (ch4 == 'e' || ch4 == 'E')) { - return Boolean.FALSE; - } - break; - } - default: - break; - } - - return null; - } - - /** - *

- * Converts a String to a Boolean throwing an exception if no match. - *

- * - *

- * NOTE: This method may return {@code null} and may throw a - * {@code NullPointerException} if unboxed to a {@code boolean}. - *

- * - *
-	 *   BooleanUtils.toBooleanObject("true", "true", "false", "null")   = Boolean.TRUE
-	 *   BooleanUtils.toBooleanObject(null, null, "false", "null")       = Boolean.TRUE
-	 *   BooleanUtils.toBooleanObject(null, null, null, "null")          = Boolean.TRUE
-	 *   BooleanUtils.toBooleanObject(null, null, null, null)            = Boolean.TRUE
-	 *   BooleanUtils.toBooleanObject("false", "true", "false", "null")  = Boolean.FALSE
-	 *   BooleanUtils.toBooleanObject("false", "true", "false", "false") = Boolean.FALSE
-	 *   BooleanUtils.toBooleanObject(null, "true", null, "false")       = Boolean.FALSE
-	 *   BooleanUtils.toBooleanObject(null, "true", null, null)          = Boolean.FALSE
-	 *   BooleanUtils.toBooleanObject("null", "true", "false", "null")   = null
-	 * 
- * - * @param str the String to check - * @param trueString the String to match for {@code true} (case sensitive), may - * be {@code null} - * @param falseString the String to match for {@code false} (case sensitive), - * may be {@code null} - * @param nullString the String to match for {@code null} (case sensitive), may - * be {@code null} - * @return the Boolean value of the string, {@code null} if either the String - * matches {@code nullString} or if {@code null} input and - * {@code nullString} is {@code null} - * @throws IllegalArgumentException if the String doesn't match - */ - public static Boolean toBooleanObject(final String str, final String trueString, final String falseString, - final String nullString) { - if (str == null) { - if (trueString == null) { - return Boolean.TRUE; - } - if (falseString == null) { - return Boolean.FALSE; - } - if (nullString == null) { - return null; - } - } else if (str.equals(trueString)) { - return Boolean.TRUE; - } else if (str.equals(falseString)) { - return Boolean.FALSE; - } else if (str.equals(nullString)) { - return null; - } - // no match - throw new IllegalArgumentException("The String did not match any specified value"); - } - - /** - *

- * Converts a boolean to an int using the convention that {@code true} is - * {@code 1} and {@code false} is {@code 0}. - *

- * - *
-	 *   BooleanUtils.toInteger(true)  = 1
-	 *   BooleanUtils.toInteger(false) = 0
-	 * 
- * - * @param bool the boolean to convert - * @return one if {@code true}, zero if {@code false} - */ - public static int toInteger(final boolean bool) { - return bool ? 1 : 0; - } - - /** - *

- * Converts a boolean to an int specifying the conversion values. - *

- * - *
-	 *   BooleanUtils.toInteger(true, 1, 0)  = 1
-	 *   BooleanUtils.toInteger(false, 1, 0) = 0
-	 * 
- * - * @param bool the to convert - * @param trueValue the value to return if {@code true} - * @param falseValue the value to return if {@code false} - * @return the appropriate value - */ - public static int toInteger(final boolean bool, final int trueValue, final int falseValue) { - return bool ? trueValue : falseValue; - } - - /** - *

- * Converts a Boolean to an int specifying the conversion values. - *

- * - *
-	 *   BooleanUtils.toInteger(Boolean.TRUE, 1, 0, 2)  = 1
-	 *   BooleanUtils.toInteger(Boolean.FALSE, 1, 0, 2) = 0
-	 *   BooleanUtils.toInteger(null, 1, 0, 2)          = 2
-	 * 
- * - * @param bool the Boolean to convert - * @param trueValue the value to return if {@code true} - * @param falseValue the value to return if {@code false} - * @param nullValue the value to return if {@code null} - * @return the appropriate value - */ - public static int toInteger(final Boolean bool, final int trueValue, final int falseValue, final int nullValue) { - if (bool == null) { - return nullValue; - } - return bool.booleanValue() ? trueValue : falseValue; - } - - /** - *

- * Converts a boolean to an Integer using the convention that {@code true} is - * {@code 1} and {@code false} is {@code 0}. - *

- * - *
-	 *   BooleanUtils.toIntegerObject(true)  = Integer.valueOf(1)
-	 *   BooleanUtils.toIntegerObject(false) = Integer.valueOf(0)
-	 * 
- * - * @param bool the boolean to convert - * @return one if {@code true}, zero if {@code false} - */ - public static Integer toIntegerObject(final boolean bool) { - return bool ? NumberUtils.INTEGER_ONE : NumberUtils.INTEGER_ZERO; - } - - /** - *

- * Converts a boolean to an Integer specifying the conversion values. - *

- * - *
-	 *   BooleanUtils.toIntegerObject(true, Integer.valueOf(1), Integer.valueOf(0))  = Integer.valueOf(1)
-	 *   BooleanUtils.toIntegerObject(false, Integer.valueOf(1), Integer.valueOf(0)) = Integer.valueOf(0)
-	 * 
- * - * @param bool the to convert - * @param trueValue the value to return if {@code true}, may be {@code null} - * @param falseValue the value to return if {@code false}, may be {@code null} - * @return the appropriate value - */ - public static Integer toIntegerObject(final boolean bool, final Integer trueValue, final Integer falseValue) { - return bool ? trueValue : falseValue; - } - - /** - *

- * Converts a Boolean to a Integer using the convention that {@code zero} is - * {@code false}. - *

- * - *

- * {@code null} will be converted to {@code null}. - *

- * - *
-	 *   BooleanUtils.toIntegerObject(Boolean.TRUE)  = Integer.valueOf(1)
-	 *   BooleanUtils.toIntegerObject(Boolean.FALSE) = Integer.valueOf(0)
-	 * 
- * - * @param bool the Boolean to convert - * @return one if Boolean.TRUE, zero if Boolean.FALSE, {@code null} if - * {@code null} - */ - public static Integer toIntegerObject(final Boolean bool) { - if (bool == null) { - return null; - } - return bool.booleanValue() ? NumberUtils.INTEGER_ONE : NumberUtils.INTEGER_ZERO; - } - - /** - *

- * Converts a Boolean to an Integer specifying the conversion values. - *

- * - *
-	 *   BooleanUtils.toIntegerObject(Boolean.TRUE, Integer.valueOf(1), Integer.valueOf(0), Integer.valueOf(2))  = Integer.valueOf(1)
-	 *   BooleanUtils.toIntegerObject(Boolean.FALSE, Integer.valueOf(1), Integer.valueOf(0), Integer.valueOf(2)) = Integer.valueOf(0)
-	 *   BooleanUtils.toIntegerObject(null, Integer.valueOf(1), Integer.valueOf(0), Integer.valueOf(2))          = Integer.valueOf(2)
-	 * 
- * - * @param bool the Boolean to convert - * @param trueValue the value to return if {@code true}, may be {@code null} - * @param falseValue the value to return if {@code false}, may be {@code null} - * @param nullValue the value to return if {@code null}, may be {@code null} - * @return the appropriate value - */ - public static Integer toIntegerObject(final Boolean bool, final Integer trueValue, final Integer falseValue, - final Integer nullValue) { - if (bool == null) { - return nullValue; - } - return bool.booleanValue() ? trueValue : falseValue; - } - - /** - *

- * Converts a boolean to a String returning one of the input Strings. - *

- * - *
-	 *   BooleanUtils.toString(true, "true", "false")   = "true"
-	 *   BooleanUtils.toString(false, "true", "false")  = "false"
-	 * 
- * - * @param bool the Boolean to check - * @param trueString the String to return if {@code true}, may be {@code null} - * @param falseString the String to return if {@code false}, may be {@code null} - * @return one of the two input Strings - */ - public static String toString(final boolean bool, final String trueString, final String falseString) { - return bool ? trueString : falseString; - } - - /** - *

- * Converts a Boolean to a String returning one of the input Strings. - *

- * - *
-	 *   BooleanUtils.toString(Boolean.TRUE, "true", "false", null)   = "true"
-	 *   BooleanUtils.toString(Boolean.FALSE, "true", "false", null)  = "false"
-	 *   BooleanUtils.toString(null, "true", "false", null)           = null;
-	 * 
- * - * @param bool the Boolean to check - * @param trueString the String to return if {@code true}, may be {@code null} - * @param falseString the String to return if {@code false}, may be {@code null} - * @param nullString the String to return if {@code null}, may be {@code null} - * @return one of the three input Strings - */ - public static String toString(final Boolean bool, final String trueString, final String falseString, - final String nullString) { - if (bool == null) { - return nullString; - } - return bool.booleanValue() ? trueString : falseString; - } - - /** - *

- * Converts a boolean to a String returning {@code 'on'} or {@code 'off'}. - *

- * - *
-	 *   BooleanUtils.toStringOnOff(true)   = "on"
-	 *   BooleanUtils.toStringOnOff(false)  = "off"
-	 * 
- * - * @param bool the Boolean to check - * @return {@code 'on'}, {@code 'off'}, or {@code null} - */ - public static String toStringOnOff(final boolean bool) { - return toString(bool, ON, OFF); - } - - /** - *

- * Converts a Boolean to a String returning {@code 'on'}, {@code 'off'}, or - * {@code null}. - *

- * - *
-	 *   BooleanUtils.toStringOnOff(Boolean.TRUE)  = "on"
-	 *   BooleanUtils.toStringOnOff(Boolean.FALSE) = "off"
-	 *   BooleanUtils.toStringOnOff(null)          = null;
-	 * 
- * - * @param bool the Boolean to check - * @return {@code 'on'}, {@code 'off'}, or {@code null} - */ - public static String toStringOnOff(final Boolean bool) { - return toString(bool, ON, OFF, null); - } - - /** - *

- * Converts a boolean to a String returning {@code 'true'} or {@code 'false'}. - *

- * - *
-	 *   BooleanUtils.toStringTrueFalse(true)   = "true"
-	 *   BooleanUtils.toStringTrueFalse(false)  = "false"
-	 * 
- * - * @param bool the Boolean to check - * @return {@code 'true'}, {@code 'false'}, or {@code null} - */ - public static String toStringTrueFalse(final boolean bool) { - return toString(bool, TRUE, FALSE); - } - - /** - *

- * Converts a Boolean to a String returning {@code 'true'}, {@code 'false'}, or - * {@code null}. - *

- * - *
-	 *   BooleanUtils.toStringTrueFalse(Boolean.TRUE)  = "true"
-	 *   BooleanUtils.toStringTrueFalse(Boolean.FALSE) = "false"
-	 *   BooleanUtils.toStringTrueFalse(null)          = null;
-	 * 
- * - * @param bool the Boolean to check - * @return {@code 'true'}, {@code 'false'}, or {@code null} - */ - public static String toStringTrueFalse(final Boolean bool) { - return toString(bool, TRUE, FALSE, null); - } - - /** - *

- * Converts a boolean to a String returning {@code 'yes'} or {@code 'no'}. - *

- * - *
-	 *   BooleanUtils.toStringYesNo(true)   = "yes"
-	 *   BooleanUtils.toStringYesNo(false)  = "no"
-	 * 
- * - * @param bool the Boolean to check - * @return {@code 'yes'}, {@code 'no'}, or {@code null} - */ - public static String toStringYesNo(final boolean bool) { - return toString(bool, YES, NO); - } - - /** - *

- * Converts a Boolean to a String returning {@code 'yes'}, {@code 'no'}, or - * {@code null}. - *

- * - *
-	 *   BooleanUtils.toStringYesNo(Boolean.TRUE)  = "yes"
-	 *   BooleanUtils.toStringYesNo(Boolean.FALSE) = "no"
-	 *   BooleanUtils.toStringYesNo(null)          = null;
-	 * 
- * - * @param bool the Boolean to check - * @return {@code 'yes'}, {@code 'no'}, or {@code null} - */ - public static String toStringYesNo(final Boolean bool) { - return toString(bool, YES, NO, null); - } - - /** - *

- * Performs an xor on a set of booleans. - *

- * - *
-	 *   BooleanUtils.xor(true, true)   = false
-	 *   BooleanUtils.xor(false, false) = false
-	 *   BooleanUtils.xor(true, false)  = true
-	 * 
- * - * @param array an array of {@code boolean}s - * @return the result of the xor operations - * @throws NullPointerException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty. - */ - public static boolean xor(final boolean... array) { - ObjectUtils.requireNonEmpty(array, "array"); - // false if the neutral element of the xor operator - boolean result = false; - for (final boolean element : array) { - result ^= element; - } - - return result; - } - - /** - *

- * {@code BooleanUtils} instances should NOT be constructed in standard - * programming. Instead, the class should be used as - * {@code BooleanUtils.negate(true);}. - *

- * - *

- * This constructor is public to permit tools that require a JavaBean instance - * to operate. - *

- */ - public BooleanUtils() { - } - -} diff --git a/src/main/java/org/apache/commons/lang3/CharEncoding.java b/src/main/java/org/apache/commons/lang3/CharEncoding.java deleted file mode 100755 index 4141f3a3..00000000 --- a/src/main/java/org/apache/commons/lang3/CharEncoding.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3; - -import java.nio.charset.Charset; -import java.nio.charset.IllegalCharsetNameException; - -/** - *

- * Character encoding names required of every implementation of the Java - * platform. - *

- * - *

- * According to JRE - * character encoding names: - *

- * - *

- * Every implementation of the Java platform is required to support the - * following character encodings. Consult the release documentation for your - * implementation to see if any other encodings are supported. - *

- * - * @see JRE - * character encoding names - * @since 2.1 - * @deprecated Java 7 introduced {@link java.nio.charset.StandardCharsets}, - * which defines these constants as {@link Charset} objects. Use - * {@link Charset#name()} to get the string values provided in this - * class. This class will be removed in a future release. - */ -@Deprecated -public class CharEncoding { - - /** - *

- * ISO Latin Alphabet #1, also known as ISO-LATIN-1. - *

- * - *

- * Every implementation of the Java platform is required to support this - * character encoding. - *

- */ - public static final String ISO_8859_1 = "ISO-8859-1"; - - /** - *

- * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block - * of the Unicode character set. - *

- * - *

- * Every implementation of the Java platform is required to support this - * character encoding. - *

- */ - public static final String US_ASCII = "US-ASCII"; - - /** - *

- * Sixteen-bit Unicode Transformation Format, byte order specified by a - * mandatory initial byte-order mark (either order accepted on input, big-endian - * used on output). - *

- * - *

- * Every implementation of the Java platform is required to support this - * character encoding. - *

- */ - public static final String UTF_16 = "UTF-16"; - - /** - *

- * Sixteen-bit Unicode Transformation Format, big-endian byte order. - *

- * - *

- * Every implementation of the Java platform is required to support this - * character encoding. - *

- */ - public static final String UTF_16BE = "UTF-16BE"; - - /** - *

- * Sixteen-bit Unicode Transformation Format, little-endian byte order. - *

- * - *

- * Every implementation of the Java platform is required to support this - * character encoding. - *

- */ - public static final String UTF_16LE = "UTF-16LE"; - - /** - *

- * Eight-bit Unicode Transformation Format. - *

- * - *

- * Every implementation of the Java platform is required to support this - * character encoding. - *

- */ - public static final String UTF_8 = "UTF-8"; - - /** - *

- * Returns whether the named charset is supported. - *

- * - *

- * This is similar to - * java.nio.charset.Charset.isSupported(String) but handles more formats - *

- * - * @param name the name of the requested charset; may be either a canonical name - * or an alias, null returns false - * @return {@code true} if the charset is available in the current Java virtual - * machine - * @deprecated Please use {@link Charset#isSupported(String)} instead, although - * be aware that {@code null} values are not accepted by that method - * and an {@link IllegalCharsetNameException} may be thrown. - */ - @Deprecated - public static boolean isSupported(final String name) { - if (name == null) { - return false; - } - try { - return Charset.isSupported(name); - } catch (final IllegalCharsetNameException ex) { - return false; - } - } - -} diff --git a/src/main/java/org/apache/commons/lang3/CharRange.java b/src/main/java/org/apache/commons/lang3/CharRange.java deleted file mode 100755 index 92baae21..00000000 --- a/src/main/java/org/apache/commons/lang3/CharRange.java +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import java.io.Serializable; -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - *

- * A contiguous range of characters, optionally negated. - *

- * - *

- * Instances are immutable. - *

- * - *

- * #ThreadSafe# - *

- * - * @since 1.0 - */ -// TODO: This is no longer public and will be removed later as CharSet is moved -// to depend on Range. -final class CharRange implements Iterable, Serializable { - - /** - * Required for serialization support. Lang version 2.0. - * - * @see java.io.Serializable - */ - private static final long serialVersionUID = 8270183163158333422L; - - /** The first character, inclusive, in the range. */ - private final char start; - /** The last character, inclusive, in the range. */ - private final char end; - /** True if the range is everything except the characters specified. */ - private final boolean negated; - - /** Cached toString. */ - private transient String iToString; - - /** Empty array. */ - static final CharRange[] EMPTY_ARRAY = new CharRange[0]; - - /** - *

- * Constructs a {@code CharRange} over a set of characters, optionally negating - * the range. - *

- * - *

- * A negated range includes everything except that defined by the start and end - * characters. - *

- * - *

- * If start and end are in the wrong order, they are reversed. Thus {@code a-e} - * is the same as {@code e-a}. - *

- * - * @param start first character, inclusive, in this range - * @param end last character, inclusive, in this range - * @param negated true to express everything except the range - */ - private CharRange(char start, char end, final boolean negated) { - if (start > end) { - final char temp = start; - start = end; - end = temp; - } - - this.start = start; - this.end = end; - this.negated = negated; - } - - /** - *

- * Constructs a {@code CharRange} over a single character. - *

- * - * @param ch only character in this range - * @return the new CharRange object - * @since 2.5 - */ - public static CharRange is(final char ch) { - return new CharRange(ch, ch, false); - } - - /** - *

- * Constructs a negated {@code CharRange} over a single character. - *

- * - *

- * A negated range includes everything except that defined by the single - * character. - *

- * - * @param ch only character in this range - * @return the new CharRange object - * @since 2.5 - */ - public static CharRange isNot(final char ch) { - return new CharRange(ch, ch, true); - } - - /** - *

- * Constructs a {@code CharRange} over a set of characters. - *

- * - *

- * If start and end are in the wrong order, they are reversed. Thus {@code a-e} - * is the same as {@code e-a}. - *

- * - * @param start first character, inclusive, in this range - * @param end last character, inclusive, in this range - * @return the new CharRange object - * @since 2.5 - */ - public static CharRange isIn(final char start, final char end) { - return new CharRange(start, end, false); - } - - /** - *

- * Constructs a negated {@code CharRange} over a set of characters. - *

- * - *

- * A negated range includes everything except that defined by the start and end - * characters. - *

- * - *

- * If start and end are in the wrong order, they are reversed. Thus {@code a-e} - * is the same as {@code e-a}. - *

- * - * @param start first character, inclusive, in this range - * @param end last character, inclusive, in this range - * @return the new CharRange object - * @since 2.5 - */ - public static CharRange isNotIn(final char start, final char end) { - return new CharRange(start, end, true); - } - - // Accessors - // ----------------------------------------------------------------------- - /** - *

- * Gets the start character for this character range. - *

- * - * @return the start char (inclusive) - */ - public char getStart() { - return this.start; - } - - /** - *

- * Gets the end character for this character range. - *

- * - * @return the end char (inclusive) - */ - public char getEnd() { - return this.end; - } - - /** - *

- * Is this {@code CharRange} negated. - *

- * - *

- * A negated range includes everything except that defined by the start and end - * characters. - *

- * - * @return {@code true} if negated - */ - public boolean isNegated() { - return negated; - } - - // Contains - // ----------------------------------------------------------------------- - /** - *

- * Is the character specified contained in this range. - *

- * - * @param ch the character to check - * @return {@code true} if this range contains the input character - */ - public boolean contains(final char ch) { - return (ch >= start && ch <= end) != negated; - } - - /** - *

- * Are all the characters of the passed in range contained in this range. - *

- * - * @param range the range to check against - * @return {@code true} if this range entirely contains the input range - * @throws IllegalArgumentException if {@code null} input - */ - public boolean contains(final CharRange range) { - Validate.notNull(range, "range"); - if (negated) { - if (range.negated) { - return start >= range.start && end <= range.end; - } - return range.end < start || range.start > end; - } - if (range.negated) { - return start == 0 && end == Character.MAX_VALUE; - } - return start <= range.start && end >= range.end; - } - - // Basics - // ----------------------------------------------------------------------- - /** - *

- * Compares two CharRange objects, returning true if they represent exactly the - * same range of characters defined in the same way. - *

- * - * @param obj the object to compare to - * @return true if equal - */ - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof CharRange)) { - return false; - } - final CharRange other = (CharRange) obj; - return start == other.start && end == other.end && negated == other.negated; - } - - /** - *

- * Gets a hashCode compatible with the equals method. - *

- * - * @return a suitable hashCode - */ - @Override - public int hashCode() { - return 83 + start + 7 * end + (negated ? 1 : 0); - } - - /** - *

- * Gets a string representation of the character range. - *

- * - * @return string representation of this range - */ - @Override - public String toString() { - if (iToString == null) { - final StringBuilder buf = new StringBuilder(4); - if (isNegated()) { - buf.append('^'); - } - buf.append(start); - if (start != end) { - buf.append('-'); - buf.append(end); - } - iToString = buf.toString(); - } - return iToString; - } - - // Expansions - // ----------------------------------------------------------------------- - /** - *

- * Returns an iterator which can be used to walk through the characters - * described by this range. - *

- * - *

- * #NotThreadSafe# the iterator is not thread-safe - *

- * - * @return an iterator to the chars represented by this range - * @since 2.5 - */ - @Override - public Iterator iterator() { - return new CharacterIterator(this); - } - - /** - * Character {@link Iterator}. - *

- * #NotThreadSafe# - *

- */ - private static class CharacterIterator implements Iterator { - /** The current character */ - private char current; - - private final CharRange range; - private boolean hasNext; - - /** - * Constructs a new iterator for the character range. - * - * @param r The character range - */ - private CharacterIterator(final CharRange r) { - range = r; - hasNext = true; - - if (range.negated) { - if (range.start == 0) { - if (range.end == Character.MAX_VALUE) { - // This range is an empty set - hasNext = false; - } else { - current = (char) (range.end + 1); - } - } else { - current = 0; - } - } else { - current = range.start; - } - } - - /** - * Prepares the next character in the range. - */ - private void prepareNext() { - if (range.negated) { - if (current == Character.MAX_VALUE) { - hasNext = false; - } else if (current + 1 == range.start) { - if (range.end == Character.MAX_VALUE) { - hasNext = false; - } else { - current = (char) (range.end + 1); - } - } else { - current = (char) (current + 1); - } - } else if (current < range.end) { - current = (char) (current + 1); - } else { - hasNext = false; - } - } - - /** - * Has the iterator not reached the end character yet? - * - * @return {@code true} if the iterator has yet to reach the character date - */ - @Override - public boolean hasNext() { - return hasNext; - } - - /** - * Returns the next character in the iteration - * - * @return {@code Character} for the next character - */ - @Override - public Character next() { - if (!hasNext) { - throw new NoSuchElementException(); - } - final char cur = current; - prepareNext(); - return Character.valueOf(cur); - } - - /** - * Always throws UnsupportedOperationException. - * - * @throws UnsupportedOperationException Always thrown. - * @see java.util.Iterator#remove() - */ - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - } -} diff --git a/src/main/java/org/apache/commons/lang3/CharSequenceUtils.java b/src/main/java/org/apache/commons/lang3/CharSequenceUtils.java deleted file mode 100755 index 37e1796e..00000000 --- a/src/main/java/org/apache/commons/lang3/CharSequenceUtils.java +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -/** - *

- * Operations on {@link CharSequence} that are {@code null} safe. - *

- * - * @see CharSequence - * @since 3.0 - */ -public class CharSequenceUtils { - - private static final int NOT_FOUND = -1; - - /** - *

- * {@code CharSequenceUtils} instances should NOT be constructed in standard - * programming. - *

- * - *

- * This constructor is public to permit tools that require a JavaBean instance - * to operate. - *

- */ - public CharSequenceUtils() { - } - - // ----------------------------------------------------------------------- - /** - *

- * Returns a new {@code CharSequence} that is a subsequence of this sequence - * starting with the {@code char} value at the specified index. - *

- * - *

- * This provides the {@code CharSequence} equivalent to - * {@link String#substring(int)}. The length (in {@code char}) of the returned - * sequence is {@code length() - start}, so if {@code start == end} then an - * empty sequence is returned. - *

- * - * @param cs the specified subsequence, null returns null - * @param start the start index, inclusive, valid - * @return a new subsequence, may be null - * @throws IndexOutOfBoundsException if {@code start} is negative or if - * {@code start} is greater than - * {@code length()} - */ - public static CharSequence subSequence(final CharSequence cs, final int start) { - return cs == null ? null : cs.subSequence(start, cs.length()); - } - - // ----------------------------------------------------------------------- - /** - * Returns the index within {@code cs} of the first occurrence of the specified - * character, starting the search at the specified index. - *

- * If a character with value {@code searchChar} occurs in the character sequence - * represented by the {@code cs} object at an index no smaller than - * {@code start}, then the index of the first such occurrence is returned. For - * values of {@code searchChar} in the range from 0 to 0xFFFF (inclusive), this - * is the smallest value k such that:

- * - *
-	 * (this.charAt(k) == searchChar) && (k >= start)
-	 * 
- * - *
is true. For other values of {@code searchChar}, it is the - * smallest value k such that:
- * - *
-	 * (this.codePointAt(k) == searchChar) && (k >= start)
-	 * 
- * - *
is true. In either case, if no such character occurs inm - * {@code cs} at or after position {@code start}, then {@code -1} is returned. - * - *

- * There is no restriction on the value of {@code start}. If it is negative, it - * has the same effect as if it were zero: the entire {@code CharSequence} may - * be searched. If it is greater than the length of {@code cs}, it has the same - * effect as if it were equal to the length of {@code cs}: {@code -1} is - * returned. - * - *

- * All indices are specified in {@code char} values (Unicode code units). - * - * @param cs the {@code CharSequence} to be processed, not null - * @param searchChar the char to be searched for - * @param start the start index, negative starts at the string start - * @return the index where the search char was found, -1 if not found - * @since 3.6 updated to behave more like {@code String} - */ - static int indexOf(final CharSequence cs, final int searchChar, int start) { - if (cs instanceof String) { - return ((String) cs).indexOf(searchChar, start); - } - final int sz = cs.length(); - if (start < 0) { - start = 0; - } - if (searchChar < Character.MIN_SUPPLEMENTARY_CODE_POINT) { - for (int i = start; i < sz; i++) { - if (cs.charAt(i) == searchChar) { - return i; - } - } - return NOT_FOUND; - } - // supplementary characters (LANG1300) - if (searchChar <= Character.MAX_CODE_POINT) { - final char[] chars = Character.toChars(searchChar); - for (int i = start; i < sz - 1; i++) { - final char high = cs.charAt(i); - final char low = cs.charAt(i + 1); - if (high == chars[0] && low == chars[1]) { - return i; - } - } - } - return NOT_FOUND; - } - - /** - * Used by the indexOf(CharSequence methods) as a green implementation of - * indexOf. - * - * @param cs the {@code CharSequence} to be processed - * @param searchChar the {@code CharSequence} to be searched for - * @param start the start index - * @return the index where the search sequence was found - */ - static int indexOf(final CharSequence cs, final CharSequence searchChar, final int start) { - if (cs instanceof String) { - return ((String) cs).indexOf(searchChar.toString(), start); - } else if (cs instanceof StringBuilder) { - return ((StringBuilder) cs).indexOf(searchChar.toString(), start); - } else if (cs instanceof StringBuffer) { - return ((StringBuffer) cs).indexOf(searchChar.toString(), start); - } - return cs.toString().indexOf(searchChar.toString(), start); -// if (cs instanceof String && searchChar instanceof String) { -// // TODO: Do we assume searchChar is usually relatively small; -// // If so then calling toString() on it is better than reverting to -// // the green implementation in the else block -// return ((String) cs).indexOf((String) searchChar, start); -// } else { -// // TODO: Implement rather than convert to String -// return cs.toString().indexOf(searchChar.toString(), start); -// } - } - - /** - * Returns the index within {@code cs} of the last occurrence of the specified - * character, searching backward starting at the specified index. For values of - * {@code searchChar} in the range from 0 to 0xFFFF (inclusive), the index - * returned is the largest value k such that:

- * - *
-	 * (this.charAt(k) == searchChar) && (k <= start)
-	 * 
- * - *
is true. For other values of {@code searchChar}, it is the - * largest value k such that:
- * - *
-	 * (this.codePointAt(k) == searchChar) && (k <= start)
-	 * 
- * - *
is true. In either case, if no such character occurs in - * {@code cs} at or before position {@code start}, then {@code -1} is returned. - * - *

- * All indices are specified in {@code char} values (Unicode code units). - * - * @param cs the {@code CharSequence} to be processed - * @param searchChar the char to be searched for - * @param start the start index, negative returns -1, beyond length starts - * at end - * @return the index where the search char was found, -1 if not found - * @since 3.6 updated to behave more like {@code String} - */ - static int lastIndexOf(final CharSequence cs, final int searchChar, int start) { - if (cs instanceof String) { - return ((String) cs).lastIndexOf(searchChar, start); - } - final int sz = cs.length(); - if (start < 0) { - return NOT_FOUND; - } - if (start >= sz) { - start = sz - 1; - } - if (searchChar < Character.MIN_SUPPLEMENTARY_CODE_POINT) { - for (int i = start; i >= 0; --i) { - if (cs.charAt(i) == searchChar) { - return i; - } - } - return NOT_FOUND; - } - // supplementary characters (LANG1300) - // NOTE - we must do a forward traversal for this to avoid duplicating code - // points - if (searchChar <= Character.MAX_CODE_POINT) { - final char[] chars = Character.toChars(searchChar); - // make sure it's not the last index - if (start == sz - 1) { - return NOT_FOUND; - } - for (int i = start; i >= 0; i--) { - final char high = cs.charAt(i); - final char low = cs.charAt(i + 1); - if (chars[0] == high && chars[1] == low) { - return i; - } - } - } - return NOT_FOUND; - } - - static final int TO_STRING_LIMIT = 16; - - /** - * Used by the lastIndexOf(CharSequence methods) as a green implementation of - * lastIndexOf - * - * @param cs the {@code CharSequence} to be processed - * @param searchChar the {@code CharSequence} to find - * @param start the start index - * @return the index where the search sequence was found - */ - static int lastIndexOf(final CharSequence cs, final CharSequence searchChar, int start) { - if (searchChar == null || cs == null) { - return NOT_FOUND; - } - if (searchChar instanceof String) { - if (cs instanceof String) { - return ((String) cs).lastIndexOf((String) searchChar, start); - } else if (cs instanceof StringBuilder) { - return ((StringBuilder) cs).lastIndexOf((String) searchChar, start); - } else if (cs instanceof StringBuffer) { - return ((StringBuffer) cs).lastIndexOf((String) searchChar, start); - } - } - - final int len1 = cs.length(); - final int len2 = searchChar.length(); - - if (start > len1) { - start = len1; - } - - if (start < 0 || len2 < 0 || len2 > len1) { - return NOT_FOUND; - } - - if (len2 == 0) { - return start; - } - - if (len2 <= TO_STRING_LIMIT) { - if (cs instanceof String) { - return ((String) cs).lastIndexOf(searchChar.toString(), start); - } else if (cs instanceof StringBuilder) { - return ((StringBuilder) cs).lastIndexOf(searchChar.toString(), start); - } else if (cs instanceof StringBuffer) { - return ((StringBuffer) cs).lastIndexOf(searchChar.toString(), start); - } - } - - if (start + len2 > len1) { - start = len1 - len2; - } - - final char char0 = searchChar.charAt(0); - - int i = start; - while (true) { - while (cs.charAt(i) != char0) { - i--; - if (i < 0) { - return NOT_FOUND; - } - } - if (checkLaterThan1(cs, searchChar, len2, i)) { - return i; - } - i--; - if (i < 0) { - return NOT_FOUND; - } - } - } - - private static boolean checkLaterThan1(final CharSequence cs, final CharSequence searchChar, final int len2, - final int start1) { - for (int i = 1, j = len2 - 1; i <= j; i++, j--) { - if (cs.charAt(start1 + i) != searchChar.charAt(i) || cs.charAt(start1 + j) != searchChar.charAt(j)) { - return false; - } - } - return true; - } - - /** - * Converts the given CharSequence to a char[]. - * - * @param source the {@code CharSequence} to be processed. - * @return the resulting char array, never null. - * @since 3.11 - */ - public static char[] toCharArray(final CharSequence source) { - final int len = StringUtils.length(source); - if (len == 0) { - return new char[0]; - } - if (source instanceof String) { - return ((String) source).toCharArray(); - } - final char[] array = new char[len]; - for (int i = 0; i < len; i++) { - array[i] = source.charAt(i); - } - return array; - } - - /** - * Green implementation of regionMatches. - * - * @param cs the {@code CharSequence} to be processed - * @param ignoreCase whether or not to be case insensitive - * @param thisStart the index to start on the {@code cs} CharSequence - * @param substring the {@code CharSequence} to be looked for - * @param start the index to start on the {@code substring} CharSequence - * @param length character length of the region - * @return whether the region matched - */ - static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int thisStart, - final CharSequence substring, final int start, final int length) { - if (cs instanceof String && substring instanceof String) { - return ((String) cs).regionMatches(ignoreCase, thisStart, (String) substring, start, length); - } - int index1 = thisStart; - int index2 = start; - int tmpLen = length; - - // Extract these first so we detect NPEs the same as the java.lang.String - // version - final int srcLen = cs.length() - thisStart; - final int otherLen = substring.length() - start; - - // Check for invalid parameters - if (thisStart < 0 || start < 0 || length < 0) { - return false; - } - - // Check that the regions are long enough - if (srcLen < length || otherLen < length) { - return false; - } - - while (tmpLen-- > 0) { - final char c1 = cs.charAt(index1++); - final char c2 = substring.charAt(index2++); - - if (c1 == c2) { - continue; - } - - if (!ignoreCase) { - return false; - } - - // The real same check as in String.regionMatches(): - final char u1 = Character.toUpperCase(c1); - final char u2 = Character.toUpperCase(c2); - if (u1 != u2 && Character.toLowerCase(u1) != Character.toLowerCase(u2)) { - return false; - } - } - - return true; - } -} diff --git a/src/main/java/org/apache/commons/lang3/CharSet.java b/src/main/java/org/apache/commons/lang3/CharSet.java deleted file mode 100755 index 60b8ab8c..00000000 --- a/src/main/java/org/apache/commons/lang3/CharSet.java +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import java.io.Serializable; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - *

- * A set of characters. - *

- * - *

- * Instances are immutable, but instances of subclasses may not be. - *

- * - *

- * #ThreadSafe# - *

- * - * @since 1.0 - */ -public class CharSet implements Serializable { - - /** - * Required for serialization support. Lang version 2.0. - * - * @see java.io.Serializable - */ - private static final long serialVersionUID = 5947847346149275958L; - - /** - * A CharSet defining no characters. - * - * @since 2.0 - */ - public static final CharSet EMPTY = new CharSet((String) null); - - /** - * A CharSet defining ASCII alphabetic characters "a-zA-Z". - * - * @since 2.0 - */ - public static final CharSet ASCII_ALPHA = new CharSet("a-zA-Z"); - - /** - * A CharSet defining ASCII alphabetic characters "a-z". - * - * @since 2.0 - */ - public static final CharSet ASCII_ALPHA_LOWER = new CharSet("a-z"); - - /** - * A CharSet defining ASCII alphabetic characters "A-Z". - * - * @since 2.0 - */ - public static final CharSet ASCII_ALPHA_UPPER = new CharSet("A-Z"); - - /** - * A CharSet defining ASCII alphabetic characters "0-9". - * - * @since 2.0 - */ - public static final CharSet ASCII_NUMERIC = new CharSet("0-9"); - - /** - * A Map of the common cases used in the factory. Subclasses can add more common - * patterns if desired - * - * @since 2.0 - */ - protected static final Map COMMON = Collections.synchronizedMap(new HashMap<>()); - - static { - COMMON.put(null, EMPTY); - COMMON.put(StringUtils.EMPTY, EMPTY); - COMMON.put("a-zA-Z", ASCII_ALPHA); - COMMON.put("A-Za-z", ASCII_ALPHA); - COMMON.put("a-z", ASCII_ALPHA_LOWER); - COMMON.put("A-Z", ASCII_ALPHA_UPPER); - COMMON.put("0-9", ASCII_NUMERIC); - } - - /** The set of CharRange objects. */ - private final Set set = Collections.synchronizedSet(new HashSet<>()); - - // ----------------------------------------------------------------------- - /** - *

- * Factory method to create a new CharSet using a special syntax. - *

- * - *
    - *
  • {@code null} or empty string ("") - set containing no characters
  • - *
  • Single character, such as "a" - set containing just that character
  • - *
  • Multi character, such as "a-e" - set containing characters from one - * character to the other
  • - *
  • Negated, such as "^a" or "^a-e" - set containing all characters except - * those defined
  • - *
  • Combinations, such as "abe-g" - set containing all the characters from - * the individual sets
  • - *
- * - *

- * The matching order is: - *

- *
    - *
  1. Negated multi character range, such as "^a-e" - *
  2. Ordinary multi character range, such as "a-e" - *
  3. Negated single character, such as "^a" - *
  4. Ordinary single character, such as "a" - *
- * - *

- * Matching works left to right. Once a match is found the search starts again - * from the next character. - *

- * - *

- * If the same range is defined twice using the same syntax, only one range will - * be kept. Thus, "a-ca-c" creates only one range of "a-c". - *

- * - *

- * If the start and end of a range are in the wrong order, they are reversed. - * Thus "a-e" is the same as "e-a". As a result, "a-ee-a" would create only one - * range, as the "a-e" and "e-a" are the same. - *

- * - *

- * The set of characters represented is the union of the specified ranges. - *

- * - *

- * There are two ways to add a literal negation character ({@code ^}): - *

- *
    - *
  • As the last character in a string, e.g. - * {@code CharSet.getInstance("a-z^")}
  • - *
  • As a separate element, e.g. {@code CharSet.getInstance("^", "a-z")}
  • - *
- * - *

- * Examples using the negation character: - *

- * - *
-	 *     CharSet.getInstance("^a-c").contains('a') = false
-	 *     CharSet.getInstance("^a-c").contains('d') = true
-	 *     CharSet.getInstance("^^a-c").contains('a') = true // (only '^' is negated)
-	 *     CharSet.getInstance("^^a-c").contains('^') = false
-	 *     CharSet.getInstance("^a-cd-f").contains('d') = true
-	 *     CharSet.getInstance("a-c^").contains('^') = true
-	 *     CharSet.getInstance("^", "a-c").contains('^') = true
-	 * 
- * - *

- * All CharSet objects returned by this method will be immutable. - *

- * - * @param setStrs Strings to merge into the set, may be null - * @return a CharSet instance - * @since 2.4 - */ - public static CharSet getInstance(final String... setStrs) { - if (setStrs == null) { - return null; - } - if (setStrs.length == 1) { - final CharSet common = COMMON.get(setStrs[0]); - if (common != null) { - return common; - } - } - return new CharSet(setStrs); - } - - // ----------------------------------------------------------------------- - /** - *

- * Constructs a new CharSet using the set syntax. Each string is merged in with - * the set. - *

- * - * @param set Strings to merge into the initial set - * @throws NullPointerException if set is {@code null} - */ - protected CharSet(final String... set) { - for (final String s : set) { - add(s); - } - } - - // ----------------------------------------------------------------------- - /** - *

- * Add a set definition string to the {@code CharSet}. - *

- * - * @param str set definition string - */ - protected void add(final String str) { - if (str == null) { - return; - } - - final int len = str.length(); - int pos = 0; - while (pos < len) { - final int remainder = len - pos; - if (remainder >= 4 && str.charAt(pos) == '^' && str.charAt(pos + 2) == '-') { - // negated range - set.add(CharRange.isNotIn(str.charAt(pos + 1), str.charAt(pos + 3))); - pos += 4; - } else if (remainder >= 3 && str.charAt(pos + 1) == '-') { - // range - set.add(CharRange.isIn(str.charAt(pos), str.charAt(pos + 2))); - pos += 3; - } else if (remainder >= 2 && str.charAt(pos) == '^') { - // negated char - set.add(CharRange.isNot(str.charAt(pos + 1))); - pos += 2; - } else { - // char - set.add(CharRange.is(str.charAt(pos))); - pos += 1; - } - } - } - - // ----------------------------------------------------------------------- - /** - *

- * Gets the internal set as an array of CharRange objects. - *

- * - * @return an array of immutable CharRange objects - * @since 2.0 - */ -// NOTE: This is no longer public as CharRange is no longer a public class. -// It may be replaced when CharSet moves to Range. - /* public */ CharRange[] getCharRanges() { - return set.toArray(CharRange.EMPTY_ARRAY); - } - - // ----------------------------------------------------------------------- - /** - *

- * Does the {@code CharSet} contain the specified character {@code ch}. - *

- * - * @param ch the character to check for - * @return {@code true} if the set contains the characters - */ - public boolean contains(final char ch) { - synchronized (set) { - for (final CharRange range : set) { - if (range.contains(ch)) { - return true; - } - } - } - return false; - } - - // Basics - // ----------------------------------------------------------------------- - /** - *

- * Compares two {@code CharSet} objects, returning true if they represent - * exactly the same set of characters defined in the same way. - *

- * - *

- * The two sets {@code abc} and {@code a-c} are not equal according to - * this method. - *

- * - * @param obj the object to compare to - * @return true if equal - * @since 2.0 - */ - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof CharSet)) { - return false; - } - final CharSet other = (CharSet) obj; - return set.equals(other.set); - } - - /** - *

- * Gets a hash code compatible with the equals method. - *

- * - * @return a suitable hash code - * @since 2.0 - */ - @Override - public int hashCode() { - return 89 + set.hashCode(); - } - - /** - *

- * Gets a string representation of the set. - *

- * - * @return string representation of the set - */ - @Override - public String toString() { - return set.toString(); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/CharSetUtils.java b/src/main/java/org/apache/commons/lang3/CharSetUtils.java deleted file mode 100755 index 7a009e7c..00000000 --- a/src/main/java/org/apache/commons/lang3/CharSetUtils.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -/** - *

- * Operations on {@code CharSet} instances. - *

- * - *

- * This class handles {@code null} input gracefully. An exception will not be - * thrown for a {@code null} input. Each method documents its behavior in more - * detail. - *

- * - *

- * #ThreadSafe# - *

- * - * @see CharSet - * @since 1.0 - */ -public class CharSetUtils { - - /** - *

- * Takes an argument in set-syntax, see evaluateSet, and identifies whether any - * of the characters are present in the specified string. - *

- * - *
-	 * CharSetUtils.containsAny(null, *)        = false
-	 * CharSetUtils.containsAny("", *)          = false
-	 * CharSetUtils.containsAny(*, null)        = false
-	 * CharSetUtils.containsAny(*, "")          = false
-	 * CharSetUtils.containsAny("hello", "k-p") = true
-	 * CharSetUtils.containsAny("hello", "a-d") = false
-	 * 
- * - * @see CharSet#getInstance(java.lang.String...) for set-syntax. - * @param str String to look for characters in, may be null - * @param set String[] set of characters to identify, may be null - * @return whether or not the characters in the set are in the primary string - * @since 3.2 - */ - public static boolean containsAny(final String str, final String... set) { - if (StringUtils.isEmpty(str) || deepEmpty(set)) { - return false; - } - final CharSet chars = CharSet.getInstance(set); - for (final char c : str.toCharArray()) { - if (chars.contains(c)) { - return true; - } - } - return false; - } - - /** - *

- * Takes an argument in set-syntax, see evaluateSet, and returns the number of - * characters present in the specified string. - *

- * - *
-	 * CharSetUtils.count(null, *)        = 0
-	 * CharSetUtils.count("", *)          = 0
-	 * CharSetUtils.count(*, null)        = 0
-	 * CharSetUtils.count(*, "")          = 0
-	 * CharSetUtils.count("hello", "k-p") = 3
-	 * CharSetUtils.count("hello", "a-e") = 1
-	 * 
- * - * @see CharSet#getInstance(java.lang.String...) for set-syntax. - * @param str String to count characters in, may be null - * @param set String[] set of characters to count, may be null - * @return the character count, zero if null string input - */ - public static int count(final String str, final String... set) { - if (StringUtils.isEmpty(str) || deepEmpty(set)) { - return 0; - } - final CharSet chars = CharSet.getInstance(set); - int count = 0; - for (final char c : str.toCharArray()) { - if (chars.contains(c)) { - count++; - } - } - return count; - } - - /** - * Determines whether or not all the Strings in an array are empty or not. - * - * @param strings String[] whose elements are being checked for emptiness - * @return whether or not the String is empty - */ - private static boolean deepEmpty(final String[] strings) { - if (strings != null) { - for (final String s : strings) { - if (StringUtils.isNotEmpty(s)) { - return false; - } - } - } - return true; - } - - /** - *

- * Takes an argument in set-syntax, see evaluateSet, and deletes any of - * characters present in the specified string. - *

- * - *
-	 * CharSetUtils.delete(null, *)        = null
-	 * CharSetUtils.delete("", *)          = ""
-	 * CharSetUtils.delete(*, null)        = *
-	 * CharSetUtils.delete(*, "")          = *
-	 * CharSetUtils.delete("hello", "hl")  = "eo"
-	 * CharSetUtils.delete("hello", "le")  = "ho"
-	 * 
- * - * @see CharSet#getInstance(java.lang.String...) for set-syntax. - * @param str String to delete characters from, may be null - * @param set String[] set of characters to delete, may be null - * @return the modified String, {@code null} if null string input - */ - public static String delete(final String str, final String... set) { - if (StringUtils.isEmpty(str) || deepEmpty(set)) { - return str; - } - return modify(str, set, false); - } - - /** - *

- * Takes an argument in set-syntax, see evaluateSet, and keeps any of characters - * present in the specified string. - *

- * - *
-	 * CharSetUtils.keep(null, *)        = null
-	 * CharSetUtils.keep("", *)          = ""
-	 * CharSetUtils.keep(*, null)        = ""
-	 * CharSetUtils.keep(*, "")          = ""
-	 * CharSetUtils.keep("hello", "hl")  = "hll"
-	 * CharSetUtils.keep("hello", "le")  = "ell"
-	 * 
- * - * @see CharSet#getInstance(java.lang.String...) for set-syntax. - * @param str String to keep characters from, may be null - * @param set String[] set of characters to keep, may be null - * @return the modified String, {@code null} if null string input - * @since 2.0 - */ - public static String keep(final String str, final String... set) { - if (str == null) { - return null; - } - if (str.isEmpty() || deepEmpty(set)) { - return StringUtils.EMPTY; - } - return modify(str, set, true); - } - - /** - * Implementation of delete and keep - * - * @param str String to modify characters within - * @param set String[] set of characters to modify - * @param expect whether to evaluate on match, or non-match - * @return the modified String, not null - */ - private static String modify(final String str, final String[] set, final boolean expect) { - final CharSet chars = CharSet.getInstance(set); - final StringBuilder buffer = new StringBuilder(str.length()); - final char[] chrs = str.toCharArray(); - for (final char chr : chrs) { - if (chars.contains(chr) == expect) { - buffer.append(chr); - } - } - return buffer.toString(); - } - - /** - *

- * Squeezes any repetitions of a character that is mentioned in the supplied - * set. - *

- * - *
-	 * CharSetUtils.squeeze(null, *)        = null
-	 * CharSetUtils.squeeze("", *)          = ""
-	 * CharSetUtils.squeeze(*, null)        = *
-	 * CharSetUtils.squeeze(*, "")          = *
-	 * CharSetUtils.squeeze("hello", "k-p") = "helo"
-	 * CharSetUtils.squeeze("hello", "a-e") = "hello"
-	 * 
- * - * @see CharSet#getInstance(java.lang.String...) for set-syntax. - * @param str the string to squeeze, may be null - * @param set the character set to use for manipulation, may be null - * @return the modified String, {@code null} if null string input - */ - public static String squeeze(final String str, final String... set) { - if (StringUtils.isEmpty(str) || deepEmpty(set)) { - return str; - } - final CharSet chars = CharSet.getInstance(set); - final StringBuilder buffer = new StringBuilder(str.length()); - final char[] chrs = str.toCharArray(); - final int sz = chrs.length; - char lastChar = chrs[0]; - char ch = ' '; - Character inChars = null; - Character notInChars = null; - buffer.append(lastChar); - for (int i = 1; i < sz; i++) { - ch = chrs[i]; - if (ch == lastChar) { - if (inChars != null && ch == inChars) { - continue; - } - if (notInChars == null || ch != notInChars) { - if (chars.contains(ch)) { - inChars = ch; - continue; - } - notInChars = ch; - } - } - buffer.append(ch); - lastChar = ch; - } - return buffer.toString(); - } - - /** - *

- * CharSetUtils instances should NOT be constructed in standard programming. - * Instead, the class should be used as {@code CharSetUtils.evaluateSet(null);}. - *

- * - *

- * This constructor is public to permit tools that require a JavaBean instance - * to operate. - *

- */ - public CharSetUtils() { - } -} diff --git a/src/main/java/org/apache/commons/lang3/CharUtils.java b/src/main/java/org/apache/commons/lang3/CharUtils.java deleted file mode 100755 index f6b6aba1..00000000 --- a/src/main/java/org/apache/commons/lang3/CharUtils.java +++ /dev/null @@ -1,635 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -/** - *

- * Operations on char primitives and Character objects. - *

- * - *

- * This class tries to handle {@code null} input gracefully. An exception will - * not be thrown for a {@code null} input. Each method documents its behavior in - * more detail. - *

- * - *

- * #ThreadSafe# - *

- * - * @since 2.1 - */ -public class CharUtils { - - private static final String[] CHAR_STRING_ARRAY = new String[128]; - - private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', - 'e', 'f' }; - - /** - * Linefeed character LF ({@code '\n'}, Unicode 000a). - * - * @see JLF: - * Escape Sequences for Character and String Literals - * @since 2.2 - */ - public static final char LF = '\n'; - - /** - * Carriage return characterf CR ('\r', Unicode 000d). - * - * @see JLF: - * Escape Sequences for Character and String Literals - * @since 2.2 - */ - public static final char CR = '\r'; - - /** - * {@code \u0000} null control character ('\0'), abbreviated NUL. - * - * @since 3.6 - */ - public static final char NUL = '\0'; - - static { - for (char c = 0; c < CHAR_STRING_ARRAY.length; c++) { - CHAR_STRING_ARRAY[c] = String.valueOf(c); - } - } - - /** - *

- * {@code CharUtils} instances should NOT be constructed in standard - * programming. Instead, the class should be used as - * {@code CharUtils.toString('c');}. - *

- * - *

- * This constructor is public to permit tools that require a JavaBean instance - * to operate. - *

- */ - public CharUtils() { - } - - // ----------------------------------------------------------------------- - /** - *

- * Converts the character to a Character. - *

- * - *

- * For ASCII 7 bit characters, this uses a cache that will return the same - * Character object each time. - *

- * - *
-	 *   CharUtils.toCharacterObject(' ')  = ' '
-	 *   CharUtils.toCharacterObject('A')  = 'A'
-	 * 
- * - * @deprecated Java 5 introduced {@link Character#valueOf(char)} which caches - * chars 0 through 127. - * @param ch the character to convert - * @return a Character of the specified character - */ - @Deprecated - public static Character toCharacterObject(final char ch) { - return Character.valueOf(ch); - } - - /** - *

- * Converts the String to a Character using the first character, returning null - * for empty Strings. - *

- * - *

- * For ASCII 7 bit characters, this uses a cache that will return the same - * Character object each time. - *

- * - *
-	 *   CharUtils.toCharacterObject(null) = null
-	 *   CharUtils.toCharacterObject("")   = null
-	 *   CharUtils.toCharacterObject("A")  = 'A'
-	 *   CharUtils.toCharacterObject("BA") = 'B'
-	 * 
- * - * @param str the character to convert - * @return the Character value of the first letter of the String - */ - public static Character toCharacterObject(final String str) { - if (StringUtils.isEmpty(str)) { - return null; - } - return Character.valueOf(str.charAt(0)); - } - - // ----------------------------------------------------------------------- - /** - *

- * Converts the Character to a char throwing an exception for {@code null}. - *

- * - *
-	 *   CharUtils.toChar(' ')  = ' '
-	 *   CharUtils.toChar('A')  = 'A'
-	 *   CharUtils.toChar(null) throws IllegalArgumentException
-	 * 
- * - * @param ch the character to convert - * @return the char value of the Character - * @throws NullPointerException if the Character is null - */ - public static char toChar(final Character ch) { - Validate.notNull(ch, "ch"); - return ch.charValue(); - } - - /** - *

- * Converts the Character to a char handling {@code null}. - *

- * - *
-	 *   CharUtils.toChar(null, 'X') = 'X'
-	 *   CharUtils.toChar(' ', 'X')  = ' '
-	 *   CharUtils.toChar('A', 'X')  = 'A'
-	 * 
- * - * @param ch the character to convert - * @param defaultValue the value to use if the Character is null - * @return the char value of the Character or the default if null - */ - public static char toChar(final Character ch, final char defaultValue) { - if (ch == null) { - return defaultValue; - } - return ch.charValue(); - } - - // ----------------------------------------------------------------------- - /** - *

- * Converts the String to a char using the first character, throwing an - * exception on empty Strings. - *

- * - *
-	 *   CharUtils.toChar("A")  = 'A'
-	 *   CharUtils.toChar("BA") = 'B'
-	 *   CharUtils.toChar(null) throws IllegalArgumentException
-	 *   CharUtils.toChar("")   throws IllegalArgumentException
-	 * 
- * - * @param str the character to convert - * @return the char value of the first letter of the String - * @throws NullPointerException if the string is null - * @throws IllegalArgumentException if the String is empty - */ - public static char toChar(final String str) { - Validate.notEmpty(str, "The String must not be empty"); - return str.charAt(0); - } - - /** - *

- * Converts the String to a char using the first character, defaulting the value - * on empty Strings. - *

- * - *
-	 *   CharUtils.toChar(null, 'X') = 'X'
-	 *   CharUtils.toChar("", 'X')   = 'X'
-	 *   CharUtils.toChar("A", 'X')  = 'A'
-	 *   CharUtils.toChar("BA", 'X') = 'B'
-	 * 
- * - * @param str the character to convert - * @param defaultValue the value to use if the Character is null - * @return the char value of the first letter of the String or the default if - * null - */ - public static char toChar(final String str, final char defaultValue) { - if (StringUtils.isEmpty(str)) { - return defaultValue; - } - return str.charAt(0); - } - - // ----------------------------------------------------------------------- - /** - *

- * Converts the character to the Integer it represents, throwing an exception if - * the character is not numeric. - *

- * - *

- * This method converts the char '1' to the int 1 and so on. - *

- * - *
-	 *   CharUtils.toIntValue('3')  = 3
-	 *   CharUtils.toIntValue('A')  throws IllegalArgumentException
-	 * 
- * - * @param ch the character to convert - * @return the int value of the character - * @throws IllegalArgumentException if the character is not ASCII numeric - */ - public static int toIntValue(final char ch) { - if (!isAsciiNumeric(ch)) { - throw new IllegalArgumentException("The character " + ch + " is not in the range '0' - '9'"); - } - return ch - 48; - } - - /** - *

- * Converts the character to the Integer it represents, throwing an exception if - * the character is not numeric. - *

- * - *

- * This method converts the char '1' to the int 1 and so on. - *

- * - *
-	 *   CharUtils.toIntValue('3', -1)  = 3
-	 *   CharUtils.toIntValue('A', -1)  = -1
-	 * 
- * - * @param ch the character to convert - * @param defaultValue the default value to use if the character is not numeric - * @return the int value of the character - */ - public static int toIntValue(final char ch, final int defaultValue) { - if (!isAsciiNumeric(ch)) { - return defaultValue; - } - return ch - 48; - } - - /** - *

- * Converts the character to the Integer it represents, throwing an exception if - * the character is not numeric. - *

- * - *

- * This method converts the char '1' to the int 1 and so on. - *

- * - *
-	 *   CharUtils.toIntValue('3')  = 3
-	 *   CharUtils.toIntValue(null) throws IllegalArgumentException
-	 *   CharUtils.toIntValue('A')  throws IllegalArgumentException
-	 * 
- * - * @param ch the character to convert, not null - * @return the int value of the character - * @throws NullPointerException if the Character is null - * @throws IllegalArgumentException if the Character is not ASCII numeric - */ - public static int toIntValue(final Character ch) { - Validate.notNull(ch, "ch"); - return toIntValue(ch.charValue()); - } - - /** - *

- * Converts the character to the Integer it represents, throwing an exception if - * the character is not numeric. - *

- * - *

- * This method converts the char '1' to the int 1 and so on. - *

- * - *
-	 *   CharUtils.toIntValue(null, -1) = -1
-	 *   CharUtils.toIntValue('3', -1)  = 3
-	 *   CharUtils.toIntValue('A', -1)  = -1
-	 * 
- * - * @param ch the character to convert - * @param defaultValue the default value to use if the character is not numeric - * @return the int value of the character - */ - public static int toIntValue(final Character ch, final int defaultValue) { - if (ch == null) { - return defaultValue; - } - return toIntValue(ch.charValue(), defaultValue); - } - - // ----------------------------------------------------------------------- - /** - *

- * Converts the character to a String that contains the one character. - *

- * - *

- * For ASCII 7 bit characters, this uses a cache that will return the same - * String object each time. - *

- * - *
-	 *   CharUtils.toString(' ')  = " "
-	 *   CharUtils.toString('A')  = "A"
-	 * 
- * - * @param ch the character to convert - * @return a String containing the one specified character - */ - public static String toString(final char ch) { - if (ch < 128) { - return CHAR_STRING_ARRAY[ch]; - } - return new String(new char[] { ch }); - } - - /** - *

- * Converts the character to a String that contains the one character. - *

- * - *

- * For ASCII 7 bit characters, this uses a cache that will return the same - * String object each time. - *

- * - *

- * If {@code null} is passed in, {@code null} will be returned. - *

- * - *
-	 *   CharUtils.toString(null) = null
-	 *   CharUtils.toString(' ')  = " "
-	 *   CharUtils.toString('A')  = "A"
-	 * 
- * - * @param ch the character to convert - * @return a String containing the one specified character - */ - public static String toString(final Character ch) { - if (ch == null) { - return null; - } - return toString(ch.charValue()); - } - - // -------------------------------------------------------------------------- - /** - *

- * Converts the string to the Unicode format '\u0020'. - *

- * - *

- * This format is the Java source code format. - *

- * - *
-	 *   CharUtils.unicodeEscaped(' ') = "\u0020"
-	 *   CharUtils.unicodeEscaped('A') = "\u0041"
-	 * 
- * - * @param ch the character to convert - * @return the escaped Unicode string - */ - public static String unicodeEscaped(final char ch) { - return "\\u" + HEX_DIGITS[(ch >> 12) & 15] + HEX_DIGITS[(ch >> 8) & 15] + HEX_DIGITS[(ch >> 4) & 15] - + HEX_DIGITS[(ch) & 15]; - } - - /** - *

- * Converts the string to the Unicode format '\u0020'. - *

- * - *

- * This format is the Java source code format. - *

- * - *

- * If {@code null} is passed in, {@code null} will be returned. - *

- * - *
-	 *   CharUtils.unicodeEscaped(null) = null
-	 *   CharUtils.unicodeEscaped(' ')  = "\u0020"
-	 *   CharUtils.unicodeEscaped('A')  = "\u0041"
-	 * 
- * - * @param ch the character to convert, may be null - * @return the escaped Unicode string, null if null input - */ - public static String unicodeEscaped(final Character ch) { - if (ch == null) { - return null; - } - return unicodeEscaped(ch.charValue()); - } - - // -------------------------------------------------------------------------- - /** - *

- * Checks whether the character is ASCII 7 bit. - *

- * - *
-	 *   CharUtils.isAscii('a')  = true
-	 *   CharUtils.isAscii('A')  = true
-	 *   CharUtils.isAscii('3')  = true
-	 *   CharUtils.isAscii('-')  = true
-	 *   CharUtils.isAscii('\n') = true
-	 *   CharUtils.isAscii('©') = false
-	 * 
- * - * @param ch the character to check - * @return true if less than 128 - */ - public static boolean isAscii(final char ch) { - return ch < 128; - } - - /** - *

- * Checks whether the character is ASCII 7 bit printable. - *

- * - *
-	 *   CharUtils.isAsciiPrintable('a')  = true
-	 *   CharUtils.isAsciiPrintable('A')  = true
-	 *   CharUtils.isAsciiPrintable('3')  = true
-	 *   CharUtils.isAsciiPrintable('-')  = true
-	 *   CharUtils.isAsciiPrintable('\n') = false
-	 *   CharUtils.isAsciiPrintable('©') = false
-	 * 
- * - * @param ch the character to check - * @return true if between 32 and 126 inclusive - */ - public static boolean isAsciiPrintable(final char ch) { - return ch >= 32 && ch < 127; - } - - /** - *

- * Checks whether the character is ASCII 7 bit control. - *

- * - *
-	 *   CharUtils.isAsciiControl('a')  = false
-	 *   CharUtils.isAsciiControl('A')  = false
-	 *   CharUtils.isAsciiControl('3')  = false
-	 *   CharUtils.isAsciiControl('-')  = false
-	 *   CharUtils.isAsciiControl('\n') = true
-	 *   CharUtils.isAsciiControl('©') = false
-	 * 
- * - * @param ch the character to check - * @return true if less than 32 or equals 127 - */ - public static boolean isAsciiControl(final char ch) { - return ch < 32 || ch == 127; - } - - /** - *

- * Checks whether the character is ASCII 7 bit alphabetic. - *

- * - *
-	 *   CharUtils.isAsciiAlpha('a')  = true
-	 *   CharUtils.isAsciiAlpha('A')  = true
-	 *   CharUtils.isAsciiAlpha('3')  = false
-	 *   CharUtils.isAsciiAlpha('-')  = false
-	 *   CharUtils.isAsciiAlpha('\n') = false
-	 *   CharUtils.isAsciiAlpha('©') = false
-	 * 
- * - * @param ch the character to check - * @return true if between 65 and 90 or 97 and 122 inclusive - */ - public static boolean isAsciiAlpha(final char ch) { - return isAsciiAlphaUpper(ch) || isAsciiAlphaLower(ch); - } - - /** - *

- * Checks whether the character is ASCII 7 bit alphabetic upper case. - *

- * - *
-	 *   CharUtils.isAsciiAlphaUpper('a')  = false
-	 *   CharUtils.isAsciiAlphaUpper('A')  = true
-	 *   CharUtils.isAsciiAlphaUpper('3')  = false
-	 *   CharUtils.isAsciiAlphaUpper('-')  = false
-	 *   CharUtils.isAsciiAlphaUpper('\n') = false
-	 *   CharUtils.isAsciiAlphaUpper('©') = false
-	 * 
- * - * @param ch the character to check - * @return true if between 65 and 90 inclusive - */ - public static boolean isAsciiAlphaUpper(final char ch) { - return ch >= 'A' && ch <= 'Z'; - } - - /** - *

- * Checks whether the character is ASCII 7 bit alphabetic lower case. - *

- * - *
-	 *   CharUtils.isAsciiAlphaLower('a')  = true
-	 *   CharUtils.isAsciiAlphaLower('A')  = false
-	 *   CharUtils.isAsciiAlphaLower('3')  = false
-	 *   CharUtils.isAsciiAlphaLower('-')  = false
-	 *   CharUtils.isAsciiAlphaLower('\n') = false
-	 *   CharUtils.isAsciiAlphaLower('©') = false
-	 * 
- * - * @param ch the character to check - * @return true if between 97 and 122 inclusive - */ - public static boolean isAsciiAlphaLower(final char ch) { - return ch >= 'a' && ch <= 'z'; - } - - /** - *

- * Checks whether the character is ASCII 7 bit numeric. - *

- * - *
-	 *   CharUtils.isAsciiNumeric('a')  = false
-	 *   CharUtils.isAsciiNumeric('A')  = false
-	 *   CharUtils.isAsciiNumeric('3')  = true
-	 *   CharUtils.isAsciiNumeric('-')  = false
-	 *   CharUtils.isAsciiNumeric('\n') = false
-	 *   CharUtils.isAsciiNumeric('©') = false
-	 * 
- * - * @param ch the character to check - * @return true if between 48 and 57 inclusive - */ - public static boolean isAsciiNumeric(final char ch) { - return ch >= '0' && ch <= '9'; - } - - /** - *

- * Checks whether the character is ASCII 7 bit numeric. - *

- * - *
-	 *   CharUtils.isAsciiAlphanumeric('a')  = true
-	 *   CharUtils.isAsciiAlphanumeric('A')  = true
-	 *   CharUtils.isAsciiAlphanumeric('3')  = true
-	 *   CharUtils.isAsciiAlphanumeric('-')  = false
-	 *   CharUtils.isAsciiAlphanumeric('\n') = false
-	 *   CharUtils.isAsciiAlphanumeric('©') = false
-	 * 
- * - * @param ch the character to check - * @return true if between 48 and 57 or 65 and 90 or 97 and 122 inclusive - */ - public static boolean isAsciiAlphanumeric(final char ch) { - return isAsciiAlpha(ch) || isAsciiNumeric(ch); - } - - /** - *

- * Compares two {@code char} values numerically. This is the same functionality - * as provided in Java 7. - *

- * - * @param x the first {@code char} to compare - * @param y the second {@code char} to compare - * @return the value {@code 0} if {@code x == y}; a value less than {@code 0} if - * {@code x < y}; and a value greater than {@code 0} if {@code x > y} - * @since 3.4 - */ - public static int compare(final char x, final char y) { - return x - y; - } -} diff --git a/src/main/java/org/apache/commons/lang3/Charsets.java b/src/main/java/org/apache/commons/lang3/Charsets.java deleted file mode 100755 index ef638262..00000000 --- a/src/main/java/org/apache/commons/lang3/Charsets.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3; - -import java.nio.charset.Charset; -import java.nio.charset.UnsupportedCharsetException; - -/** - * Internal use only. - *

- * Provides utilities for {@link Charset}. - *

- *

- * Package private since Apache Commons IO already provides a Charsets because - * {@link Charset} is in {@code java.nio.charset}. - *

- * - * @since 3.10 - */ -class Charsets { - - /** - * Returns the given {@code charset} or the default Charset if {@code charset} - * is null. - * - * @param charset a Charset or null. - * @return the given {@code charset} or the default Charset if {@code charset} - * is null. - */ - static Charset toCharset(final Charset charset) { - return charset == null ? Charset.defaultCharset() : charset; - } - - /** - * Returns the given {@code charset} or the default Charset if {@code charset} - * is null. - * - * @param charsetName a Charset or null. - * @return the given {@code charset} or the default Charset if {@code charset} - * is null. - * @throws UnsupportedCharsetException If no support for the named charset is - * available in this instance of the Java - * virtual machine - */ - static Charset toCharset(final String charsetName) { - return charsetName == null ? Charset.defaultCharset() : Charset.forName(charsetName); - } - - /** - * Returns the given {@code charset} or the default Charset if {@code charset} - * is null. - * - * @param charsetName a Charset or null. - * @return the given {@code charset} or the default Charset if {@code charset} - * is null. - */ - static String toCharsetName(final String charsetName) { - return charsetName == null ? Charset.defaultCharset().name() : charsetName; - } - -} diff --git a/src/main/java/org/apache/commons/lang3/ClassLoaderUtils.java b/src/main/java/org/apache/commons/lang3/ClassLoaderUtils.java deleted file mode 100755 index fd03c40c..00000000 --- a/src/main/java/org/apache/commons/lang3/ClassLoaderUtils.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3; - -import java.net.URLClassLoader; -import java.util.Arrays; - -/** - * Helps work with {@link ClassLoader}. - * - * @since 3.10 - */ -public class ClassLoaderUtils { - - /** - * Converts the given class loader to a String calling - * {@link #toString(URLClassLoader)}. - * - * @param classLoader to URLClassLoader to convert. - * @return the formatted string. - */ - public static String toString(final ClassLoader classLoader) { - if (classLoader instanceof URLClassLoader) { - return toString((URLClassLoader) classLoader); - } - return classLoader.toString(); - } - - /** - * Converts the given URLClassLoader to a String in the format - * {@code "URLClassLoader.toString() + [URL1, URL2, ...]"}. - * - * @param classLoader to URLClassLoader to convert. - * @return the formatted string. - */ - public static String toString(final URLClassLoader classLoader) { - return classLoader + Arrays.toString(classLoader.getURLs()); - } -} diff --git a/src/main/java/org/apache/commons/lang3/ClassPathUtils.java b/src/main/java/org/apache/commons/lang3/ClassPathUtils.java deleted file mode 100755 index 105137c8..00000000 --- a/src/main/java/org/apache/commons/lang3/ClassPathUtils.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -/** - * Operations regarding the classpath. - * - *

- * The methods of this class do not allow {@code null} inputs. - *

- * - * @since 3.3 - */ -//@Immutable -public class ClassPathUtils { - - /** - *

- * {@code ClassPathUtils} instances should NOT be constructed in standard - * programming. Instead, the class should be used as - * {@code ClassPathUtils.toFullyQualifiedName(MyClass.class, "MyClass.properties");}. - *

- * - *

- * This constructor is public to permit tools that require a JavaBean instance - * to operate. - *

- */ - public ClassPathUtils() { - } - - /** - * Returns the fully qualified name for the resource with name - * {@code resourceName} relative to the given context. - * - *

- * Note that this method does not check whether the resource actually exists. It - * only constructs the name. Null inputs are not allowed. - *

- * - *
-	 * ClassPathUtils.toFullyQualifiedName(StringUtils.class,
-	 * 		"StringUtils.properties") = "org.apache.commons.lang3.StringUtils.properties"
-	 * 
- * - * @param context The context for constructing the name. - * @param resourceName the resource name to construct the fully qualified name - * for. - * @return the fully qualified name of the resource with name - * {@code resourceName}. - * @throws java.lang.NullPointerException if either {@code context} or - * {@code resourceName} is null. - */ - public static String toFullyQualifiedName(final Class context, final String resourceName) { - Validate.notNull(context, "context"); - Validate.notNull(resourceName, "resourceName"); - return toFullyQualifiedName(context.getPackage(), resourceName); - } - - /** - * Returns the fully qualified name for the resource with name - * {@code resourceName} relative to the given context. - * - *

- * Note that this method does not check whether the resource actually exists. It - * only constructs the name. Null inputs are not allowed. - *

- * - *
-	 * ClassPathUtils.toFullyQualifiedName(StringUtils.class.getPackage(),
-	 * 		"StringUtils.properties") = "org.apache.commons.lang3.StringUtils.properties"
-	 * 
- * - * @param context The context for constructing the name. - * @param resourceName the resource name to construct the fully qualified name - * for. - * @return the fully qualified name of the resource with name - * {@code resourceName}. - * @throws java.lang.NullPointerException if either {@code context} or - * {@code resourceName} is null. - */ - public static String toFullyQualifiedName(final Package context, final String resourceName) { - Validate.notNull(context, "context"); - Validate.notNull(resourceName, "resourceName"); - return context.getName() + "." + resourceName; - } - - /** - * Returns the fully qualified path for the resource with name - * {@code resourceName} relative to the given context. - * - *

- * Note that this method does not check whether the resource actually exists. It - * only constructs the path. Null inputs are not allowed. - *

- * - *
-	 * ClassPathUtils.toFullyQualifiedPath(StringUtils.class,
-	 * 		"StringUtils.properties") = "org/apache/commons/lang3/StringUtils.properties"
-	 * 
- * - * @param context The context for constructing the path. - * @param resourceName the resource name to construct the fully qualified path - * for. - * @return the fully qualified path of the resource with name - * {@code resourceName}. - * @throws java.lang.NullPointerException if either {@code context} or - * {@code resourceName} is null. - */ - public static String toFullyQualifiedPath(final Class context, final String resourceName) { - Validate.notNull(context, "context"); - Validate.notNull(resourceName, "resourceName"); - return toFullyQualifiedPath(context.getPackage(), resourceName); - } - - /** - * Returns the fully qualified path for the resource with name - * {@code resourceName} relative to the given context. - * - *

- * Note that this method does not check whether the resource actually exists. It - * only constructs the path. Null inputs are not allowed. - *

- * - *
-	 * ClassPathUtils.toFullyQualifiedPath(StringUtils.class.getPackage(),
-	 * 		"StringUtils.properties") = "org/apache/commons/lang3/StringUtils.properties"
-	 * 
- * - * @param context The context for constructing the path. - * @param resourceName the resource name to construct the fully qualified path - * for. - * @return the fully qualified path of the resource with name - * {@code resourceName}. - * @throws java.lang.NullPointerException if either {@code context} or - * {@code resourceName} is null. - */ - public static String toFullyQualifiedPath(final Package context, final String resourceName) { - Validate.notNull(context, "context"); - Validate.notNull(resourceName, "resourceName"); - return context.getName().replace('.', '/') + "/" + resourceName; - } - -} diff --git a/src/main/java/org/apache/commons/lang3/Conversion.java b/src/main/java/org/apache/commons/lang3/Conversion.java deleted file mode 100755 index b51f799f..00000000 --- a/src/main/java/org/apache/commons/lang3/Conversion.java +++ /dev/null @@ -1,1610 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; - -/** - *

- * Static methods to convert a type into another, with endianness and bit - * ordering awareness. - *

- *

- * The methods names follow a naming rule:
- * {@code [source endianness][source bit ordering]To[destination endianness][destination bit ordering]} - *

- *

- * Source/destination type fields is one of the following: - *

- *
    - *
  • binary: an array of booleans
  • - *
  • byte or byteArray
  • - *
  • int or intArray
  • - *
  • long or longArray
  • - *
  • hex: a String containing hexadecimal digits (lowercase in - * destination)
  • - *
  • hexDigit: a Char containing a hexadecimal digit (lowercase in - * destination)
  • - *
  • uuid
  • - *
- *

- * Endianness field: little endian is the default, in this case the field is - * absent. In case of big endian, the field is "Be".
- * Bit ordering: Lsb0 is the default, in this case the field is absent. In case - * of Msb0, the field is "Msb0". - *

- *

- * Example: intBeMsb0ToHex convert an int with big endian byte order and Msb0 - * bit order into its hexadecimal string representation - *

- *

- * Most of the methods provide only default encoding for destination, this - * limits the number of ways to do one thing. Unless you are dealing with data - * from/to outside of the JVM platform, you should not need to use "Be" and - * "Msb0" methods. - *

- *

- * Development status: work on going, only a part of the little endian, Lsb0 - * methods implemented so far. - *

- * - * @since 3.2 - */ - -public class Conversion { - - private static final boolean[] TTTT = { true, true, true, true }; - private static final boolean[] FTTT = { false, true, true, true }; - private static final boolean[] TFTT = { true, false, true, true }; - private static final boolean[] FFTT = { false, false, true, true }; - private static final boolean[] TTFT = { true, true, false, true }; - private static final boolean[] FTFT = { false, true, false, true }; - private static final boolean[] TFFT = { true, false, false, true }; - private static final boolean[] FFFT = { false, false, false, true }; - private static final boolean[] TTTF = { true, true, true, false }; - private static final boolean[] FTTF = { false, true, true, false }; - private static final boolean[] TFTF = { true, false, true, false }; - private static final boolean[] FFTF = { false, false, true, false }; - private static final boolean[] TTFF = { true, true, false, false }; - private static final boolean[] FTFF = { false, true, false, false }; - private static final boolean[] TFFF = { true, false, false, false }; - private static final boolean[] FFFF = { false, false, false, false }; - - /** - *

- * Converts a hexadecimal digit into an int using the default (Lsb0) bit - * ordering. - *

- *

- * '1' is converted to 1 - *

- * - * @param hexDigit the hexadecimal digit to convert - * @return an int equals to {@code hexDigit} - * @throws IllegalArgumentException if {@code hexDigit} is not a hexadecimal - * digit - */ - public static int hexDigitToInt(final char hexDigit) { - final int digit = Character.digit(hexDigit, 16); - if (digit < 0) { - throw new IllegalArgumentException("Cannot interpret '" + hexDigit + "' as a hexadecimal digit"); - } - return digit; - } - - /** - *

- * Converts a hexadecimal digit into an int using the Msb0 bit ordering. - *

- *

- * '1' is converted to 8 - *

- * - * @param hexDigit the hexadecimal digit to convert - * @return an int equals to {@code hexDigit} - * @throws IllegalArgumentException if {@code hexDigit} is not a hexadecimal - * digit - */ - public static int hexDigitMsb0ToInt(final char hexDigit) { - switch (hexDigit) { - case '0': - return 0x0; - case '1': - return 0x8; - case '2': - return 0x4; - case '3': - return 0xC; - case '4': - return 0x2; - case '5': - return 0xA; - case '6': - return 0x6; - case '7': - return 0xE; - case '8': - return 0x1; - case '9': - return 0x9; - case 'a':// fall through - case 'A': - return 0x5; - case 'b':// fall through - case 'B': - return 0xD; - case 'c':// fall through - case 'C': - return 0x3; - case 'd':// fall through - case 'D': - return 0xB; - case 'e':// fall through - case 'E': - return 0x7; - case 'f':// fall through - case 'F': - return 0xF; - default: - throw new IllegalArgumentException("Cannot interpret '" + hexDigit + "' as a hexadecimal digit"); - } - } - - /** - *

- * Converts a hexadecimal digit into binary (represented as boolean array) using - * the default (Lsb0) bit ordering. - *

- *

- * '1' is converted as follow: (1, 0, 0, 0) - *

- * - * @param hexDigit the hexadecimal digit to convert - * @return a boolean array with the binary representation of {@code hexDigit} - * @throws IllegalArgumentException if {@code hexDigit} is not a hexadecimal - * digit - */ - public static boolean[] hexDigitToBinary(final char hexDigit) { - switch (hexDigit) { - case '0': - return FFFF.clone(); - case '1': - return TFFF.clone(); - case '2': - return FTFF.clone(); - case '3': - return TTFF.clone(); - case '4': - return FFTF.clone(); - case '5': - return TFTF.clone(); - case '6': - return FTTF.clone(); - case '7': - return TTTF.clone(); - case '8': - return FFFT.clone(); - case '9': - return TFFT.clone(); - case 'a':// fall through - case 'A': - return FTFT.clone(); - case 'b':// fall through - case 'B': - return TTFT.clone(); - case 'c':// fall through - case 'C': - return FFTT.clone(); - case 'd':// fall through - case 'D': - return TFTT.clone(); - case 'e':// fall through - case 'E': - return FTTT.clone(); - case 'f':// fall through - case 'F': - return TTTT.clone(); - default: - throw new IllegalArgumentException("Cannot interpret '" + hexDigit + "' as a hexadecimal digit"); - } - } - - /** - *

- * Converts a hexadecimal digit into binary (represented as boolean array) using - * the Msb0 bit ordering. - *

- *

- * '1' is converted as follow: (0, 0, 0, 1) - *

- * - * @param hexDigit the hexadecimal digit to convert - * @return a boolean array with the binary representation of {@code hexDigit} - * @throws IllegalArgumentException if {@code hexDigit} is not a hexadecimal - * digit - */ - public static boolean[] hexDigitMsb0ToBinary(final char hexDigit) { - switch (hexDigit) { - case '0': - return FFFF.clone(); - case '1': - return FFFT.clone(); - case '2': - return FFTF.clone(); - case '3': - return FFTT.clone(); - case '4': - return FTFF.clone(); - case '5': - return FTFT.clone(); - case '6': - return FTTF.clone(); - case '7': - return FTTT.clone(); - case '8': - return TFFF.clone(); - case '9': - return TFFT.clone(); - case 'a':// fall through - case 'A': - return TFTF.clone(); - case 'b':// fall through - case 'B': - return TFTT.clone(); - case 'c':// fall through - case 'C': - return TTFF.clone(); - case 'd':// fall through - case 'D': - return TTFT.clone(); - case 'e':// fall through - case 'E': - return TTTF.clone(); - case 'f':// fall through - case 'F': - return TTTT.clone(); - default: - throw new IllegalArgumentException("Cannot interpret '" + hexDigit + "' as a hexadecimal digit"); - } - } - - /** - *

- * Converts binary (represented as boolean array) to a hexadecimal digit using - * the default (Lsb0) bit ordering. - *

- *

- * (1, 0, 0, 0) is converted as follow: '1' - *

- * - * @param src the binary to convert - * @return a hexadecimal digit representing the selected bits - * @throws IllegalArgumentException if {@code src} is empty - * @throws NullPointerException if {@code src} is {@code null} - */ - public static char binaryToHexDigit(final boolean[] src) { - return binaryToHexDigit(src, 0); - } - - /** - *

- * Converts binary (represented as boolean array) to a hexadecimal digit using - * the default (Lsb0) bit ordering. - *

- *

- * (1, 0, 0, 0) is converted as follow: '1' - *

- * - * @param src the binary to convert - * @param srcPos the position of the lsb to start the conversion - * @return a hexadecimal digit representing the selected bits - * @throws IllegalArgumentException if {@code src} is empty - * @throws NullPointerException if {@code src} is {@code null} - */ - public static char binaryToHexDigit(final boolean[] src, final int srcPos) { - if (src.length == 0) { - throw new IllegalArgumentException("Cannot convert an empty array."); - } - if (src.length > srcPos + 3 && src[srcPos + 3]) { - if (src[srcPos + 2]) { - if (src[srcPos + 1]) { - return src[srcPos] ? 'f' : 'e'; - } - return src[srcPos] ? 'd' : 'c'; - } - if (src[srcPos + 1]) { - return src[srcPos] ? 'b' : 'a'; - } - return src[srcPos] ? '9' : '8'; - } - if (src.length > srcPos + 2 && src[srcPos + 2]) { - if (src[srcPos + 1]) { - return src[srcPos] ? '7' : '6'; - } - return src[srcPos] ? '5' : '4'; - } - if (src.length > srcPos + 1 && src[srcPos + 1]) { - return src[srcPos] ? '3' : '2'; - } - return src[srcPos] ? '1' : '0'; - } - - /** - *

- * Converts binary (represented as boolean array) to a hexadecimal digit using - * the Msb0 bit ordering. - *

- *

- * (1, 0, 0, 0) is converted as follow: '8' - *

- * - * @param src the binary to convert - * @return a hexadecimal digit representing the selected bits - * @throws IllegalArgumentException if {@code src} is empty, - * {@code src.length < 4} or - * {@code src.length > 8} - * @throws NullPointerException if {@code src} is {@code null} - */ - public static char binaryToHexDigitMsb0_4bits(final boolean[] src) { - return binaryToHexDigitMsb0_4bits(src, 0); - } - - /** - *

- * Converts binary (represented as boolean array) to a hexadecimal digit using - * the Msb0 bit ordering. - *

- *

- * (1, 0, 0, 0) is converted as follow: '8' (1, 0, 0, 1, 1, 0, 1, 0) with srcPos - * = 3 is converted to 'D' - *

- * - * @param src the binary to convert - * @param srcPos the position of the lsb to start the conversion - * @return a hexadecimal digit representing the selected bits - * @throws IllegalArgumentException if {@code src} is empty, - * {@code src.length > 8} or - * {@code src.length - srcPos < 4} - * @throws NullPointerException if {@code src} is {@code null} - */ - public static char binaryToHexDigitMsb0_4bits(final boolean[] src, final int srcPos) { - if (src.length > 8) { - throw new IllegalArgumentException("src.length>8: src.length=" + src.length); - } - if (src.length - srcPos < 4) { - throw new IllegalArgumentException("src.length-srcPos<4: src.length=" + src.length + ", srcPos=" + srcPos); - } - if (src[srcPos + 3]) { - if (src[srcPos + 2]) { - if (src[srcPos + 1]) { - return src[srcPos] ? 'f' : '7'; - } - return src[srcPos] ? 'b' : '3'; - } - if (src[srcPos + 1]) { - return src[srcPos] ? 'd' : '5'; - } - return src[srcPos] ? '9' : '1'; - } - if (src[srcPos + 2]) { - if (src[srcPos + 1]) { - return src[srcPos] ? 'e' : '6'; - } - return src[srcPos] ? 'a' : '2'; - } - if (src[srcPos + 1]) { - return src[srcPos] ? 'c' : '4'; - } - return src[srcPos] ? '8' : '0'; - } - - /** - *

- * Converts the first 4 bits of a binary (represented as boolean array) in big - * endian Msb0 bit ordering to a hexadecimal digit. - *

- *

- * (1, 0, 0, 0) is converted as follow: '8' (1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - * 0, 1, 0, 0) is converted to '4' - *

- * - * @param src the binary to convert - * @return a hexadecimal digit representing the selected bits - * @throws IllegalArgumentException if {@code src} is empty - * @throws NullPointerException if {@code src} is {@code null} - */ - public static char binaryBeMsb0ToHexDigit(final boolean[] src) { - return binaryBeMsb0ToHexDigit(src, 0); - } - - /** - *

- * Converts a binary (represented as boolean array) in big endian Msb0 bit - * ordering to a hexadecimal digit. - *

- *

- * (1, 0, 0, 0) with srcPos = 0 is converted as follow: '8' (1, 0, 0, 0, 0, 0, - * 0, 0, 0, 0, 0, 1, 0, 1, 0, 0) with srcPos = 2 is converted to '5' - *

- * - * @param src the binary to convert - * @param srcPos the position of the lsb to start the conversion - * @return a hexadecimal digit representing the selected bits - * @throws IllegalArgumentException if {@code src} is empty - * @throws NullPointerException if {@code src} is {@code null} - */ - public static char binaryBeMsb0ToHexDigit(boolean[] src, int srcPos) { - if (src.length == 0) { - throw new IllegalArgumentException("Cannot convert an empty array."); - } - final int beSrcPos = src.length - 1 - srcPos; - final int srcLen = Math.min(4, beSrcPos + 1); - final boolean[] paddedSrc = new boolean[4]; - System.arraycopy(src, beSrcPos + 1 - srcLen, paddedSrc, 4 - srcLen, srcLen); - src = paddedSrc; - srcPos = 0; - if (src[srcPos]) { - if (src.length > srcPos + 1 && src[srcPos + 1]) { - if (src.length > srcPos + 2 && src[srcPos + 2]) { - return src.length > srcPos + 3 && src[srcPos + 3] ? 'f' : 'e'; - } - return src.length > srcPos + 3 && src[srcPos + 3] ? 'd' : 'c'; - } - if (src.length > srcPos + 2 && src[srcPos + 2]) { - return src.length > srcPos + 3 && src[srcPos + 3] ? 'b' : 'a'; - } - return src.length > srcPos + 3 && src[srcPos + 3] ? '9' : '8'; - } - if (src.length > srcPos + 1 && src[srcPos + 1]) { - if (src.length > srcPos + 2 && src[srcPos + 2]) { - return src.length > srcPos + 3 && src[srcPos + 3] ? '7' : '6'; - } - return src.length > srcPos + 3 && src[srcPos + 3] ? '5' : '4'; - } - if (src.length > srcPos + 2 && src[srcPos + 2]) { - return src.length > srcPos + 3 && src[srcPos + 3] ? '3' : '2'; - } - return src.length > srcPos + 3 && src[srcPos + 3] ? '1' : '0'; - } - - /** - *

- * Converts the 4 lsb of an int to a hexadecimal digit. - *

- *

- * 0 returns '0' - *

- *

- * 1 returns '1' - *

- *

- * 10 returns 'A' and so on... - *

- * - * @param nibble the 4 bits to convert - * @return a hexadecimal digit representing the 4 lsb of {@code nibble} - * @throws IllegalArgumentException if {@code nibble < 0} or {@code nibble > 15} - */ - public static char intToHexDigit(final int nibble) { - final char c = Character.forDigit(nibble, 16); - if (c == Character.MIN_VALUE) { - throw new IllegalArgumentException("nibble value not between 0 and 15: " + nibble); - } - return c; - } - - /** - *

- * Converts the 4 lsb of an int to a hexadecimal digit encoded using the Msb0 - * bit ordering. - *

- *

- * 0 returns '0' - *

- *

- * 1 returns '8' - *

- *

- * 10 returns '5' and so on... - *

- * - * @param nibble the 4 bits to convert - * @return a hexadecimal digit representing the 4 lsb of {@code nibble} - * @throws IllegalArgumentException if {@code nibble < 0} or {@code nibble > 15} - */ - public static char intToHexDigitMsb0(final int nibble) { - switch (nibble) { - case 0x0: - return '0'; - case 0x1: - return '8'; - case 0x2: - return '4'; - case 0x3: - return 'c'; - case 0x4: - return '2'; - case 0x5: - return 'a'; - case 0x6: - return '6'; - case 0x7: - return 'e'; - case 0x8: - return '1'; - case 0x9: - return '9'; - case 0xA: - return '5'; - case 0xB: - return 'd'; - case 0xC: - return '3'; - case 0xD: - return 'b'; - case 0xE: - return '7'; - case 0xF: - return 'f'; - default: - throw new IllegalArgumentException("nibble value not between 0 and 15: " + nibble); - } - } - - /** - *

- * Converts an array of int into a long using the default (little endian, Lsb0) - * byte and bit ordering. - *

- * - * @param src the int array to convert - * @param srcPos the position in {@code src}, in int unit, from where to start - * the conversion - * @param dstInit initial value of the destination long - * @param dstPos the position of the lsb, in bits, in the result long - * @param nInts the number of ints to convert - * @return a long containing the selected bits - * @throws IllegalArgumentException if {@code (nInts-1)*32+dstPos >= 64} - * @throws NullPointerException if {@code src} is {@code null} - * @throws ArrayIndexOutOfBoundsException if {@code srcPos + nInts > src.length} - */ - public static long intArrayToLong(final int[] src, final int srcPos, final long dstInit, final int dstPos, - final int nInts) { - if (src.length == 0 && srcPos == 0 || 0 == nInts) { - return dstInit; - } - if ((nInts - 1) * 32 + dstPos >= 64) { - throw new IllegalArgumentException("(nInts-1)*32+dstPos is greater or equal to than 64"); - } - long out = dstInit; - for (int i = 0; i < nInts; i++) { - final int shift = i * 32 + dstPos; - final long bits = (0xffffffffL & src[i + srcPos]) << shift; - final long mask = 0xffffffffL << shift; - out = (out & ~mask) | bits; - } - return out; - } - - /** - *

- * Converts an array of short into a long using the default (little endian, - * Lsb0) byte and bit ordering. - *

- * - * @param src the short array to convert - * @param srcPos the position in {@code src}, in short unit, from where to - * start the conversion - * @param dstInit initial value of the destination long - * @param dstPos the position of the lsb, in bits, in the result long - * @param nShorts the number of shorts to convert - * @return a long containing the selected bits - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if {@code (nShorts-1)*16+dstPos >= 64} - * @throws ArrayIndexOutOfBoundsException if - * {@code srcPos + nShorts > src.length} - */ - public static long shortArrayToLong(final short[] src, final int srcPos, final long dstInit, final int dstPos, - final int nShorts) { - if (src.length == 0 && srcPos == 0 || 0 == nShorts) { - return dstInit; - } - if ((nShorts - 1) * 16 + dstPos >= 64) { - throw new IllegalArgumentException("(nShorts-1)*16+dstPos is greater or equal to than 64"); - } - long out = dstInit; - for (int i = 0; i < nShorts; i++) { - final int shift = i * 16 + dstPos; - final long bits = (0xffffL & src[i + srcPos]) << shift; - final long mask = 0xffffL << shift; - out = (out & ~mask) | bits; - } - return out; - } - - /** - *

- * Converts an array of short into an int using the default (little endian, - * Lsb0) byte and bit ordering. - *

- * - * @param src the short array to convert - * @param srcPos the position in {@code src}, in short unit, from where to - * start the conversion - * @param dstInit initial value of the destination int - * @param dstPos the position of the lsb, in bits, in the result int - * @param nShorts the number of shorts to convert - * @return an int containing the selected bits - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if {@code (nShorts-1)*16+dstPos >= 32} - * @throws ArrayIndexOutOfBoundsException if - * {@code srcPos + nShorts > src.length} - */ - public static int shortArrayToInt(final short[] src, final int srcPos, final int dstInit, final int dstPos, - final int nShorts) { - if (src.length == 0 && srcPos == 0 || 0 == nShorts) { - return dstInit; - } - if ((nShorts - 1) * 16 + dstPos >= 32) { - throw new IllegalArgumentException("(nShorts-1)*16+dstPos is greater or equal to than 32"); - } - int out = dstInit; - for (int i = 0; i < nShorts; i++) { - final int shift = i * 16 + dstPos; - final int bits = (0xffff & src[i + srcPos]) << shift; - final int mask = 0xffff << shift; - out = (out & ~mask) | bits; - } - return out; - } - - /** - *

- * Converts an array of byte into a long using the default (little endian, Lsb0) - * byte and bit ordering. - *

- * - * @param src the byte array to convert - * @param srcPos the position in {@code src}, in byte unit, from where to start - * the conversion - * @param dstInit initial value of the destination long - * @param dstPos the position of the lsb, in bits, in the result long - * @param nBytes the number of bytes to convert - * @return a long containing the selected bits - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if {@code (nBytes-1)*8+dstPos >= 64} - * @throws ArrayIndexOutOfBoundsException if - * {@code srcPos + nBytes > src.length} - */ - public static long byteArrayToLong(final byte[] src, final int srcPos, final long dstInit, final int dstPos, - final int nBytes) { - if (src.length == 0 && srcPos == 0 || 0 == nBytes) { - return dstInit; - } - if ((nBytes - 1) * 8 + dstPos >= 64) { - throw new IllegalArgumentException("(nBytes-1)*8+dstPos is greater or equal to than 64"); - } - long out = dstInit; - for (int i = 0; i < nBytes; i++) { - final int shift = i * 8 + dstPos; - final long bits = (0xffL & src[i + srcPos]) << shift; - final long mask = 0xffL << shift; - out = (out & ~mask) | bits; - } - return out; - } - - /** - *

- * Converts an array of byte into an int using the default (little endian, Lsb0) - * byte and bit ordering. - *

- * - * @param src the byte array to convert - * @param srcPos the position in {@code src}, in byte unit, from where to start - * the conversion - * @param dstInit initial value of the destination int - * @param dstPos the position of the lsb, in bits, in the result int - * @param nBytes the number of bytes to convert - * @return an int containing the selected bits - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if {@code (nBytes-1)*8+dstPos >= 32} - * @throws ArrayIndexOutOfBoundsException if - * {@code srcPos + nBytes > src.length} - */ - public static int byteArrayToInt(final byte[] src, final int srcPos, final int dstInit, final int dstPos, - final int nBytes) { - if (src.length == 0 && srcPos == 0 || 0 == nBytes) { - return dstInit; - } - if ((nBytes - 1) * 8 + dstPos >= 32) { - throw new IllegalArgumentException("(nBytes-1)*8+dstPos is greater or equal to than 32"); - } - int out = dstInit; - for (int i = 0; i < nBytes; i++) { - final int shift = i * 8 + dstPos; - final int bits = (0xff & src[i + srcPos]) << shift; - final int mask = 0xff << shift; - out = (out & ~mask) | bits; - } - return out; - } - - /** - *

- * Converts an array of byte into a short using the default (little endian, - * Lsb0) byte and bit ordering. - *

- * - * @param src the byte array to convert - * @param srcPos the position in {@code src}, in byte unit, from where to start - * the conversion - * @param dstInit initial value of the destination short - * @param dstPos the position of the lsb, in bits, in the result short - * @param nBytes the number of bytes to convert - * @return a short containing the selected bits - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if {@code (nBytes-1)*8+dstPos >= 16} - * @throws ArrayIndexOutOfBoundsException if - * {@code srcPos + nBytes > src.length} - */ - public static short byteArrayToShort(final byte[] src, final int srcPos, final short dstInit, final int dstPos, - final int nBytes) { - if (src.length == 0 && srcPos == 0 || 0 == nBytes) { - return dstInit; - } - if ((nBytes - 1) * 8 + dstPos >= 16) { - throw new IllegalArgumentException("(nBytes-1)*8+dstPos is greater or equal to than 16"); - } - short out = dstInit; - for (int i = 0; i < nBytes; i++) { - final int shift = i * 8 + dstPos; - final int bits = (0xff & src[i + srcPos]) << shift; - final int mask = 0xff << shift; - out = (short) ((out & ~mask) | bits); - } - return out; - } - - /** - *

- * Converts an array of Char into a long using the default (little endian, Lsb0) - * byte and bit ordering. - *

- * - * @param src the hex string to convert - * @param srcPos the position in {@code src}, in Char unit, from where to start - * the conversion - * @param dstInit initial value of the destination long - * @param dstPos the position of the lsb, in bits, in the result long - * @param nHex the number of Chars to convert - * @return a long containing the selected bits - * @throws IllegalArgumentException if {@code (nHexs-1)*4+dstPos >= 64} - */ - public static long hexToLong(final String src, final int srcPos, final long dstInit, final int dstPos, - final int nHex) { - if (0 == nHex) { - return dstInit; - } - if ((nHex - 1) * 4 + dstPos >= 64) { - throw new IllegalArgumentException("(nHexs-1)*4+dstPos is greater or equal to than 64"); - } - long out = dstInit; - for (int i = 0; i < nHex; i++) { - final int shift = i * 4 + dstPos; - final long bits = (0xfL & hexDigitToInt(src.charAt(i + srcPos))) << shift; - final long mask = 0xfL << shift; - out = (out & ~mask) | bits; - } - return out; - } - - /** - *

- * Converts an array of Char into an int using the default (little endian, Lsb0) - * byte and bit ordering. - *

- * - * @param src the hex string to convert - * @param srcPos the position in {@code src}, in Char unit, from where to start - * the conversion - * @param dstInit initial value of the destination int - * @param dstPos the position of the lsb, in bits, in the result int - * @param nHex the number of Chars to convert - * @return an int containing the selected bits - * @throws IllegalArgumentException if {@code (nHexs-1)*4+dstPos >= 32} - */ - public static int hexToInt(final String src, final int srcPos, final int dstInit, final int dstPos, - final int nHex) { - if (0 == nHex) { - return dstInit; - } - if ((nHex - 1) * 4 + dstPos >= 32) { - throw new IllegalArgumentException("(nHexs-1)*4+dstPos is greater or equal to than 32"); - } - int out = dstInit; - for (int i = 0; i < nHex; i++) { - final int shift = i * 4 + dstPos; - final int bits = (0xf & hexDigitToInt(src.charAt(i + srcPos))) << shift; - final int mask = 0xf << shift; - out = (out & ~mask) | bits; - } - return out; - } - - /** - *

- * Converts an array of Char into a short using the default (little endian, - * Lsb0) byte and bit ordering. - *

- * - * @param src the hex string to convert - * @param srcPos the position in {@code src}, in Char unit, from where to start - * the conversion - * @param dstInit initial value of the destination short - * @param dstPos the position of the lsb, in bits, in the result short - * @param nHex the number of Chars to convert - * @return a short containing the selected bits - * @throws IllegalArgumentException if {@code (nHexs-1)*4+dstPos >= 16} - */ - public static short hexToShort(final String src, final int srcPos, final short dstInit, final int dstPos, - final int nHex) { - if (0 == nHex) { - return dstInit; - } - if ((nHex - 1) * 4 + dstPos >= 16) { - throw new IllegalArgumentException("(nHexs-1)*4+dstPos is greater or equal to than 16"); - } - short out = dstInit; - for (int i = 0; i < nHex; i++) { - final int shift = i * 4 + dstPos; - final int bits = (0xf & hexDigitToInt(src.charAt(i + srcPos))) << shift; - final int mask = 0xf << shift; - out = (short) ((out & ~mask) | bits); - } - return out; - } - - /** - *

- * Converts an array of Char into a byte using the default (little endian, Lsb0) - * byte and bit ordering. - *

- * - * @param src the hex string to convert - * @param srcPos the position in {@code src}, in Char unit, from where to start - * the conversion - * @param dstInit initial value of the destination byte - * @param dstPos the position of the lsb, in bits, in the result byte - * @param nHex the number of Chars to convert - * @return a byte containing the selected bits - * @throws IllegalArgumentException if {@code (nHexs-1)*4+dstPos >= 8} - */ - public static byte hexToByte(final String src, final int srcPos, final byte dstInit, final int dstPos, - final int nHex) { - if (0 == nHex) { - return dstInit; - } - if ((nHex - 1) * 4 + dstPos >= 8) { - throw new IllegalArgumentException("(nHexs-1)*4+dstPos is greater or equal to than 8"); - } - byte out = dstInit; - for (int i = 0; i < nHex; i++) { - final int shift = i * 4 + dstPos; - final int bits = (0xf & hexDigitToInt(src.charAt(i + srcPos))) << shift; - final int mask = 0xf << shift; - out = (byte) ((out & ~mask) | bits); - } - return out; - } - - /** - *

- * Converts binary (represented as boolean array) into a long using the default - * (little endian, Lsb0) byte and bit ordering. - *

- * - * @param src the binary to convert - * @param srcPos the position in {@code src}, in boolean unit, from where to - * start the conversion - * @param dstInit initial value of the destination long - * @param dstPos the position of the lsb, in bits, in the result long - * @param nBools the number of booleans to convert - * @return a long containing the selected bits - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if {@code nBools-1+dstPos >= 64} - * @throws ArrayIndexOutOfBoundsException if - * {@code srcPos + nBools > src.length} - */ - public static long binaryToLong(final boolean[] src, final int srcPos, final long dstInit, final int dstPos, - final int nBools) { - if (src.length == 0 && srcPos == 0 || 0 == nBools) { - return dstInit; - } - if (nBools - 1 + dstPos >= 64) { - throw new IllegalArgumentException("nBools-1+dstPos is greater or equal to than 64"); - } - long out = dstInit; - for (int i = 0; i < nBools; i++) { - final int shift = i + dstPos; - final long bits = (src[i + srcPos] ? 1L : 0) << shift; - final long mask = 0x1L << shift; - out = (out & ~mask) | bits; - } - return out; - } - - /** - *

- * Converts binary (represented as boolean array) into an int using the default - * (little endian, Lsb0) byte and bit ordering. - *

- * - * @param src the binary to convert - * @param srcPos the position in {@code src}, in boolean unit, from where to - * start the conversion - * @param dstInit initial value of the destination int - * @param dstPos the position of the lsb, in bits, in the result int - * @param nBools the number of booleans to convert - * @return an int containing the selected bits - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if {@code nBools-1+dstPos >= 32} - * @throws ArrayIndexOutOfBoundsException if - * {@code srcPos + nBools > src.length} - */ - public static int binaryToInt(final boolean[] src, final int srcPos, final int dstInit, final int dstPos, - final int nBools) { - if (src.length == 0 && srcPos == 0 || 0 == nBools) { - return dstInit; - } - if (nBools - 1 + dstPos >= 32) { - throw new IllegalArgumentException("nBools-1+dstPos is greater or equal to than 32"); - } - int out = dstInit; - for (int i = 0; i < nBools; i++) { - final int shift = i + dstPos; - final int bits = (src[i + srcPos] ? 1 : 0) << shift; - final int mask = 0x1 << shift; - out = (out & ~mask) | bits; - } - return out; - } - - /** - *

- * Converts binary (represented as boolean array) into a short using the default - * (little endian, Lsb0) byte and bit ordering. - *

- * - * @param src the binary to convert - * @param srcPos the position in {@code src}, in boolean unit, from where to - * start the conversion - * @param dstInit initial value of the destination short - * @param dstPos the position of the lsb, in bits, in the result short - * @param nBools the number of booleans to convert - * @return a short containing the selected bits - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if {@code nBools-1+dstPos >= 16} - * @throws ArrayIndexOutOfBoundsException if - * {@code srcPos + nBools > src.length} - */ - public static short binaryToShort(final boolean[] src, final int srcPos, final short dstInit, final int dstPos, - final int nBools) { - if (src.length == 0 && srcPos == 0 || 0 == nBools) { - return dstInit; - } - if (nBools - 1 + dstPos >= 16) { - throw new IllegalArgumentException("nBools-1+dstPos is greater or equal to than 16"); - } - short out = dstInit; - for (int i = 0; i < nBools; i++) { - final int shift = i + dstPos; - final int bits = (src[i + srcPos] ? 1 : 0) << shift; - final int mask = 0x1 << shift; - out = (short) ((out & ~mask) | bits); - } - return out; - } - - /** - *

- * Converts binary (represented as boolean array) into a byte using the default - * (little endian, Lsb0) byte and bit ordering. - *

- * - * @param src the binary to convert - * @param srcPos the position in {@code src}, in boolean unit, from where to - * start the conversion - * @param dstInit initial value of the destination byte - * @param dstPos the position of the lsb, in bits, in the result byte - * @param nBools the number of booleans to convert - * @return a byte containing the selected bits - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if {@code nBools-1+dstPos >= 8} - * @throws ArrayIndexOutOfBoundsException if - * {@code srcPos + nBools > src.length} - */ - public static byte binaryToByte(final boolean[] src, final int srcPos, final byte dstInit, final int dstPos, - final int nBools) { - if (src.length == 0 && srcPos == 0 || 0 == nBools) { - return dstInit; - } - if (nBools - 1 + dstPos >= 8) { - throw new IllegalArgumentException("nBools-1+dstPos is greater or equal to than 8"); - } - byte out = dstInit; - for (int i = 0; i < nBools; i++) { - final int shift = i + dstPos; - final int bits = (src[i + srcPos] ? 1 : 0) << shift; - final int mask = 0x1 << shift; - out = (byte) ((out & ~mask) | bits); - } - return out; - } - - /** - *

- * Converts a long into an array of int using the default (little endian, Lsb0) - * byte and bit ordering. - *

- * - * @param src the long to convert - * @param srcPos the position in {@code src}, in bits, from where to start the - * conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nInts the number of ints to copy to {@code dst}, must be smaller or - * equal to the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} and - * {@code nInts > 0} - * @throws IllegalArgumentException if {@code (nInts-1)*32+srcPos >= 64} - * @throws ArrayIndexOutOfBoundsException if {@code dstPos + nInts > dst.length} - */ - public static int[] longToIntArray(final long src, final int srcPos, final int[] dst, final int dstPos, - final int nInts) { - if (0 == nInts) { - return dst; - } - if ((nInts - 1) * 32 + srcPos >= 64) { - throw new IllegalArgumentException("(nInts-1)*32+srcPos is greater or equal to than 64"); - } - for (int i = 0; i < nInts; i++) { - final int shift = i * 32 + srcPos; - dst[dstPos + i] = (int) (0xffffffff & (src >> shift)); - } - return dst; - } - - /** - *

- * Converts a long into an array of short using the default (little endian, - * Lsb0) byte and bit ordering. - *

- * - * @param src the long to convert - * @param srcPos the position in {@code src}, in bits, from where to start the - * conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nShorts the number of shorts to copy to {@code dst}, must be smaller - * or equal to the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code (nShorts-1)*16+srcPos >= 64} - * @throws ArrayIndexOutOfBoundsException if - * {@code dstPos + nShorts > dst.length} - */ - public static short[] longToShortArray(final long src, final int srcPos, final short[] dst, final int dstPos, - final int nShorts) { - if (0 == nShorts) { - return dst; - } - if ((nShorts - 1) * 16 + srcPos >= 64) { - throw new IllegalArgumentException("(nShorts-1)*16+srcPos is greater or equal to than 64"); - } - for (int i = 0; i < nShorts; i++) { - final int shift = i * 16 + srcPos; - dst[dstPos + i] = (short) (0xffff & (src >> shift)); - } - return dst; - } - - /** - *

- * Converts an int into an array of short using the default (little endian, - * Lsb0) byte and bit ordering. - *

- * - * @param src the int to convert - * @param srcPos the position in {@code src}, in bits, from where to start the - * conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nShorts the number of shorts to copy to {@code dst}, must be smaller - * or equal to the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code (nShorts-1)*16+srcPos >= 32} - * @throws ArrayIndexOutOfBoundsException if - * {@code dstPos + nShorts > dst.length} - */ - public static short[] intToShortArray(final int src, final int srcPos, final short[] dst, final int dstPos, - final int nShorts) { - if (0 == nShorts) { - return dst; - } - if ((nShorts - 1) * 16 + srcPos >= 32) { - throw new IllegalArgumentException("(nShorts-1)*16+srcPos is greater or equal to than 32"); - } - for (int i = 0; i < nShorts; i++) { - final int shift = i * 16 + srcPos; - dst[dstPos + i] = (short) (0xffff & (src >> shift)); - } - return dst; - } - - /** - *

- * Converts a long into an array of byte using the default (little endian, Lsb0) - * byte and bit ordering. - *

- * - * @param src the long to convert - * @param srcPos the position in {@code src}, in bits, from where to start the - * conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nBytes the number of bytes to copy to {@code dst}, must be smaller or - * equal to the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code (nBytes-1)*8+srcPos >= 64} - * @throws ArrayIndexOutOfBoundsException if - * {@code dstPos + nBytes > dst.length} - */ - public static byte[] longToByteArray(final long src, final int srcPos, final byte[] dst, final int dstPos, - final int nBytes) { - if (0 == nBytes) { - return dst; - } - if ((nBytes - 1) * 8 + srcPos >= 64) { - throw new IllegalArgumentException("(nBytes-1)*8+srcPos is greater or equal to than 64"); - } - for (int i = 0; i < nBytes; i++) { - final int shift = i * 8 + srcPos; - dst[dstPos + i] = (byte) (0xff & (src >> shift)); - } - return dst; - } - - /** - *

- * Converts an int into an array of byte using the default (little endian, Lsb0) - * byte and bit ordering. - *

- * - * @param src the int to convert - * @param srcPos the position in {@code src}, in bits, from where to start the - * conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nBytes the number of bytes to copy to {@code dst}, must be smaller or - * equal to the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code (nBytes-1)*8+srcPos >= 32} - * @throws ArrayIndexOutOfBoundsException if - * {@code dstPos + nBytes > dst.length} - */ - public static byte[] intToByteArray(final int src, final int srcPos, final byte[] dst, final int dstPos, - final int nBytes) { - if (0 == nBytes) { - return dst; - } - if ((nBytes - 1) * 8 + srcPos >= 32) { - throw new IllegalArgumentException("(nBytes-1)*8+srcPos is greater or equal to than 32"); - } - for (int i = 0; i < nBytes; i++) { - final int shift = i * 8 + srcPos; - dst[dstPos + i] = (byte) (0xff & (src >> shift)); - } - return dst; - } - - /** - *

- * Converts a short into an array of byte using the default (little endian, - * Lsb0) byte and bit ordering. - *

- * - * @param src the short to convert - * @param srcPos the position in {@code src}, in bits, from where to start the - * conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nBytes the number of bytes to copy to {@code dst}, must be smaller or - * equal to the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code (nBytes-1)*8+srcPos >= 16} - * @throws ArrayIndexOutOfBoundsException if - * {@code dstPos + nBytes > dst.length} - */ - public static byte[] shortToByteArray(final short src, final int srcPos, final byte[] dst, final int dstPos, - final int nBytes) { - if (0 == nBytes) { - return dst; - } - if ((nBytes - 1) * 8 + srcPos >= 16) { - throw new IllegalArgumentException("(nBytes-1)*8+srcPos is greater or equal to than 16"); - } - for (int i = 0; i < nBytes; i++) { - final int shift = i * 8 + srcPos; - dst[dstPos + i] = (byte) (0xff & (src >> shift)); - } - return dst; - } - - /** - *

- * Converts a long into an array of Char using the default (little endian, Lsb0) - * byte and bit ordering. - *

- * - * @param src the long to convert - * @param srcPos the position in {@code src}, in bits, from where to start the - * conversion - * @param dstInit the initial value for the result String - * @param dstPos the position in {@code dst} where to copy the result - * @param nHexs the number of Chars to copy to {@code dst}, must be smaller or - * equal to the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws IllegalArgumentException if {@code (nHexs-1)*4+srcPos >= 64} - * @throws StringIndexOutOfBoundsException if {@code dst.init.length() < dstPos} - */ - public static String longToHex(final long src, final int srcPos, final String dstInit, final int dstPos, - final int nHexs) { - if (0 == nHexs) { - return dstInit; - } - if ((nHexs - 1) * 4 + srcPos >= 64) { - throw new IllegalArgumentException("(nHexs-1)*4+srcPos is greater or equal to than 64"); - } - final StringBuilder sb = new StringBuilder(dstInit); - int append = sb.length(); - for (int i = 0; i < nHexs; i++) { - final int shift = i * 4 + srcPos; - final int bits = (int) (0xF & (src >> shift)); - if (dstPos + i == append) { - ++append; - sb.append(intToHexDigit(bits)); - } else { - sb.setCharAt(dstPos + i, intToHexDigit(bits)); - } - } - return sb.toString(); - } - - /** - *

- * Converts an int into an array of Char using the default (little endian, Lsb0) - * byte and bit ordering. - *

- * - * @param src the int to convert - * @param srcPos the position in {@code src}, in bits, from where to start the - * conversion - * @param dstInit the initial value for the result String - * @param dstPos the position in {@code dst} where to copy the result - * @param nHexs the number of Chars to copy to {@code dst}, must be smaller or - * equal to the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws IllegalArgumentException if {@code (nHexs-1)*4+srcPos >= 32} - * @throws StringIndexOutOfBoundsException if {@code dst.init.length() < dstPos} - */ - public static String intToHex(final int src, final int srcPos, final String dstInit, final int dstPos, - final int nHexs) { - if (0 == nHexs) { - return dstInit; - } - if ((nHexs - 1) * 4 + srcPos >= 32) { - throw new IllegalArgumentException("(nHexs-1)*4+srcPos is greater or equal to than 32"); - } - final StringBuilder sb = new StringBuilder(dstInit); - int append = sb.length(); - for (int i = 0; i < nHexs; i++) { - final int shift = i * 4 + srcPos; - final int bits = 0xF & (src >> shift); - if (dstPos + i == append) { - ++append; - sb.append(intToHexDigit(bits)); - } else { - sb.setCharAt(dstPos + i, intToHexDigit(bits)); - } - } - return sb.toString(); - } - - /** - *

- * Converts a short into an array of Char using the default (little endian, - * Lsb0) byte and bit ordering. - *

- * - * @param src the short to convert - * @param srcPos the position in {@code src}, in bits, from where to start the - * conversion - * @param dstInit the initial value for the result String - * @param dstPos the position in {@code dst} where to copy the result - * @param nHexs the number of Chars to copy to {@code dst}, must be smaller or - * equal to the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws IllegalArgumentException if {@code (nHexs-1)*4+srcPos >= 16} - * @throws StringIndexOutOfBoundsException if {@code dst.init.length() < dstPos} - */ - public static String shortToHex(final short src, final int srcPos, final String dstInit, final int dstPos, - final int nHexs) { - if (0 == nHexs) { - return dstInit; - } - if ((nHexs - 1) * 4 + srcPos >= 16) { - throw new IllegalArgumentException("(nHexs-1)*4+srcPos is greater or equal to than 16"); - } - final StringBuilder sb = new StringBuilder(dstInit); - int append = sb.length(); - for (int i = 0; i < nHexs; i++) { - final int shift = i * 4 + srcPos; - final int bits = 0xF & (src >> shift); - if (dstPos + i == append) { - ++append; - sb.append(intToHexDigit(bits)); - } else { - sb.setCharAt(dstPos + i, intToHexDigit(bits)); - } - } - return sb.toString(); - } - - /** - *

- * Converts a byte into an array of Char using the default (little endian, Lsb0) - * byte and bit ordering. - *

- * - * @param src the byte to convert - * @param srcPos the position in {@code src}, in bits, from where to start the - * conversion - * @param dstInit the initial value for the result String - * @param dstPos the position in {@code dst} where to copy the result - * @param nHexs the number of Chars to copy to {@code dst}, must be smaller or - * equal to the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws IllegalArgumentException if {@code (nHexs-1)*4+srcPos >= 8} - * @throws StringIndexOutOfBoundsException if {@code dst.init.length() < dstPos} - */ - public static String byteToHex(final byte src, final int srcPos, final String dstInit, final int dstPos, - final int nHexs) { - if (0 == nHexs) { - return dstInit; - } - if ((nHexs - 1) * 4 + srcPos >= 8) { - throw new IllegalArgumentException("(nHexs-1)*4+srcPos is greater or equal to than 8"); - } - final StringBuilder sb = new StringBuilder(dstInit); - int append = sb.length(); - for (int i = 0; i < nHexs; i++) { - final int shift = i * 4 + srcPos; - final int bits = 0xF & (src >> shift); - if (dstPos + i == append) { - ++append; - sb.append(intToHexDigit(bits)); - } else { - sb.setCharAt(dstPos + i, intToHexDigit(bits)); - } - } - return sb.toString(); - } - - /** - *

- * Converts a long into an array of boolean using the default (little endian, - * Lsb0) byte and bit ordering. - *

- * - * @param src the long to convert - * @param srcPos the position in {@code src}, in bits, from where to start the - * conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nBools the number of booleans to copy to {@code dst}, must be smaller - * or equal to the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code nBools-1+srcPos >= 64} - * @throws ArrayIndexOutOfBoundsException if - * {@code dstPos + nBools > dst.length} - */ - public static boolean[] longToBinary(final long src, final int srcPos, final boolean[] dst, final int dstPos, - final int nBools) { - if (0 == nBools) { - return dst; - } - if (nBools - 1 + srcPos >= 64) { - throw new IllegalArgumentException("nBools-1+srcPos is greater or equal to than 64"); - } - for (int i = 0; i < nBools; i++) { - final int shift = i + srcPos; - dst[dstPos + i] = (0x1 & (src >> shift)) != 0; - } - return dst; - } - - /** - *

- * Converts an int into an array of boolean using the default (little endian, - * Lsb0) byte and bit ordering. - *

- * - * @param src the int to convert - * @param srcPos the position in {@code src}, in bits, from where to start the - * conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nBools the number of booleans to copy to {@code dst}, must be smaller - * or equal to the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code nBools-1+srcPos >= 32} - * @throws ArrayIndexOutOfBoundsException if - * {@code dstPos + nBools > dst.length} - */ - public static boolean[] intToBinary(final int src, final int srcPos, final boolean[] dst, final int dstPos, - final int nBools) { - if (0 == nBools) { - return dst; - } - if (nBools - 1 + srcPos >= 32) { - throw new IllegalArgumentException("nBools-1+srcPos is greater or equal to than 32"); - } - for (int i = 0; i < nBools; i++) { - final int shift = i + srcPos; - dst[dstPos + i] = (0x1 & (src >> shift)) != 0; - } - return dst; - } - - /** - *

- * Converts a short into an array of boolean using the default (little endian, - * Lsb0) byte and bit ordering. - *

- * - * @param src the short to convert - * @param srcPos the position in {@code src}, in bits, from where to start the - * conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nBools the number of booleans to copy to {@code dst}, must be smaller - * or equal to the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code nBools-1+srcPos >= 16} - * @throws ArrayIndexOutOfBoundsException if - * {@code dstPos + nBools > dst.length} - */ - public static boolean[] shortToBinary(final short src, final int srcPos, final boolean[] dst, final int dstPos, - final int nBools) { - if (0 == nBools) { - return dst; - } - if (nBools - 1 + srcPos >= 16) { - throw new IllegalArgumentException("nBools-1+srcPos is greater or equal to than 16"); - } - assert (nBools - 1) < 16 - srcPos; - for (int i = 0; i < nBools; i++) { - final int shift = i + srcPos; - dst[dstPos + i] = (0x1 & (src >> shift)) != 0; - } - return dst; - } - - /** - *

- * Converts a byte into an array of boolean using the default (little endian, - * Lsb0) byte and bit ordering. - *

- * - * @param src the byte to convert - * @param srcPos the position in {@code src}, in bits, from where to start the - * conversion - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nBools the number of booleans to copy to {@code dst}, must be smaller - * or equal to the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code nBools-1+srcPos >= 8} - * @throws ArrayIndexOutOfBoundsException if - * {@code dstPos + nBools > dst.length} - */ - public static boolean[] byteToBinary(final byte src, final int srcPos, final boolean[] dst, final int dstPos, - final int nBools) { - if (0 == nBools) { - return dst; - } - if (nBools - 1 + srcPos >= 8) { - throw new IllegalArgumentException("nBools-1+srcPos is greater or equal to than 8"); - } - for (int i = 0; i < nBools; i++) { - final int shift = i + srcPos; - dst[dstPos + i] = (0x1 & (src >> shift)) != 0; - } - return dst; - } - - /** - *

- * Converts UUID into an array of byte using the default (little endian, Lsb0) - * byte and bit ordering. - *

- * - * @param src the UUID to convert - * @param dst the destination array - * @param dstPos the position in {@code dst} where to copy the result - * @param nBytes the number of bytes to copy to {@code dst}, must be smaller or - * equal to the width of the input (from srcPos to msb) - * @return {@code dst} - * @throws NullPointerException if {@code dst} is {@code null} - * @throws IllegalArgumentException if {@code nBytes > 16} - * @throws ArrayIndexOutOfBoundsException if - * {@code dstPos + nBytes > dst.length} - */ - public static byte[] uuidToByteArray(final EaglercraftUUID src, final byte[] dst, final int dstPos, final int nBytes) { - if (0 == nBytes) { - return dst; - } - if (nBytes > 16) { - throw new IllegalArgumentException("nBytes is greater than 16"); - } - longToByteArray(src.getMostSignificantBits(), 0, dst, dstPos, Math.min(nBytes, 8)); - if (nBytes >= 8) { - longToByteArray(src.getLeastSignificantBits(), 0, dst, dstPos + 8, nBytes - 8); - } - return dst; - } - - /** - *

- * Converts bytes from an array into a UUID using the default (little endian, - * Lsb0) byte and bit ordering. - *

- * - * @param src the byte array to convert - * @param srcPos the position in {@code src} where to copy the result from - * @return a UUID - * @throws NullPointerException if {@code src} is {@code null} - * @throws IllegalArgumentException if array does not contain at least 16 bytes - * beginning with {@code srcPos} - */ - public static EaglercraftUUID byteArrayToUuid(final byte[] src, final int srcPos) { - if (src.length - srcPos < 16) { - throw new IllegalArgumentException("Need at least 16 bytes for UUID"); - } - return new EaglercraftUUID(byteArrayToLong(src, srcPos, 0, 0, 8), byteArrayToLong(src, srcPos + 8, 0, 0, 8)); - } -} diff --git a/src/main/java/org/apache/commons/lang3/EnumUtils.java b/src/main/java/org/apache/commons/lang3/EnumUtils.java deleted file mode 100755 index cf8634b1..00000000 --- a/src/main/java/org/apache/commons/lang3/EnumUtils.java +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -/** - *

- * Utility library to provide helper methods for Java enums. - *

- * - *

- * #ThreadSafe# - *

- * - * @since 3.0 - */ -public class EnumUtils { - - private static final String NULL_ELEMENTS_NOT_PERMITTED = "null elements not permitted"; - private static final String CANNOT_STORE_S_S_VALUES_IN_S_BITS = "Cannot store %s %s values in %s bits"; - private static final String S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE = "%s does not seem to be an Enum type"; - private static final String ENUM_CLASS_MUST_BE_DEFINED = "EnumClass must be defined."; - - /** - * Validate {@code enumClass}. - * - * @param the type of the enumeration - * @param enumClass to check - * @return {@code enumClass} - * @throws NullPointerException if {@code enumClass} is {@code null} - * @throws IllegalArgumentException if {@code enumClass} is not an enum class - * @since 3.2 - */ - private static > Class asEnum(final Class enumClass) { - Validate.notNull(enumClass, ENUM_CLASS_MUST_BE_DEFINED); - Validate.isTrue(enumClass.isEnum(), S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE, enumClass); - return enumClass; - } - - /** - * Validate that {@code enumClass} is compatible with representation in a - * {@code long}. - * - * @param the type of the enumeration - * @param enumClass to check - * @return {@code enumClass} - * @throws NullPointerException if {@code enumClass} is {@code null} - * @throws IllegalArgumentException if {@code enumClass} is not an enum class or - * has more than 64 values - * @since 3.0.1 - */ - private static > Class checkBitVectorable(final Class enumClass) { - final E[] constants = asEnum(enumClass).getEnumConstants(); - Validate.isTrue(constants.length <= Long.SIZE, CANNOT_STORE_S_S_VALUES_IN_S_BITS, - Integer.valueOf(constants.length), enumClass.getSimpleName(), Integer.valueOf(Long.SIZE)); - - return enumClass; - } - - /** - *

- * Creates a long bit vector representation of the given array of Enum values. - *

- * - *

- * This generates a value that is usable by {@link EnumUtils#processBitVector}. - *

- * - *

- * Do not use this method if you have more than 64 values in your Enum, as this - * would create a value greater than a long can hold. - *

- * - * @param enumClass the class of the enum we are working with, not {@code null} - * @param values the values we want to convert, not {@code null} - * @param the type of the enumeration - * @return a long whose value provides a binary representation of the given set - * of enum values. - * @throws NullPointerException if {@code enumClass} or {@code values} is - * {@code null} - * @throws IllegalArgumentException if {@code enumClass} is not an enum class or - * has more than 64 values - * @since 3.0.1 - * @see #generateBitVectors(Class, Iterable) - */ - @SafeVarargs - public static > long generateBitVector(final Class enumClass, final E... values) { - Validate.noNullElements(values); - return generateBitVector(enumClass, Arrays.asList(values)); - } - - /** - *

- * Creates a long bit vector representation of the given subset of an Enum. - *

- * - *

- * This generates a value that is usable by {@link EnumUtils#processBitVector}. - *

- * - *

- * Do not use this method if you have more than 64 values in your Enum, as this - * would create a value greater than a long can hold. - *

- * - * @param enumClass the class of the enum we are working with, not {@code null} - * @param values the values we want to convert, not {@code null}, neither - * containing {@code null} - * @param the type of the enumeration - * @return a long whose value provides a binary representation of the given set - * of enum values. - * @throws NullPointerException if {@code enumClass} or {@code values} is - * {@code null} - * @throws IllegalArgumentException if {@code enumClass} is not an enum class or - * has more than 64 values, or if any - * {@code values} {@code null} - * @since 3.0.1 - * @see #generateBitVectors(Class, Iterable) - */ - public static > long generateBitVector(final Class enumClass, - final Iterable values) { - checkBitVectorable(enumClass); - Validate.notNull(values); - long total = 0; - for (final E constant : values) { - Validate.notNull(constant, NULL_ELEMENTS_NOT_PERMITTED); - total |= 1L << constant.ordinal(); - } - return total; - } - - /** - *

- * Gets the enum for the class, returning {@code null} if not found. - *

- * - *

- * This method differs from {@link Enum#valueOf} in that it does not throw an - * exception for an invalid enum name. - *

- * - * @param the type of the enumeration - * @param enumClass the class of the enum to query, not null - * @param enumName the enum name, null returns null - * @return the enum, null if not found - */ - public static > E getEnum(final Class enumClass, final String enumName) { - return getEnum(enumClass, enumName, null); - } - - /** - *

- * Gets the enum for the class, returning {@code defaultEnum} if not found. - *

- * - *

- * This method differs from {@link Enum#valueOf} in that it does not throw an - * exception for an invalid enum name. - *

- * - * @param the type of the enumeration - * @param enumClass the class of the enum to query, not null - * @param enumName the enum name, null returns default enum - * @param defaultEnum the default enum - * @return the enum, default enum if not found - * @since 3.10 - */ - public static > E getEnum(final Class enumClass, final String enumName, final E defaultEnum) { - if (enumName == null) { - return defaultEnum; - } - try { - return Enum.valueOf(enumClass, enumName); - } catch (final IllegalArgumentException ex) { - return defaultEnum; - } - } - - /** - *

- * Gets the enum for the class, returning {@code null} if not found. - *

- * - *

- * This method differs from {@link Enum#valueOf} in that it does not throw an - * exception for an invalid enum name and performs case insensitive matching of - * the name. - *

- * - * @param the type of the enumeration - * @param enumClass the class of the enum to query, not null - * @param enumName the enum name, null returns null - * @return the enum, null if not found - * @since 3.8 - */ - public static > E getEnumIgnoreCase(final Class enumClass, final String enumName) { - return getEnumIgnoreCase(enumClass, enumName, null); - } - - /** - *

- * Gets the enum for the class, returning {@code defaultEnum} if not found. - *

- * - *

- * This method differs from {@link Enum#valueOf} in that it does not throw an - * exception for an invalid enum name and performs case insensitive matching of - * the name. - *

- * - * @param the type of the enumeration - * @param enumClass the class of the enum to query, not null - * @param enumName the enum name, null returns default enum - * @param defaultEnum the default enum - * @return the enum, default enum if not found - * @since 3.10 - */ - public static > E getEnumIgnoreCase(final Class enumClass, final String enumName, - final E defaultEnum) { - if (enumName == null || !enumClass.isEnum()) { - return defaultEnum; - } - for (final E each : enumClass.getEnumConstants()) { - if (each.name().equalsIgnoreCase(enumName)) { - return each; - } - } - return defaultEnum; - } - - /** - *

- * Gets the {@code List} of enums. - *

- * - *

- * This method is useful when you need a list of enums rather than an array. - *

- * - * @param the type of the enumeration - * @param enumClass the class of the enum to query, not null - * @return the modifiable list of enums, never null - */ - public static > List getEnumList(final Class enumClass) { - return new ArrayList<>(Arrays.asList(enumClass.getEnumConstants())); - } - - /** - *

- * Gets the {@code Map} of enums by name. - *

- * - *

- * This method is useful when you need a map of enums by name. - *

- * - * @param the type of the enumeration - * @param enumClass the class of the enum to query, not null - * @return the modifiable map of enum names to enums, never null - */ - public static > Map getEnumMap(final Class enumClass) { - final Map map = new LinkedHashMap<>(); - for (final E e : enumClass.getEnumConstants()) { - map.put(e.name(), e); - } - return map; - } - - /** - *

- * Checks if the specified name is a valid enum for the class. - *

- * - *

- * This method differs from {@link Enum#valueOf} in that checks if the name is a - * valid enum without needing to catch the exception. - *

- * - * @param the type of the enumeration - * @param enumClass the class of the enum to query, not null - * @param enumName the enum name, null returns false - * @return true if the enum name is valid, otherwise false - */ - public static > boolean isValidEnum(final Class enumClass, final String enumName) { - return getEnum(enumClass, enumName) != null; - } - - /** - *

- * Checks if the specified name is a valid enum for the class. - *

- * - *

- * This method differs from {@link Enum#valueOf} in that checks if the name is a - * valid enum without needing to catch the exception and performs case - * insensitive matching of the name. - *

- * - * @param the type of the enumeration - * @param enumClass the class of the enum to query, not null - * @param enumName the enum name, null returns false - * @return true if the enum name is valid, otherwise false - * @since 3.8 - */ - public static > boolean isValidEnumIgnoreCase(final Class enumClass, final String enumName) { - return getEnumIgnoreCase(enumClass, enumName) != null; - } - - /** - * This constructor is public to permit tools that require a JavaBean instance - * to operate. - */ - public EnumUtils() { - } -} diff --git a/src/main/java/org/apache/commons/lang3/Functions.java b/src/main/java/org/apache/commons/lang3/Functions.java deleted file mode 100755 index fec7929a..00000000 --- a/src/main/java/org/apache/commons/lang3/Functions.java +++ /dev/null @@ -1,757 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.lang.reflect.UndeclaredThrowableException; -import java.util.Collection; -import java.util.Objects; -import java.util.concurrent.Callable; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.BiPredicate; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.stream.Stream; - -import org.apache.commons.lang3.Streams.FailableStream; -import org.apache.commons.lang3.function.FailableBooleanSupplier; - -/** - * This class provides utility functions, and classes for working with the - * {@code java.util.function} package, or more generally, with Java 8 lambdas. - * More specifically, it attempts to address the fact that lambdas are supposed - * not to throw Exceptions, at least not checked Exceptions, AKA instances of - * {@link Exception}. This enforces the use of constructs like: - * - *
- * {
- * 	@code
- * 	Consumer consumer = m -> {
- * 		try {
- * 			m.invoke(o, args);
- * 		} catch (Throwable t) {
- * 			throw Functions.rethrow(t);
- * 		}
- * 	};
- * }
- * 
- * - *

- * By replacing a {@link java.util.function.Consumer Consumer<O>} with a - * {@link FailableConsumer FailableConsumer<O,? extends Throwable>}, this - * can be written like follows: - *

- * - *
- * {@code
- *   Functions.accept((m) -> m.invoke(o,args));
- * }
- * 
- * - *

- * Obviously, the second version is much more concise and the spirit of Lambda - * expressions is met better than the second version. - *

- * - * @since 3.9 - * @deprecated Use {@link org.apache.commons.lang3.function.Failable}. - */ -@Deprecated -public class Functions { - - /** - * A functional interface like {@link BiConsumer} that declares a - * {@code Throwable}. - * - *

- * TODO for 4.0: Move to org.apache.commons.lang3.function. - *

- * - * @param Consumed type 1. - * @param Consumed type 2. - * @param Thrown exception. - * @deprecated Use {@link org.apache.commons.lang3.function.FailableBiConsumer}. - */ - @Deprecated - @FunctionalInterface - public interface FailableBiConsumer { - - /** - * Accepts the consumer. - * - * @param object1 the first parameter for the consumable to accept - * @param object2 the second parameter for the consumable to accept - * @throws T Thrown when the consumer fails. - */ - void accept(O1 object1, O2 object2) throws T; - } - - /** - * A functional interface like {@link BiFunction} that declares a - * {@code Throwable}. - * - *

- * TODO for 4.0: Move to org.apache.commons.lang3.function. - *

- * - * @param Input type 1. - * @param Input type 2. - * @param Return type. - * @param Thrown exception. - * @deprecated Use {@link org.apache.commons.lang3.function.FailableBiFunction}. - */ - @Deprecated - @FunctionalInterface - public interface FailableBiFunction { - - /** - * Applies this function. - * - * @param input1 the first input for the function - * @param input2 the second input for the function - * @return the result of the function - * @throws T Thrown when the function fails. - */ - R apply(O1 input1, O2 input2) throws T; - } - - /** - * A functional interface like {@link BiPredicate} that declares a - * {@code Throwable}. - * - *

- * TODO for 4.0: Move to org.apache.commons.lang3.function. - *

- * - * @param Predicate type 1. - * @param Predicate type 2. - * @param Thrown exception. - * @deprecated Use - * {@link org.apache.commons.lang3.function.FailableBiPredicate}. - */ - @Deprecated - @FunctionalInterface - public interface FailableBiPredicate { - - /** - * Tests the predicate. - * - * @param object1 the first object to test the predicate on - * @param object2 the second object to test the predicate on - * @return the predicate's evaluation - * @throws T if the predicate fails - */ - boolean test(O1 object1, O2 object2) throws T; - } - - /** - * A functional interface like {@link java.util.concurrent.Callable} that - * declares a {@code Throwable}. - * - *

- * TODO for 4.0: Move to org.apache.commons.lang3.function. - *

- * - * @param Return type. - * @param Thrown exception. - * @deprecated Use {@link org.apache.commons.lang3.function.FailableCallable}. - */ - @Deprecated - @FunctionalInterface - public interface FailableCallable { - - /** - * Calls the callable. - * - * @return The value returned from the callable - * @throws T if the callable fails - */ - R call() throws T; - } - - /** - * A functional interface like {@link Consumer} that declares a - * {@code Throwable}. - * - *

- * TODO for 4.0: Move to org.apache.commons.lang3.function. - *

- * - * @param Consumed type 1. - * @param Thrown exception. - * @deprecated Use {@link org.apache.commons.lang3.function.FailableConsumer}. - */ - @Deprecated - @FunctionalInterface - public interface FailableConsumer { - - /** - * Accepts the consumer. - * - * @param object the parameter for the consumable to accept - * @throws T Thrown when the consumer fails. - */ - void accept(O object) throws T; - } - - /** - * A functional interface like {@link Function} that declares a - * {@code Throwable}. - * - *

- * TODO for 4.0: Move to org.apache.commons.lang3.function. - *

- * - * @param Input type 1. - * @param Return type. - * @param Thrown exception. - * @deprecated Use {@link org.apache.commons.lang3.function.FailableFunction}. - */ - @Deprecated - @FunctionalInterface - public interface FailableFunction { - - /** - * Applies this function. - * - * @param input the input for the function - * @return the result of the function - * @throws T Thrown when the function fails. - */ - R apply(I input) throws T; - } - - /** - * A functional interface like {@link Predicate} that declares a - * {@code Throwable}. - * - *

- * TODO for 4.0: Move to org.apache.commons.lang3.function. - *

- * - * @param Predicate type 1. - * @param Thrown exception. - * @deprecated Use {@link org.apache.commons.lang3.function.FailablePredicate}. - */ - @Deprecated - @FunctionalInterface - public interface FailablePredicate { - - /** - * Tests the predicate. - * - * @param object the object to test the predicate on - * @return the predicate's evaluation - * @throws T if the predicate fails - */ - boolean test(I object) throws T; - } - - /** - * A functional interface like {@link Runnable} that declares a - * {@code Throwable}. - * - *

- * TODO for 4.0: Move to org.apache.commons.lang3.function. - *

- * - * @param Thrown exception. - * @deprecated Use {@link org.apache.commons.lang3.function.FailableRunnable}. - */ - @Deprecated - @FunctionalInterface - public interface FailableRunnable { - - /** - * Runs the function. - * - * @throws T Thrown when the function fails. - */ - void run() throws T; - } - - /** - * A functional interface like {@link Supplier} that declares a - * {@code Throwable}. - * - *

- * TODO for 4.0: Move to org.apache.commons.lang3.function. - *

- * - * @param Return type. - * @param Thrown exception. - * @deprecated Use {@link org.apache.commons.lang3.function.FailableSupplier}. - */ - @Deprecated - @FunctionalInterface - public interface FailableSupplier { - - /** - * Supplies an object - * - * @return a result - * @throws T if the supplier fails - */ - R get() throws T; - } - - /** - * Consumes a consumer and rethrows any exception as a {@link RuntimeException}. - * - * @param consumer the consumer to consume - * @param object1 the first object to consume by {@code consumer} - * @param object2 the second object to consume by {@code consumer} - * @param the type of the first argument the consumer accepts - * @param the type of the second argument the consumer accepts - * @param the type of checked exception the consumer may throw - */ - public static void accept(final FailableBiConsumer consumer, - final O1 object1, final O2 object2) { - run(() -> consumer.accept(object1, object2)); - } - - /** - * Consumes a consumer and rethrows any exception as a {@link RuntimeException}. - * - * @param consumer the consumer to consume - * @param object the object to consume by {@code consumer} - * @param the type the consumer accepts - * @param the type of checked exception the consumer may throw - */ - public static void accept(final FailableConsumer consumer, final O object) { - run(() -> consumer.accept(object)); - } - - /** - * Applies a function and rethrows any exception as a {@link RuntimeException}. - * - * @param function the function to apply - * @param input1 the first input to apply {@code function} on - * @param input2 the second input to apply {@code function} on - * @param the type of the first argument the function accepts - * @param the type of the second argument the function accepts - * @param the return type of the function - * @param the type of checked exception the function may throw - * @return the value returned from the function - */ - public static O apply(final FailableBiFunction function, - final O1 input1, final O2 input2) { - return get(() -> function.apply(input1, input2)); - } - - /** - * Applies a function and rethrows any exception as a {@link RuntimeException}. - * - * @param function the function to apply - * @param input the input to apply {@code function} on - * @param the type of the argument the function accepts - * @param the return type of the function - * @param the type of checked exception the function may throw - * @return the value returned from the function - */ - public static O apply(final FailableFunction function, final I input) { - return get(() -> function.apply(input)); - } - - /** - * Converts the given {@link FailableBiConsumer} into a standard - * {@link BiConsumer}. - * - * @param the type of the first argument of the consumers - * @param the type of the second argument of the consumers - * @param consumer a failable {@code BiConsumer} - * @return a standard {@code BiConsumer} - * @since 3.10 - */ - public static BiConsumer asBiConsumer(final FailableBiConsumer consumer) { - return (input1, input2) -> accept(consumer, input1, input2); - } - - /** - * Converts the given {@link FailableBiFunction} into a standard - * {@link BiFunction}. - * - * @param the type of the first argument of the input of the functions - * @param the type of the second argument of the input of the functions - * @param the type of the output of the functions - * @param function a {@code FailableBiFunction} - * @return a standard {@code BiFunction} - * @since 3.10 - */ - public static BiFunction asBiFunction(final FailableBiFunction function) { - return (input1, input2) -> apply(function, input1, input2); - } - - /** - * Converts the given {@link FailableBiPredicate} into a standard - * {@link BiPredicate}. - * - * @param the type of the first argument used by the predicates - * @param the type of the second argument used by the predicates - * @param predicate a {@code FailableBiPredicate} - * @return a standard {@code BiPredicate} - * @since 3.10 - */ - public static BiPredicate asBiPredicate(final FailableBiPredicate predicate) { - return (input1, input2) -> test(predicate, input1, input2); - } - - /** - * Converts the given {@link FailableCallable} into a standard {@link Callable}. - * - * @param the type used by the callables - * @param callable a {@code FailableCallable} - * @return a standard {@code Callable} - * @since 3.10 - */ - public static Callable asCallable(final FailableCallable callable) { - return () -> call(callable); - } - - /** - * Converts the given {@link FailableConsumer} into a standard {@link Consumer}. - * - * @param the type used by the consumers - * @param consumer a {@code FailableConsumer} - * @return a standard {@code Consumer} - * @since 3.10 - */ - public static Consumer asConsumer(final FailableConsumer consumer) { - return input -> accept(consumer, input); - } - - /** - * Converts the given {@link FailableFunction} into a standard {@link Function}. - * - * @param the type of the input of the functions - * @param the type of the output of the functions - * @param function a {code FailableFunction} - * @return a standard {@code Function} - * @since 3.10 - */ - public static Function asFunction(final FailableFunction function) { - return input -> apply(function, input); - } - - /** - * Converts the given {@link FailablePredicate} into a standard - * {@link Predicate}. - * - * @param the type used by the predicates - * @param predicate a {@code FailablePredicate} - * @return a standard {@code Predicate} - * @since 3.10 - */ - public static Predicate asPredicate(final FailablePredicate predicate) { - return input -> test(predicate, input); - } - - /** - * Converts the given {@link FailableRunnable} into a standard {@link Runnable}. - * - * @param runnable a {@code FailableRunnable} - * @return a standard {@code Runnable} - * @since 3.10 - */ - public static Runnable asRunnable(final FailableRunnable runnable) { - return () -> run(runnable); - } - - /** - * Converts the given {@link FailableSupplier} into a standard {@link Supplier}. - * - * @param the type supplied by the suppliers - * @param supplier a {@code FailableSupplier} - * @return a standard {@code Supplier} - * @since 3.10 - */ - public static Supplier asSupplier(final FailableSupplier supplier) { - return () -> get(supplier); - } - - /** - * Calls a callable and rethrows any exception as a {@link RuntimeException}. - * - * @param callable the callable to call - * @param the return type of the callable - * @param the type of checked exception the callable may throw - * @return the value returned from the callable - */ - public static O call(final FailableCallable callable) { - return get(callable::call); - } - - /** - * Invokes a supplier, and returns the result. - * - * @param supplier The supplier to invoke. - * @param The suppliers output type. - * @param The type of checked exception, which the supplier can throw. - * @return The object, which has been created by the supplier - * @since 3.10 - */ - public static O get(final FailableSupplier supplier) { - try { - return supplier.get(); - } catch (final Throwable t) { - throw rethrow(t); - } - } - - /** - * Invokes a boolean supplier, and returns the result. - * - * @param supplier The boolean supplier to invoke. - * @param The type of checked exception, which the supplier can throw. - * @return The boolean, which has been created by the supplier - */ - private static boolean getAsBoolean(final FailableBooleanSupplier supplier) { - try { - return supplier.getAsBoolean(); - } catch (final Throwable t) { - throw rethrow(t); - } - } - - /** - *

- * Rethrows a {@link Throwable} as an unchecked exception. If the argument is - * already unchecked, namely a {@code RuntimeException} or {@code Error} then - * the argument will be rethrown without modification. If the exception is - * {@code IOException} then it will be wrapped into a - * {@code UncheckedIOException}. In every other cases the exception will be - * wrapped into a {@code - * UndeclaredThrowableException} - *

- * - *

- * Note that there is a declared return type for this method, even though it - * never returns. The reason for that is to support the usual pattern: - *

- * - *
-	 * throw rethrow(myUncheckedException);
-	 * 
- * - *

- * instead of just calling the method. This pattern may help the Java compiler - * to recognize that at that point an exception will be thrown and the code flow - * analysis will not demand otherwise mandatory commands that could follow the - * method call, like a {@code return} statement from a value returning method. - *

- * - * @param throwable The throwable to rethrow ossibly wrapped into an unchecked - * exception - * @return Never returns anything, this method never terminates normally. - */ - public static RuntimeException rethrow(final Throwable throwable) { - Objects.requireNonNull(throwable, "throwable"); - if (throwable instanceof RuntimeException) { - throw (RuntimeException) throwable; - } else if (throwable instanceof Error) { - throw (Error) throwable; - } else if (throwable instanceof IOException) { - throw new UncheckedIOException((IOException) throwable); - } else { - throw new UndeclaredThrowableException(throwable); - } - } - - /** - * Runs a runnable and rethrows any exception as a {@link RuntimeException}. - * - * @param runnable The runnable to run - * @param the type of checked exception the runnable may throw - */ - public static void run(final FailableRunnable runnable) { - try { - runnable.run(); - } catch (final Throwable t) { - throw rethrow(t); - } - } - - /** - * Converts the given collection into a {@link FailableStream}. The - * {@link FailableStream} consists of the collections elements. Shortcut for - * - *
-	 * Functions.stream(collection.stream());
-	 * 
- * - * @param collection The collection, which is being converted into a - * {@link FailableStream}. - * @param The collections element type. (In turn, the result streams - * element type.) - * @return The created {@link FailableStream}. - * @since 3.10 - */ - public static FailableStream stream(final Collection collection) { - return new FailableStream<>(collection.stream()); - } - - /** - * Converts the given stream into a {@link FailableStream}. The - * {@link FailableStream} consists of the same elements, than the input stream. - * However, failable lambdas, like {@link FailablePredicate}, - * {@link FailableFunction}, and {@link FailableConsumer} may be applied, rather - * than {@link Predicate}, {@link Function}, {@link Consumer}, etc. - * - * @param stream The stream, which is being converted into a - * {@link FailableStream}. - * @param The streams element type. - * @return The created {@link FailableStream}. - * @since 3.10 - */ - public static FailableStream stream(final Stream stream) { - return new FailableStream<>(stream); - } - - /** - * Tests a predicate and rethrows any exception as a {@link RuntimeException}. - * - * @param predicate the predicate to test - * @param object1 the first input to test by {@code predicate} - * @param object2 the second input to test by {@code predicate} - * @param the type of the first argument the predicate tests - * @param the type of the second argument the predicate tests - * @param the type of checked exception the predicate may throw - * @return the boolean value returned by the predicate - */ - public static boolean test(final FailableBiPredicate predicate, - final O1 object1, final O2 object2) { - return getAsBoolean(() -> predicate.test(object1, object2)); - } - - /** - * Tests a predicate and rethrows any exception as a {@link RuntimeException}. - * - * @param predicate the predicate to test - * @param object the input to test by {@code predicate} - * @param the type of argument the predicate tests - * @param the type of checked exception the predicate may throw - * @return the boolean value returned by the predicate - */ - public static boolean test(final FailablePredicate predicate, final O object) { - return getAsBoolean(() -> predicate.test(object)); - } - - /** - * A simple try-with-resources implementation, that can be used, if your objects - * do not implement the {@link AutoCloseable} interface. The method executes the - * {@code action}. The method guarantees, that all the - * {@code resources} are being executed, in the given order, afterwards, and - * regardless of success, or failure. If either the original action, or any of - * the resource action fails, then the first failure (AKA - * {@link Throwable} is rethrown. Example use: - * - *
-	 * {
-	 * 	@code
-	 * 	final FileInputStream fis = new FileInputStream("my.file");
-	 * 	Functions.tryWithResources(useInputStream(fis), null, () -> fis.close());
-	 * }
-	 * 
- * - * @param action The action to execute. This object will always - * be invoked. - * @param errorHandler An optional error handler, which will be invoked finally, - * if any error occurred. The error handler will receive the - * first error, AKA {@link Throwable}. - * @param resources The resource actions to execute. All resource - * actions will be invoked, in the given order. A resource - * action is an instance of {@link FailableRunnable}, which - * will be executed. - * @see #tryWithResources(FailableRunnable, FailableRunnable...) - */ - @SafeVarargs - public static void tryWithResources(final FailableRunnable action, - final FailableConsumer errorHandler, - final FailableRunnable... resources) { - final FailableConsumer actualErrorHandler; - if (errorHandler == null) { - actualErrorHandler = Functions::rethrow; - } else { - actualErrorHandler = errorHandler; - } - if (resources != null) { - for (final FailableRunnable failableRunnable : resources) { - Objects.requireNonNull(failableRunnable, "runnable"); - } - } - Throwable th = null; - try { - action.run(); - } catch (final Throwable t) { - th = t; - } - if (resources != null) { - for (final FailableRunnable runnable : resources) { - try { - runnable.run(); - } catch (final Throwable t) { - if (th == null) { - th = t; - } - } - } - } - if (th != null) { - try { - actualErrorHandler.accept(th); - } catch (final Throwable t) { - throw rethrow(t); - } - } - } - - /** - * A simple try-with-resources implementation, that can be used, if your objects - * do not implement the {@link AutoCloseable} interface. The method executes the - * {@code action}. The method guarantees, that all the - * {@code resources} are being executed, in the given order, afterwards, and - * regardless of success, or failure. If either the original action, or any of - * the resource action fails, then the first failure (AKA - * {@link Throwable} is rethrown. Example use: - * - *
-	 * {
-	 * 	@code
-	 * 	final FileInputStream fis = new FileInputStream("my.file");
-	 * 	Functions.tryWithResources(useInputStream(fis), () -> fis.close());
-	 * }
-	 * 
- * - * @param action The action to execute. This object will always be - * invoked. - * @param resources The resource actions to execute. All resource - * actions will be invoked, in the given order. A resource - * action is an instance of {@link FailableRunnable}, which - * will be executed. - * @see #tryWithResources(FailableRunnable, FailableConsumer, - * FailableRunnable...) - */ - @SafeVarargs - public static void tryWithResources(final FailableRunnable action, - final FailableRunnable... resources) { - tryWithResources(action, null, resources); - } -} diff --git a/src/main/java/org/apache/commons/lang3/JavaVersion.java b/src/main/java/org/apache/commons/lang3/JavaVersion.java deleted file mode 100755 index 62c85399..00000000 --- a/src/main/java/org/apache/commons/lang3/JavaVersion.java +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import org.apache.commons.lang3.math.NumberUtils; - -/** - *

- * An enum representing all the versions of the Java specification. This is - * intended to mirror available values from the - * java.specification.version System property. - *

- * - * @since 3.0 - */ -public enum JavaVersion { - - /** - * The Java version reported by Android. This is not an official Java version - * number. - */ - JAVA_0_9(1.5f, "0.9"), - - /** - * Java 1.1. - */ - JAVA_1_1(1.1f, "1.1"), - - /** - * Java 1.2. - */ - JAVA_1_2(1.2f, "1.2"), - - /** - * Java 1.3. - */ - JAVA_1_3(1.3f, "1.3"), - - /** - * Java 1.4. - */ - JAVA_1_4(1.4f, "1.4"), - - /** - * Java 1.5. - */ - JAVA_1_5(1.5f, "1.5"), - - /** - * Java 1.6. - */ - JAVA_1_6(1.6f, "1.6"), - - /** - * Java 1.7. - */ - JAVA_1_7(1.7f, "1.7"), - - /** - * Java 1.8. - */ - JAVA_1_8(1.8f, "1.8"), - - /** - * Java 1.9. - * - * @deprecated As of release 3.5, replaced by {@link #JAVA_9} - */ - @Deprecated - JAVA_1_9(9.0f, "9"), - - /** - * Java 9. - * - * @since 3.5 - */ - JAVA_9(9.0f, "9"), - - /** - * Java 10. - * - * @since 3.7 - */ - JAVA_10(10.0f, "10"), - - /** - * Java 11. - * - * @since 3.8 - */ - JAVA_11(11.0f, "11"), - - /** - * Java 12. - * - * @since 3.9 - */ - JAVA_12(12.0f, "12"), - - /** - * Java 13. - * - * @since 3.9 - */ - JAVA_13(13.0f, "13"), - - /** - * Java 14. - * - * @since 3.11 - */ - JAVA_14(14.0f, "14"), - - /** - * Java 15. - * - * @since 3.11 - */ - JAVA_15(15.0f, "15"), - - /** - * Java 16. - * - * @since 3.11 - */ - JAVA_16(16.0f, "16"), - - /** - * Java 17. - * - * @since 3.12.0 - */ - JAVA_17(17.0f, "17"), - - /** - * The most recent java version. Mainly introduced to avoid to break when a new - * version of Java is used. - */ - JAVA_RECENT(maxVersion(), Float.toString(maxVersion())); - - /** - * The float value. - */ - private final float value; - - /** - * The standard name. - */ - private final String name; - - /** - * Constructor. - * - * @param value the float value - * @param name the standard name, not null - */ - JavaVersion(final float value, final String name) { - this.value = value; - this.name = name; - } - - // ----------------------------------------------------------------------- - /** - *

- * Whether this version of Java is at least the version of Java passed in. - *

- * - *

- * For example:
- * {@code myVersion.atLeast(JavaVersion.JAVA_1_4)} - *

- * - * @param requiredVersion the version to check against, not null - * @return true if this version is equal to or greater than the specified - * version - */ - public boolean atLeast(final JavaVersion requiredVersion) { - return this.value >= requiredVersion.value; - } - - // ----------------------------------------------------------------------- - /** - *

- * Whether this version of Java is at most the version of Java passed in. - *

- * - *

- * For example:
- * {@code myVersion.atMost(JavaVersion.JAVA_1_4)} - *

- * - * @param requiredVersion the version to check against, not null - * @return true if this version is equal to or greater than the specified - * version - * @since 3.9 - */ - public boolean atMost(final JavaVersion requiredVersion) { - return this.value <= requiredVersion.value; - } - - /** - * Transforms the given string with a Java version number to the corresponding - * constant of this enumeration class. This method is used internally. - * - * @param nom the Java version as string - * @return the corresponding enumeration constant or null if the version - * is unknown - */ - // helper for static importing - static JavaVersion getJavaVersion(final String nom) { - return get(nom); - } - - /** - * Transforms the given string with a Java version number to the corresponding - * constant of this enumeration class. This method is used internally. - * - * @param versionStr the Java version as string - * @return the corresponding enumeration constant or null if the version - * is unknown - */ - static JavaVersion get(final String versionStr) { - if (versionStr == null) { - return null; - } - switch (versionStr) { - case "0.9": - return JAVA_0_9; - case "1.1": - return JAVA_1_1; - case "1.2": - return JAVA_1_2; - case "1.3": - return JAVA_1_3; - case "1.4": - return JAVA_1_4; - case "1.5": - return JAVA_1_5; - case "1.6": - return JAVA_1_6; - case "1.7": - return JAVA_1_7; - case "1.8": - return JAVA_1_8; - case "9": - return JAVA_9; - case "10": - return JAVA_10; - case "11": - return JAVA_11; - case "12": - return JAVA_12; - case "13": - return JAVA_13; - case "14": - return JAVA_14; - case "15": - return JAVA_15; - case "16": - return JAVA_16; - case "17": - return JAVA_17; - default: - final float v = toFloatVersion(versionStr); - if ((v - 1.) < 1.) { // then we need to check decimals > .9 - final int firstComma = Math.max(versionStr.indexOf('.'), versionStr.indexOf(',')); - final int end = Math.max(versionStr.length(), versionStr.indexOf(',', firstComma)); - if (Float.parseFloat(versionStr.substring(firstComma + 1, end)) > .9f) { - return JAVA_RECENT; - } - } else if (v > 10) { - return JAVA_RECENT; - } - return null; - } - } - - // ----------------------------------------------------------------------- - /** - *

- * The string value is overridden to return the standard name. - *

- * - *

- * For example, {@code "1.5"}. - *

- * - * @return the name, not null - */ - @Override - public String toString() { - return name; - } - - /** - * Gets the Java Version from the system or 99.0 if the - * {@code java.specification.version} system property is not set. - * - * @return the value of {@code java.specification.version} system property or - * 99.0 if it is not set. - */ - private static float maxVersion() { - final float v = toFloatVersion(System.getProperty("java.specification.version", "99.0")); - if (v > 0) { - return v; - } - return 99f; - } - - /** - * Parses a float value from a String. - * - * @param value the String to parse. - * @return the float value represented by the string or -1 if the given String - * can not be parsed. - */ - private static float toFloatVersion(final String value) { - final int defaultReturnValue = -1; - if (value.contains(".")) { - final String[] toParse = value.split("\\."); - if (toParse.length >= 2) { - return NumberUtils.toFloat(toParse[0] + '.' + toParse[1], defaultReturnValue); - } - } else { - return NumberUtils.toFloat(value, defaultReturnValue); - } - return defaultReturnValue; - } -} diff --git a/src/main/java/org/apache/commons/lang3/LocaleUtils.java b/src/main/java/org/apache/commons/lang3/LocaleUtils.java deleted file mode 100755 index cf6958f4..00000000 --- a/src/main/java/org/apache/commons/lang3/LocaleUtils.java +++ /dev/null @@ -1,396 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -/** - *

- * Operations to assist when working with a {@link Locale}. - *

- * - *

- * This class tries to handle {@code null} input gracefully. An exception will - * not be thrown for a {@code null} input. Each method documents its behavior in - * more detail. - *

- * - * @since 2.2 - */ -public class LocaleUtils { - - // class to avoid synchronization (Init on demand) - static class SyncAvoid { - /** Unmodifiable list of available locales. */ - private static final List AVAILABLE_LOCALE_LIST; - /** Unmodifiable set of available locales. */ - private static final Set AVAILABLE_LOCALE_SET; - - static { - final List list = new ArrayList<>(Arrays.asList(Locale.getAvailableLocales())); // extra safe - AVAILABLE_LOCALE_LIST = Collections.unmodifiableList(list); - AVAILABLE_LOCALE_SET = Collections.unmodifiableSet(new HashSet<>(list)); - } - } - - /** Concurrent map of language locales by country. */ - private static final Map> cLanguagesByCountry = new HashMap<>(); - - /** Concurrent map of country locales by language. */ - private static final Map> cCountriesByLanguage = new HashMap<>(); - - /** - *

- * Obtains an unmodifiable list of installed locales. - *

- * - *

- * This method is a wrapper around {@link Locale#getAvailableLocales()}. It is - * more efficient, as the JDK method must create a new array each time it is - * called. - *

- * - * @return the unmodifiable list of available locales - */ - public static List availableLocaleList() { - return SyncAvoid.AVAILABLE_LOCALE_LIST; - } - - /** - *

- * Obtains an unmodifiable set of installed locales. - *

- * - *

- * This method is a wrapper around {@link Locale#getAvailableLocales()}. It is - * more efficient, as the JDK method must create a new array each time it is - * called. - *

- * - * @return the unmodifiable set of available locales - */ - public static Set availableLocaleSet() { - return SyncAvoid.AVAILABLE_LOCALE_SET; - } - - /** - *

- * Obtains the list of countries supported for a given language. - *

- * - *

- * This method takes a language code and searches to find the countries - * available for that language. Variant locales are removed. - *

- * - * @param languageCode the 2 letter language code, null returns empty - * @return an unmodifiable List of Locale objects, not null - */ - public static List countriesByLanguage(final String languageCode) { - if (languageCode == null) { - return Collections.emptyList(); - } - List countries = cCountriesByLanguage.get(languageCode); - if (countries == null) { - countries = new ArrayList<>(); - final List locales = availableLocaleList(); - for (final Locale locale : locales) { - if (languageCode.equals(locale.getLanguage()) && !locale.getCountry().isEmpty() - && locale.getVariant().isEmpty()) { - countries.add(locale); - } - } - countries = Collections.unmodifiableList(countries); - cCountriesByLanguage.putIfAbsent(languageCode, countries); - countries = cCountriesByLanguage.get(languageCode); - } - return countries; - } - - /** - *

- * Checks if the locale specified is in the list of available locales. - *

- * - * @param locale the Locale object to check if it is available - * @return true if the locale is a known locale - */ - public static boolean isAvailableLocale(final Locale locale) { - return availableLocaleList().contains(locale); - } - - /** - * Checks whether the given String is a ISO 3166 alpha-2 country code. - * - * @param str the String to check - * @return true, is the given String is a ISO 3166 compliant country code. - */ - private static boolean isISO3166CountryCode(final String str) { - return StringUtils.isAllUpperCase(str) && str.length() == 2; - } - - /** - * Checks whether the given String is a ISO 639 compliant language code. - * - * @param str the String to check. - * @return true, if the given String is a ISO 639 compliant language code. - */ - private static boolean isISO639LanguageCode(final String str) { - return StringUtils.isAllLowerCase(str) && (str.length() == 2 || str.length() == 3); - } - - /** - * Checks whether the given String is a UN M.49 numeric area code. - * - * @param str the String to check - * @return true, is the given String is a UN M.49 numeric area code. - */ - private static boolean isNumericAreaCode(final String str) { - return StringUtils.isNumeric(str) && str.length() == 3; - } - - /** - *

- * Obtains the list of languages supported for a given country. - *

- * - *

- * This method takes a country code and searches to find the languages available - * for that country. Variant locales are removed. - *

- * - * @param countryCode the 2 letter country code, null returns empty - * @return an unmodifiable List of Locale objects, not null - */ - public static List languagesByCountry(final String countryCode) { - if (countryCode == null) { - return Collections.emptyList(); - } - List langs = cLanguagesByCountry.get(countryCode); - if (langs == null) { - langs = new ArrayList<>(); - final List locales = availableLocaleList(); - for (final Locale locale : locales) { - if (countryCode.equals(locale.getCountry()) && locale.getVariant().isEmpty()) { - langs.add(locale); - } - } - langs = Collections.unmodifiableList(langs); - cLanguagesByCountry.putIfAbsent(countryCode, langs); - langs = cLanguagesByCountry.get(countryCode); - } - return langs; - } - - /** - *

- * Obtains the list of locales to search through when performing a locale - * search. - *

- * - *
-	 * localeLookupList(Locale("fr", "CA", "xxx"))
-	 *   = [Locale("fr", "CA", "xxx"), Locale("fr", "CA"), Locale("fr")]
-	 * 
- * - * @param locale the locale to start from - * @return the unmodifiable list of Locale objects, 0 being locale, not null - */ - public static List localeLookupList(final Locale locale) { - return localeLookupList(locale, locale); - } - - /** - *

- * Obtains the list of locales to search through when performing a locale - * search. - *

- * - *
-	 * localeLookupList(Locale("fr", "CA", "xxx"), Locale("en"))
-	 *   = [Locale("fr", "CA", "xxx"), Locale("fr", "CA"), Locale("fr"), Locale("en"]
-	 * 
- * - *

- * The result list begins with the most specific locale, then the next more - * general and so on, finishing with the default locale. The list will never - * contain the same locale twice. - *

- * - * @param locale the locale to start from, null returns empty list - * @param defaultLocale the default locale to use if no other is found - * @return the unmodifiable list of Locale objects, 0 being locale, not null - */ - public static List localeLookupList(final Locale locale, final Locale defaultLocale) { - final List list = new ArrayList<>(4); - if (locale != null) { - list.add(locale); - if (!locale.getVariant().isEmpty()) { - list.add(new Locale(locale.getLanguage(), locale.getCountry())); - } - if (!locale.getCountry().isEmpty()) { - list.add(new Locale(locale.getLanguage(), StringUtils.EMPTY)); - } - if (!list.contains(defaultLocale)) { - list.add(defaultLocale); - } - } - return Collections.unmodifiableList(list); - } - - /** - * Tries to parse a locale from the given String. - * - * @param str the String to parse a locale from. - * @return a Locale instance parsed from the given String. - * @throws IllegalArgumentException if the given String can not be parsed. - */ - private static Locale parseLocale(final String str) { - if (isISO639LanguageCode(str)) { - return new Locale(str); - } - - final String[] segments = str.split("_", -1); - final String language = segments[0]; - if (segments.length == 2) { - final String country = segments[1]; - if (isISO639LanguageCode(language) && isISO3166CountryCode(country) || isNumericAreaCode(country)) { - return new Locale(language, country); - } - } else if (segments.length == 3) { - final String country = segments[1]; - final String variant = segments[2]; - if (isISO639LanguageCode(language) - && (country.isEmpty() || isISO3166CountryCode(country) || isNumericAreaCode(country)) - && !variant.isEmpty()) { - return new Locale(language, country, variant); - } - } - throw new IllegalArgumentException("Invalid locale format: " + str); - } - - /** - * Returns the given locale if non-{@code null}, otherwise - * {@link Locale#getDefault()}. - * - * @param locale a locale or {@code null}. - * @return the given locale if non-{@code null}, otherwise - * {@link Locale#getDefault()}. - * @since 3.12.0 - */ - public static Locale toLocale(final Locale locale) { - return locale != null ? locale : Locale.getDefault(); - } - - /** - *

- * Converts a String to a Locale. - *

- * - *

- * This method takes the string format of a locale and creates the locale object - * from it. - *

- * - *
-	 *   LocaleUtils.toLocale("")           = new Locale("", "")
-	 *   LocaleUtils.toLocale("en")         = new Locale("en", "")
-	 *   LocaleUtils.toLocale("en_GB")      = new Locale("en", "GB")
-	 *   LocaleUtils.toLocale("en_001")     = new Locale("en", "001")
-	 *   LocaleUtils.toLocale("en_GB_xxx")  = new Locale("en", "GB", "xxx")   (#)
-	 * 
- * - *

- * (#) The behavior of the JDK variant constructor changed between JDK1.3 and - * JDK1.4. In JDK1.3, the constructor upper cases the variant, in JDK1.4, it - * doesn't. Thus, the result from getVariant() may vary depending on your JDK. - *

- * - *

- * This method validates the input strictly. The language code must be - * lowercase. The country code must be uppercase. The separator must be an - * underscore. The length must be correct. - *

- * - * @param str the locale String to convert, null returns null - * @return a Locale, null if null input - * @throws IllegalArgumentException if the string is an invalid format - * @see Locale#forLanguageTag(String) - */ - public static Locale toLocale(final String str) { - if (str == null) { - return null; - } - if (str.isEmpty()) { // LANG-941 - JDK 8 introduced an empty locale where all fields are blank - return new Locale(StringUtils.EMPTY, StringUtils.EMPTY); - } - if (str.contains("#")) { // LANG-879 - Cannot handle Java 7 script & extensions - throw new IllegalArgumentException("Invalid locale format: " + str); - } - final int len = str.length(); - if (len < 2) { - throw new IllegalArgumentException("Invalid locale format: " + str); - } - final char ch0 = str.charAt(0); - if (ch0 == '_') { - if (len < 3) { - throw new IllegalArgumentException("Invalid locale format: " + str); - } - final char ch1 = str.charAt(1); - final char ch2 = str.charAt(2); - if (!Character.isUpperCase(ch1) || !Character.isUpperCase(ch2)) { - throw new IllegalArgumentException("Invalid locale format: " + str); - } - if (len == 3) { - return new Locale(StringUtils.EMPTY, str.substring(1, 3)); - } - if (len < 5) { - throw new IllegalArgumentException("Invalid locale format: " + str); - } - if (str.charAt(3) != '_') { - throw new IllegalArgumentException("Invalid locale format: " + str); - } - return new Locale(StringUtils.EMPTY, str.substring(1, 3), str.substring(4)); - } - - return parseLocale(str); - } - - /** - *

- * {@code LocaleUtils} instances should NOT be constructed in standard - * programming. Instead, the class should be used as - * {@code LocaleUtils.toLocale("en_GB");}. - *

- * - *

- * This constructor is public to permit tools that require a JavaBean instance - * to operate. - *

- */ - public LocaleUtils() { - } - -} diff --git a/src/main/java/org/apache/commons/lang3/NotImplementedException.java b/src/main/java/org/apache/commons/lang3/NotImplementedException.java deleted file mode 100755 index 06ef9ef5..00000000 --- a/src/main/java/org/apache/commons/lang3/NotImplementedException.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -/** - *

- * Thrown to indicate that a block of code has not been implemented. This - * exception supplements {@code UnsupportedOperationException} by providing a - * more semantically rich description of the problem. - *

- * - *

- * {@code NotImplementedException} represents the case where the author has yet - * to implement the logic at this point in the program. This can act as an - * exception based TODO tag. - *

- * - *
- * public void foo() {
- * 	try {
- * 		// do something that throws an Exception
- * 	} catch (Exception ex) {
- * 		// don't know what to do here yet
- * 		throw new NotImplementedException("TODO", ex);
- * 	}
- * }
- * 
- * - * This class was originally added in Lang 2.0, but removed in 3.0. - * - * @since 3.2 - */ -public class NotImplementedException extends UnsupportedOperationException { - - private static final long serialVersionUID = 20131021L; - - private final String code; - - /** - * Constructs a NotImplementedException. - * - * @since 3.10 - */ - public NotImplementedException() { - this.code = null; - } - - /** - * Constructs a NotImplementedException. - * - * @param message description of the exception - * @since 3.2 - */ - public NotImplementedException(final String message) { - this(message, (String) null); - } - - /** - * Constructs a NotImplementedException. - * - * @param cause cause of the exception - * @since 3.2 - */ - public NotImplementedException(final Throwable cause) { - this(cause, null); - } - - /** - * Constructs a NotImplementedException. - * - * @param message description of the exception - * @param cause cause of the exception - * @since 3.2 - */ - public NotImplementedException(final String message, final Throwable cause) { - this(message, cause, null); - } - - /** - * Constructs a NotImplementedException. - * - * @param message description of the exception - * @param code code indicating a resource for more information regarding the - * lack of implementation - * @since 3.2 - */ - public NotImplementedException(final String message, final String code) { - super(message); - this.code = code; - } - - /** - * Constructs a NotImplementedException. - * - * @param cause cause of the exception - * @param code code indicating a resource for more information regarding the - * lack of implementation - * @since 3.2 - */ - public NotImplementedException(final Throwable cause, final String code) { - super(cause); - this.code = code; - } - - /** - * Constructs a NotImplementedException. - * - * @param message description of the exception - * @param cause cause of the exception - * @param code code indicating a resource for more information regarding the - * lack of implementation - * @since 3.2 - */ - public NotImplementedException(final String message, final Throwable cause, final String code) { - super(message, cause); - this.code = code; - } - - /** - * Obtain the not implemented code. This is an unformatted piece of text - * intended to point to further information regarding the lack of - * implementation. It might, for example, be an issue tracker ID or a URL. - * - * @return a code indicating a resource for more information regarding the lack - * of implementation - */ - public String getCode() { - return this.code; - } -} diff --git a/src/main/java/org/apache/commons/lang3/ObjectUtils.java b/src/main/java/org/apache/commons/lang3/ObjectUtils.java deleted file mode 100755 index 48d757ca..00000000 --- a/src/main/java/org/apache/commons/lang3/ObjectUtils.java +++ /dev/null @@ -1,1377 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import java.io.IOException; -import java.io.Serializable; -import java.lang.reflect.Array; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.time.Duration; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Map; -import java.util.Objects; -import java.util.TreeSet; -import java.util.function.Supplier; - -import org.apache.commons.lang3.exception.CloneFailedException; -import org.apache.commons.lang3.text.StrBuilder; -import org.apache.commons.lang3.time.DurationUtils; - -/** - *

- * Operations on {@code Object}. - *

- * - *

- * This class tries to handle {@code null} input gracefully. An exception will - * generally not be thrown for a {@code null} input. Each method documents its - * behavior in more detail. - *

- * - *

- * #ThreadSafe# - *

- * - * @since 1.0 - */ -//@Immutable -@SuppressWarnings("deprecation") // deprecated class StrBuilder is imported -// because it is part of the signature of deprecated methods -public class ObjectUtils { - - // Null - // ----------------------------------------------------------------------- - /** - *

- * Class used as a null placeholder where {@code null} has another meaning. - *

- * - *

- * For example, in a {@code HashMap} the - * {@link java.util.HashMap#get(java.lang.Object)} method returns {@code null} - * if the {@code Map} contains {@code null} or if there is no matching key. The - * {@code Null} placeholder can be used to distinguish between these two cases. - *

- * - *

- * Another example is {@code Hashtable}, where {@code null} cannot be stored. - *

- */ - public static class Null implements Serializable { - /** - * Required for serialization support. Declare serialization compatibility with - * Commons Lang 1.0 - * - * @see java.io.Serializable - */ - private static final long serialVersionUID = 7092611880189329093L; - - /** - * Restricted constructor - singleton. - */ - Null() { - } - - /** - *

- * Ensure singleton. - *

- * - * @return the singleton value - */ - private Object readResolve() { - return NULL; - } - } - - private static final char AT_SIGN = '@'; - - /** - *

- * Singleton used as a {@code null} placeholder where {@code null} has another - * meaning. - *

- * - *

- * For example, in a {@code HashMap} the - * {@link java.util.HashMap#get(java.lang.Object)} method returns {@code null} - * if the {@code Map} contains {@code null} or if there is no matching key. The - * {@code Null} placeholder can be used to distinguish between these two cases. - *

- * - *

- * Another example is {@code Hashtable}, where {@code null} cannot be stored. - *

- * - *

- * This instance is Serializable. - *

- */ - public static final Null NULL = new Null(); - - /** - * Checks if all values in the array are not {@code nulls}. - * - *

- * If any value is {@code null} or the array is {@code null} then {@code false} - * is returned. If all elements in array are not {@code null} or the array is - * empty (contains no elements) {@code true} is returned. - *

- * - *
-	 * ObjectUtils.allNotNull(*)             = true
-	 * ObjectUtils.allNotNull(*, *)          = true
-	 * ObjectUtils.allNotNull(null)          = false
-	 * ObjectUtils.allNotNull(null, null)    = false
-	 * ObjectUtils.allNotNull(null, *)       = false
-	 * ObjectUtils.allNotNull(*, null)       = false
-	 * ObjectUtils.allNotNull(*, *, null, *) = false
-	 * 
- * - * @param values the values to test, may be {@code null} or empty - * @return {@code false} if there is at least one {@code null} value in the - * array or the array is {@code null}, {@code true} if all values in the - * array are not {@code null}s or array contains no elements. - * @since 3.5 - */ - public static boolean allNotNull(final Object... values) { - if (values == null) { - return false; - } - - for (final Object val : values) { - if (val == null) { - return false; - } - } - - return true; - } - - /** - * Checks if all values in the given array are {@code null}. - * - *

- * If all the values are {@code null} or the array is {@code null} or empty, - * then {@code true} is returned, otherwise {@code false} is returned. - *

- * - *
-	 * ObjectUtils.allNull(*)                = false
-	 * ObjectUtils.allNull(*, null)          = false
-	 * ObjectUtils.allNull(null, *)          = false
-	 * ObjectUtils.allNull(null, null, *, *) = false
-	 * ObjectUtils.allNull(null)             = true
-	 * ObjectUtils.allNull(null, null)       = true
-	 * 
- * - * @param values the values to test, may be {@code null} or empty - * @return {@code true} if all values in the array are {@code null}s, - * {@code false} if there is at least one non-null value in the array. - * @since 3.11 - */ - public static boolean allNull(final Object... values) { - return !anyNotNull(values); - } - - /** - * Checks if any value in the given array is not {@code null}. - * - *

- * If all the values are {@code null} or the array is {@code null} or empty then - * {@code false} is returned. Otherwise {@code true} is returned. - *

- * - *
-	 * ObjectUtils.anyNotNull(*)                = true
-	 * ObjectUtils.anyNotNull(*, null)          = true
-	 * ObjectUtils.anyNotNull(null, *)          = true
-	 * ObjectUtils.anyNotNull(null, null, *, *) = true
-	 * ObjectUtils.anyNotNull(null)             = false
-	 * ObjectUtils.anyNotNull(null, null)       = false
-	 * 
- * - * @param values the values to test, may be {@code null} or empty - * @return {@code true} if there is at least one non-null value in the array, - * {@code false} if all values in the array are {@code null}s. If the - * array is {@code null} or empty {@code false} is also returned. - * @since 3.5 - */ - public static boolean anyNotNull(final Object... values) { - return firstNonNull(values) != null; - } - - /** - * Checks if any value in the given array is {@code null}. - * - *

- * If any of the values are {@code null} or the array is {@code null}, then - * {@code true} is returned, otherwise {@code false} is returned. - *

- * - *
-	 * ObjectUtils.anyNull(*)             = false
-	 * ObjectUtils.anyNull(*, *)          = false
-	 * ObjectUtils.anyNull(null)          = true
-	 * ObjectUtils.anyNull(null, null)    = true
-	 * ObjectUtils.anyNull(null, *)       = true
-	 * ObjectUtils.anyNull(*, null)       = true
-	 * ObjectUtils.anyNull(*, *, null, *) = true
-	 * 
- * - * @param values the values to test, may be {@code null} or empty - * @return {@code true} if there is at least one {@code null} value in the - * array, {@code false} if all the values are non-null. If the array is - * {@code null} or empty, {@code true} is also returned. - * @since 3.11 - */ - public static boolean anyNull(final Object... values) { - return !allNotNull(values); - } - - // cloning - // ----------------------------------------------------------------------- - /** - *

- * Clone an object. - *

- * - * @param the type of the object - * @param obj the object to clone, null returns null - * @return the clone if the object implements {@link Cloneable} otherwise - * {@code null} - * @throws CloneFailedException if the object is cloneable and the clone - * operation fails - * @since 3.0 - */ - public static T clone(final T obj) { - if (obj instanceof Cloneable) { - final Object result; - if (obj.getClass().isArray()) { - final Class componentType = obj.getClass().getComponentType(); - if (componentType.isPrimitive()) { - int length = Array.getLength(obj); - result = Array.newInstance(componentType, length); - while (length-- > 0) { - Array.set(result, length, Array.get(obj, length)); - } - } else { - result = ((Object[]) obj).clone(); - } - } else { - try { - final Method clone = obj.getClass().getMethod("clone"); - result = clone.invoke(obj); - } catch (final NoSuchMethodException e) { - throw new CloneFailedException( - "Cloneable type " + obj.getClass().getName() + " has no clone method", e); - } catch (final IllegalAccessException e) { - throw new CloneFailedException("Cannot clone Cloneable type " + obj.getClass().getName(), e); - } catch (final InvocationTargetException e) { - throw new CloneFailedException("Exception cloning Cloneable type " + obj.getClass().getName(), - e.getCause()); - } - } - @SuppressWarnings("unchecked") // OK because input is of type T - final T checked = (T) result; - return checked; - } - - return null; - } - - /** - *

- * Clone an object if possible. - *

- * - *

- * This method is similar to {@link #clone(Object)}, but will return the - * provided instance as the return value instead of {@code null} if the instance - * is not cloneable. This is more convenient if the caller uses different - * implementations (e.g. of a service) and some of the implementations do not - * allow concurrent processing or have state. In such cases the implementation - * can simply provide a proper clone implementation and the caller's code does - * not have to change. - *

- * - * @param the type of the object - * @param obj the object to clone, null returns null - * @return the clone if the object implements {@link Cloneable} otherwise the - * object itself - * @throws CloneFailedException if the object is cloneable and the clone - * operation fails - * @since 3.0 - */ - public static T cloneIfPossible(final T obj) { - final T clone = clone(obj); - return clone == null ? obj : clone; - } - - /** - *

- * Null safe comparison of Comparables. {@code null} is assumed to be less than - * a non-{@code null} value. - *

- * - * @param type of the values processed by this method - * @param c1 the first comparable, may be null - * @param c2 the second comparable, may be null - * @return a negative value if c1 < c2, zero if c1 = c2 and a positive value - * if c1 > c2 - */ - public static > int compare(final T c1, final T c2) { - return compare(c1, c2, false); - } - - /** - *

- * Null safe comparison of Comparables. - *

- * - * @param type of the values processed by this method - * @param c1 the first comparable, may be null - * @param c2 the second comparable, may be null - * @param nullGreater if true {@code null} is considered greater than a - * non-{@code null} value or if false {@code null} is - * considered less than a Non-{@code null} value - * @return a negative value if c1 < c2, zero if c1 = c2 and a positive value - * if c1 > c2 - * @see java.util.Comparator#compare(Object, Object) - */ - public static > int compare(final T c1, final T c2, final boolean nullGreater) { - if (c1 == c2) { - return 0; - } else if (c1 == null) { - return nullGreater ? 1 : -1; - } else if (c2 == null) { - return nullGreater ? -1 : 1; - } - return c1.compareTo(c2); - } - - /** - * This method returns the provided value unchanged. This can prevent javac from - * inlining a constant field, e.g., - * - *
-	 * public final static boolean MAGIC_FLAG = ObjectUtils.CONST(true);
-	 * 
- * - * This way any jars that refer to this field do not have to recompile - * themselves if the field's value changes at some future date. - * - * @param v the boolean value to return - * @return the boolean v, unchanged - * @since 3.2 - */ - public static boolean CONST(final boolean v) { - return v; - } - - /** - * This method returns the provided value unchanged. This can prevent javac from - * inlining a constant field, e.g., - * - *
-	 * public final static byte MAGIC_BYTE = ObjectUtils.CONST((byte) 127);
-	 * 
- * - * This way any jars that refer to this field do not have to recompile - * themselves if the field's value changes at some future date. - * - * @param v the byte value to return - * @return the byte v, unchanged - * @since 3.2 - */ - public static byte CONST(final byte v) { - return v; - } - - /** - * This method returns the provided value unchanged. This can prevent javac from - * inlining a constant field, e.g., - * - *
-	 * public final static char MAGIC_CHAR = ObjectUtils.CONST('a');
-	 * 
- * - * This way any jars that refer to this field do not have to recompile - * themselves if the field's value changes at some future date. - * - * @param v the char value to return - * @return the char v, unchanged - * @since 3.2 - */ - public static char CONST(final char v) { - return v; - } - - /** - * This method returns the provided value unchanged. This can prevent javac from - * inlining a constant field, e.g., - * - *
-	 * public final static double MAGIC_DOUBLE = ObjectUtils.CONST(1.0);
-	 * 
- * - * This way any jars that refer to this field do not have to recompile - * themselves if the field's value changes at some future date. - * - * @param v the double value to return - * @return the double v, unchanged - * @since 3.2 - */ - public static double CONST(final double v) { - return v; - } - - /** - * This method returns the provided value unchanged. This can prevent javac from - * inlining a constant field, e.g., - * - *
-	 * public final static float MAGIC_FLOAT = ObjectUtils.CONST(1.0f);
-	 * 
- * - * This way any jars that refer to this field do not have to recompile - * themselves if the field's value changes at some future date. - * - * @param v the float value to return - * @return the float v, unchanged - * @since 3.2 - */ - public static float CONST(final float v) { - return v; - } - - /** - * This method returns the provided value unchanged. This can prevent javac from - * inlining a constant field, e.g., - * - *
-	 * public final static int MAGIC_INT = ObjectUtils.CONST(123);
-	 * 
- * - * This way any jars that refer to this field do not have to recompile - * themselves if the field's value changes at some future date. - * - * @param v the int value to return - * @return the int v, unchanged - * @since 3.2 - */ - public static int CONST(final int v) { - return v; - } - - /** - * This method returns the provided value unchanged. This can prevent javac from - * inlining a constant field, e.g., - * - *
-	 * public final static long MAGIC_LONG = ObjectUtils.CONST(123L);
-	 * 
- * - * This way any jars that refer to this field do not have to recompile - * themselves if the field's value changes at some future date. - * - * @param v the long value to return - * @return the long v, unchanged - * @since 3.2 - */ - public static long CONST(final long v) { - return v; - } - - /** - * This method returns the provided value unchanged. This can prevent javac from - * inlining a constant field, e.g., - * - *
-	 * public final static short MAGIC_SHORT = ObjectUtils.CONST((short) 123);
-	 * 
- * - * This way any jars that refer to this field do not have to recompile - * themselves if the field's value changes at some future date. - * - * @param v the short value to return - * @return the short v, unchanged - * @since 3.2 - */ - public static short CONST(final short v) { - return v; - } - - /** - * This method returns the provided value unchanged. This can prevent javac from - * inlining a constant field, e.g., - * - *
-	 * public final static String MAGIC_STRING = ObjectUtils.CONST("abc");
-	 * 
- * - * This way any jars that refer to this field do not have to recompile - * themselves if the field's value changes at some future date. - * - * @param the Object type - * @param v the genericized Object value to return (typically a String). - * @return the genericized Object v, unchanged (typically a String). - * @since 3.2 - */ - public static T CONST(final T v) { - return v; - } - - /** - * This method returns the provided value unchanged. This can prevent javac from - * inlining a constant field, e.g., - * - *
-	 * public final static byte MAGIC_BYTE = ObjectUtils.CONST_BYTE(127);
-	 * 
- * - * This way any jars that refer to this field do not have to recompile - * themselves if the field's value changes at some future date. - * - * @param v the byte literal (as an int) value to return - * @throws IllegalArgumentException if the value passed to v is larger than a - * byte, that is, smaller than -128 or larger - * than 127. - * @return the byte v, unchanged - * @since 3.2 - */ - public static byte CONST_BYTE(final int v) { - if (v < Byte.MIN_VALUE || v > Byte.MAX_VALUE) { - throw new IllegalArgumentException( - "Supplied value must be a valid byte literal between -128 and 127: [" + v + "]"); - } - return (byte) v; - } - - /** - * This method returns the provided value unchanged. This can prevent javac from - * inlining a constant field, e.g., - * - *
-	 * public final static short MAGIC_SHORT = ObjectUtils.CONST_SHORT(127);
-	 * 
- * - * This way any jars that refer to this field do not have to recompile - * themselves if the field's value changes at some future date. - * - * @param v the short literal (as an int) value to return - * @throws IllegalArgumentException if the value passed to v is larger than a - * short, that is, smaller than -32768 or - * larger than 32767. - * @return the byte v, unchanged - * @since 3.2 - */ - public static short CONST_SHORT(final int v) { - if (v < Short.MIN_VALUE || v > Short.MAX_VALUE) { - throw new IllegalArgumentException( - "Supplied value must be a valid byte literal between -32768 and 32767: [" + v + "]"); - } - return (short) v; - } - - /** - *

- * Returns a default value if the object passed is {@code null}. - *

- * - *
-	 * ObjectUtils.defaultIfNull(null, null)      = null
-	 * ObjectUtils.defaultIfNull(null, "")        = ""
-	 * ObjectUtils.defaultIfNull(null, "zz")      = "zz"
-	 * ObjectUtils.defaultIfNull("abc", *)        = "abc"
-	 * ObjectUtils.defaultIfNull(Boolean.TRUE, *) = Boolean.TRUE
-	 * 
- * - * @param the type of the object - * @param object the {@code Object} to test, may be {@code null} - * @param defaultValue the default value to return, may be {@code null} - * @return {@code object} if it is not {@code null}, defaultValue otherwise TODO - * Rename to getIfNull in 4.0 - */ - public static T defaultIfNull(final T object, final T defaultValue) { - return object != null ? object : defaultValue; - } - - // Null-safe equals/hashCode - // ----------------------------------------------------------------------- - /** - *

- * Compares two objects for equality, where either one or both objects may be - * {@code null}. - *

- * - *
-	 * ObjectUtils.equals(null, null)                  = true
-	 * ObjectUtils.equals(null, "")                    = false
-	 * ObjectUtils.equals("", null)                    = false
-	 * ObjectUtils.equals("", "")                      = true
-	 * ObjectUtils.equals(Boolean.TRUE, null)          = false
-	 * ObjectUtils.equals(Boolean.TRUE, "true")        = false
-	 * ObjectUtils.equals(Boolean.TRUE, Boolean.TRUE)  = true
-	 * ObjectUtils.equals(Boolean.TRUE, Boolean.FALSE) = false
-	 * 
- * - * @param object1 the first object, may be {@code null} - * @param object2 the second object, may be {@code null} - * @return {@code true} if the values of both objects are the same - * @deprecated this method has been replaced by - * {@code java.util.Objects.equals(Object, Object)} in Java 7 and - * will be removed from future releases. - */ - @Deprecated - public static boolean equals(final Object object1, final Object object2) { - if (object1 == object2) { - return true; - } - if (object1 == null || object2 == null) { - return false; - } - return object1.equals(object2); - } - - /** - *

- * Returns the first value in the array which is not {@code null}. If all the - * values are {@code null} or the array is {@code null} or empty then - * {@code null} is returned. - *

- * - *
-	 * ObjectUtils.firstNonNull(null, null)      = null
-	 * ObjectUtils.firstNonNull(null, "")        = ""
-	 * ObjectUtils.firstNonNull(null, null, "")  = ""
-	 * ObjectUtils.firstNonNull(null, "zz")      = "zz"
-	 * ObjectUtils.firstNonNull("abc", *)        = "abc"
-	 * ObjectUtils.firstNonNull(null, "xyz", *)  = "xyz"
-	 * ObjectUtils.firstNonNull(Boolean.TRUE, *) = Boolean.TRUE
-	 * ObjectUtils.firstNonNull()                = null
-	 * 
- * - * @param the component type of the array - * @param values the values to test, may be {@code null} or empty - * @return the first value from {@code values} which is not {@code null}, or - * {@code null} if there are no non-null values - * @since 3.0 - */ - @SafeVarargs - public static T firstNonNull(final T... values) { - if (values != null) { - for (final T val : values) { - if (val != null) { - return val; - } - } - } - return null; - } - - /** - *

- * Executes the given suppliers in order and returns the first return value - * where a value other than {@code null} is returned. Once a non-{@code null} - * value is obtained, all following suppliers are not executed anymore. If all - * the return values are {@code null} or no suppliers are provided then - * {@code null} is returned. - *

- * - *
-	 * ObjectUtils.firstNonNullLazy(null, () -> null) = null
-	 * ObjectUtils.firstNonNullLazy(() -> null, () -> "") = ""
-	 * ObjectUtils.firstNonNullLazy(() -> "", () -> throw new IllegalStateException()) = ""
-	 * ObjectUtils.firstNonNullLazy(() -> null, () -> "zz) = "zz"
-	 * ObjectUtils.firstNonNullLazy() = null
-	 * 
- * - * @param the type of the return values - * @param suppliers the suppliers returning the values to test. {@code null} - * values are ignored. Suppliers may return {@code null} or a - * value of type @{code T} - * @return the first return value from {@code suppliers} which is not - * {@code null}, or {@code null} if there are no non-null values - * @since 3.10 - */ - @SafeVarargs - public static T getFirstNonNull(final Supplier... suppliers) { - if (suppliers != null) { - for (final Supplier supplier : suppliers) { - if (supplier != null) { - final T value = supplier.get(); - if (value != null) { - return value; - } - } - } - } - return null; - } - - /** - *

- * Returns the given {@code object} is it is non-null, otherwise returns the - * Supplier's {@link Supplier#get()} value. - *

- * - *

- * The caller responsible for thread-safety and exception handling of default - * value supplier. - *

- * - *
-	 * ObjectUtils.getIfNull(null, () -> null)     = null
-	 * ObjectUtils.getIfNull(null, null)              = null
-	 * ObjectUtils.getIfNull(null, () -> "")       = ""
-	 * ObjectUtils.getIfNull(null, () -> "zz")     = "zz"
-	 * ObjectUtils.getIfNull("abc", *)                = "abc"
-	 * ObjectUtils.getIfNull(Boolean.TRUE, *)         = Boolean.TRUE
-	 * 
- * - * @param the type of the object - * @param object the {@code Object} to test, may be {@code null} - * @param defaultSupplier the default value to return, may be {@code null} - * @return {@code object} if it is not {@code null}, - * {@code defaultValueSupplier.get()} otherwise - * @since 3.10 - */ - public static T getIfNull(final T object, final Supplier defaultSupplier) { - return object != null ? object : defaultSupplier == null ? null : defaultSupplier.get(); - } - - /** - *

- * Gets the hash code of an object returning zero when the object is - * {@code null}. - *

- * - *
-	 * ObjectUtils.hashCode(null)   = 0
-	 * ObjectUtils.hashCode(obj)    = obj.hashCode()
-	 * 
- * - * @param obj the object to obtain the hash code of, may be {@code null} - * @return the hash code of the object, or zero if null - * @since 2.1 - * @deprecated this method has been replaced by - * {@code java.util.Objects.hashCode(Object)} in Java 7 and will be - * removed in future releases - */ - @Deprecated - public static int hashCode(final Object obj) { - // hashCode(Object) retained for performance, as hash code is often critical - return obj == null ? 0 : obj.hashCode(); - } - - /** - *

- * Gets the hash code for multiple objects. - *

- * - *

- * This allows a hash code to be rapidly calculated for a number of objects. The - * hash code for a single object is the not same as - * {@link #hashCode(Object)}. The hash code for multiple objects is the same as - * that calculated by an {@code ArrayList} containing the specified objects. - *

- * - *
-	 * ObjectUtils.hashCodeMulti()                 = 1
-	 * ObjectUtils.hashCodeMulti((Object[]) null)  = 1
-	 * ObjectUtils.hashCodeMulti(a)                = 31 + a.hashCode()
-	 * ObjectUtils.hashCodeMulti(a,b)              = (31 + a.hashCode()) * 31 + b.hashCode()
-	 * ObjectUtils.hashCodeMulti(a,b,c)            = ((31 + a.hashCode()) * 31 + b.hashCode()) * 31 + c.hashCode()
-	 * 
- * - * @param objects the objects to obtain the hash code of, may be {@code null} - * @return the hash code of the objects, or zero if null - * @since 3.0 - * @deprecated this method has been replaced by - * {@code java.util.Objects.hash(Object...)} in Java 7 and will be - * removed in future releases. - */ - @Deprecated - public static int hashCodeMulti(final Object... objects) { - int hash = 1; - if (objects != null) { - for (final Object object : objects) { - final int tmpHash = hashCode(object); - hash = hash * 31 + tmpHash; - } - } - return hash; - } - - /** - *

- * Appends the toString that would be produced by {@code Object} if a class did - * not override toString itself. {@code null} will throw a NullPointerException - * for either of the two parameters. - *

- * - *
-	 * ObjectUtils.identityToString(appendable, "")            = appendable.append("java.lang.String@1e23"
-	 * ObjectUtils.identityToString(appendable, Boolean.TRUE)  = appendable.append("java.lang.Boolean@7fa"
-	 * ObjectUtils.identityToString(appendable, Boolean.TRUE)  = appendable.append("java.lang.Boolean@7fa")
-	 * 
- * - * @param appendable the appendable to append to - * @param object the object to create a toString for - * @throws IOException if an I/O error occurs. - * @since 3.2 - */ - public static void identityToString(final Appendable appendable, final Object object) throws IOException { - Validate.notNull(object, "object"); - appendable.append(object.getClass().getName()).append(AT_SIGN) - .append(Integer.toHexString(System.identityHashCode(object))); - } - - // Identity ToString - // ----------------------------------------------------------------------- - /** - *

- * Gets the toString that would be produced by {@code Object} if a class did not - * override toString itself. {@code null} will return {@code null}. - *

- * - *
-	 * ObjectUtils.identityToString(null)         = null
-	 * ObjectUtils.identityToString("")           = "java.lang.String@1e23"
-	 * ObjectUtils.identityToString(Boolean.TRUE) = "java.lang.Boolean@7fa"
-	 * 
- * - * @param object the object to create a toString for, may be {@code null} - * @return the default toString text, or {@code null} if {@code null} passed in - */ - public static String identityToString(final Object object) { - if (object == null) { - return null; - } - final String name = object.getClass().getName(); - final String hexString = Integer.toHexString(System.identityHashCode(object)); - final StringBuilder builder = new StringBuilder(name.length() + 1 + hexString.length()); - // @formatter:off - builder.append(name) - .append(AT_SIGN) - .append(hexString); - // @formatter:on - return builder.toString(); - } - - /** - *

- * Appends the toString that would be produced by {@code Object} if a class did - * not override toString itself. {@code null} will throw a NullPointerException - * for either of the two parameters. - *

- * - *
-	 * ObjectUtils.identityToString(builder, "")            = builder.append("java.lang.String@1e23"
-	 * ObjectUtils.identityToString(builder, Boolean.TRUE)  = builder.append("java.lang.Boolean@7fa"
-	 * ObjectUtils.identityToString(builder, Boolean.TRUE)  = builder.append("java.lang.Boolean@7fa")
-	 * 
- * - * @param builder the builder to append to - * @param object the object to create a toString for - * @since 3.2 - * @deprecated as of 3.6, because StrBuilder was moved to commons-text, use one - * of the other {@code identityToString} methods instead - */ - @Deprecated - public static void identityToString(final StrBuilder builder, final Object object) { - Validate.notNull(object, "object"); - final String name = object.getClass().getName(); - final String hexString = Integer.toHexString(System.identityHashCode(object)); - builder.ensureCapacity(builder.length() + name.length() + 1 + hexString.length()); - builder.append(name).append(AT_SIGN).append(hexString); - } - - /** - *

- * Appends the toString that would be produced by {@code Object} if a class did - * not override toString itself. {@code null} will throw a NullPointerException - * for either of the two parameters. - *

- * - *
-	 * ObjectUtils.identityToString(buf, "")            = buf.append("java.lang.String@1e23"
-	 * ObjectUtils.identityToString(buf, Boolean.TRUE)  = buf.append("java.lang.Boolean@7fa"
-	 * ObjectUtils.identityToString(buf, Boolean.TRUE)  = buf.append("java.lang.Boolean@7fa")
-	 * 
- * - * @param buffer the buffer to append to - * @param object the object to create a toString for - * @since 2.4 - */ - public static void identityToString(final StringBuffer buffer, final Object object) { - Validate.notNull(object, "object"); - final String name = object.getClass().getName(); - final String hexString = Integer.toHexString(System.identityHashCode(object)); - buffer.ensureCapacity(buffer.length() + name.length() + 1 + hexString.length()); - buffer.append(name).append(AT_SIGN).append(hexString); - } - - /** - *

- * Appends the toString that would be produced by {@code Object} if a class did - * not override toString itself. {@code null} will throw a NullPointerException - * for either of the two parameters. - *

- * - *
-	 * ObjectUtils.identityToString(builder, "")            = builder.append("java.lang.String@1e23"
-	 * ObjectUtils.identityToString(builder, Boolean.TRUE)  = builder.append("java.lang.Boolean@7fa"
-	 * ObjectUtils.identityToString(builder, Boolean.TRUE)  = builder.append("java.lang.Boolean@7fa")
-	 * 
- * - * @param builder the builder to append to - * @param object the object to create a toString for - * @since 3.2 - */ - public static void identityToString(final StringBuilder builder, final Object object) { - Validate.notNull(object, "object"); - final String name = object.getClass().getName(); - final String hexString = Integer.toHexString(System.identityHashCode(object)); - builder.ensureCapacity(builder.length() + name.length() + 1 + hexString.length()); - builder.append(name).append(AT_SIGN).append(hexString); - } - - // Constants (LANG-816): - /* - * These methods ensure constants are not inlined by javac. For example, - * typically a developer might declare a constant like so: - * - * public final static int MAGIC_NUMBER = 5; - * - * Should a different jar file refer to this, and the MAGIC_NUMBER is changed a - * later date (e.g., MAGIC_NUMBER = 6), the different jar file will need to - * recompile itself. This is because javac typically inlines the primitive or - * String constant directly into the bytecode, and removes the reference to the - * MAGIC_NUMBER field. - * - * To help the other jar (so that it does not need to recompile when constants - * are changed) the original developer can declare their constant using one of - * the CONST() utility methods, instead: - * - * public final static int MAGIC_NUMBER = CONST(5); - */ - - // Empty checks - // ----------------------------------------------------------------------- - /** - *

- * Checks if an Object is empty or null. - *

- * - * The following types are supported: - *
    - *
  • {@link CharSequence}: Considered empty if its length is zero.
  • - *
  • {@code Array}: Considered empty if its length is zero.
  • - *
  • {@link Collection}: Considered empty if it has zero elements.
  • - *
  • {@link Map}: Considered empty if it has zero key-value mappings.
  • - *
- * - *
-	 * ObjectUtils.isEmpty(null)             = true
-	 * ObjectUtils.isEmpty("")               = true
-	 * ObjectUtils.isEmpty("ab")             = false
-	 * ObjectUtils.isEmpty(new int[]{})      = true
-	 * ObjectUtils.isEmpty(new int[]{1,2,3}) = false
-	 * ObjectUtils.isEmpty(1234)             = false
-	 * 
- * - * @param object the {@code Object} to test, may be {@code null} - * @return {@code true} if the object has a supported type and is empty or null, - * {@code false} otherwise - * @since 3.9 - */ - public static boolean isEmpty(final Object object) { - if (object == null) { - return true; - } - if (object instanceof CharSequence) { - return ((CharSequence) object).length() == 0; - } - if (object.getClass().isArray()) { - return Array.getLength(object) == 0; - } - if (object instanceof Collection) { - return ((Collection) object).isEmpty(); - } - if (object instanceof Map) { - return ((Map) object).isEmpty(); - } - return false; - } - - /** - *

- * Checks if an Object is not empty and not null. - *

- * - * The following types are supported: - *
    - *
  • {@link CharSequence}: Considered empty if its length is zero.
  • - *
  • {@code Array}: Considered empty if its length is zero.
  • - *
  • {@link Collection}: Considered empty if it has zero elements.
  • - *
  • {@link Map}: Considered empty if it has zero key-value mappings.
  • - *
- * - *
-	 * ObjectUtils.isNotEmpty(null)             = false
-	 * ObjectUtils.isNotEmpty("")               = false
-	 * ObjectUtils.isNotEmpty("ab")             = true
-	 * ObjectUtils.isNotEmpty(new int[]{})      = false
-	 * ObjectUtils.isNotEmpty(new int[]{1,2,3}) = true
-	 * ObjectUtils.isNotEmpty(1234)             = true
-	 * 
- * - * @param object the {@code Object} to test, may be {@code null} - * @return {@code true} if the object has an unsupported type or is not empty - * and not null, {@code false} otherwise - * @since 3.9 - */ - public static boolean isNotEmpty(final Object object) { - return !isEmpty(object); - } - - /** - *

- * Null safe comparison of Comparables. - *

- * - * @param type of the values processed by this method - * @param values the set of comparable values, may be null - * @return - *
    - *
  • If any objects are non-null and unequal, the greater object. - *
  • If all objects are non-null and equal, the first. - *
  • If any of the comparables are null, the greater of the non-null - * objects. - *
  • If all the comparables are null, null is returned. - *
- */ - @SafeVarargs - public static > T max(final T... values) { - T result = null; - if (values != null) { - for (final T value : values) { - if (compare(value, result, false) > 0) { - result = value; - } - } - } - return result; - } - - /** - * Find the "best guess" middle value among comparables. If there is an even - * number of total values, the lower of the two middle values will be returned. - * - * @param type of values processed by this method - * @param comparator to use for comparisons - * @param items to compare - * @return T at middle position - * @throws NullPointerException if items or comparator is {@code null} - * @throws IllegalArgumentException if items is empty or contains {@code null} - * values - * @since 3.0.1 - */ - @SafeVarargs - public static T median(final Comparator comparator, final T... items) { - Validate.notEmpty(items, "null/empty items"); - Validate.noNullElements(items); - Validate.notNull(comparator, "comparator"); - final TreeSet sort = new TreeSet<>(comparator); - Collections.addAll(sort, items); - @SuppressWarnings("unchecked") // we know all items added were T instances - final T result = (T) sort.toArray()[(sort.size() - 1) / 2]; - return result; - } - - /** - * Find the "best guess" middle value among comparables. If there is an even - * number of total values, the lower of the two middle values will be returned. - * - * @param type of values processed by this method - * @param items to compare - * @return T at middle position - * @throws NullPointerException if items is {@code null} - * @throws IllegalArgumentException if items is empty or contains {@code null} - * values - * @since 3.0.1 - */ - @SafeVarargs - public static > T median(final T... items) { - Validate.notEmpty(items); - Validate.noNullElements(items); - final TreeSet sort = new TreeSet<>(); - Collections.addAll(sort, items); - @SuppressWarnings("unchecked") // we know all items added were T instances - final T result = (T) sort.toArray()[(sort.size() - 1) / 2]; - return result; - } - - // Comparable - // ----------------------------------------------------------------------- - /** - *

- * Null safe comparison of Comparables. - *

- * - * @param type of the values processed by this method - * @param values the set of comparable values, may be null - * @return - *
    - *
  • If any objects are non-null and unequal, the lesser object. - *
  • If all objects are non-null and equal, the first. - *
  • If any of the comparables are null, the lesser of the non-null - * objects. - *
  • If all the comparables are null, null is returned. - *
- */ - @SafeVarargs - public static > T min(final T... values) { - T result = null; - if (values != null) { - for (final T value : values) { - if (compare(value, result, true) < 0) { - result = value; - } - } - } - return result; - } - - /** - *

- * Compares two objects for inequality, where either one or both objects may be - * {@code null}. - *

- * - *
-	 * ObjectUtils.notEqual(null, null)                  = false
-	 * ObjectUtils.notEqual(null, "")                    = true
-	 * ObjectUtils.notEqual("", null)                    = true
-	 * ObjectUtils.notEqual("", "")                      = false
-	 * ObjectUtils.notEqual(Boolean.TRUE, null)          = true
-	 * ObjectUtils.notEqual(Boolean.TRUE, "true")        = true
-	 * ObjectUtils.notEqual(Boolean.TRUE, Boolean.TRUE)  = false
-	 * ObjectUtils.notEqual(Boolean.TRUE, Boolean.FALSE) = true
-	 * 
- * - * @param object1 the first object, may be {@code null} - * @param object2 the second object, may be {@code null} - * @return {@code false} if the values of both objects are the same - */ - public static boolean notEqual(final Object object1, final Object object2) { - return !equals(object1, object2); - } - - /** - * Checks that the specified object reference is not {@code null} or empty per - * {@link #isEmpty(Object)}. Use this method for validation, for example: - * - *
- * - *
-	 * public Foo(Bar bar) {
-	 * 	this.bar = Objects.requireNonEmpty(bar);
-	 * }
-	 * 
- * - *
- * - * @param the type of the reference. - * @param obj the object reference to check for nullity. - * @return {@code obj} if not {@code null}. - * @throws NullPointerException if {@code obj} is {@code null}. - * @throws IllegalArgumentException if {@code obj} is empty per - * {@link #isEmpty(Object)}. - * @see #isEmpty(Object) - * @since 3.12.0 - */ - public static T requireNonEmpty(final T obj) { - return requireNonEmpty(obj, "object"); - } - - /** - * Checks that the specified object reference is not {@code null} or empty per - * {@link #isEmpty(Object)}. Use this method for validation, for example: - * - *
- * - *
-	 * public Foo(Bar bar) {
-	 * 	this.bar = Objects.requireNonEmpty(bar, "bar");
-	 * }
-	 * 
- * - *
- * - * @param the type of the reference. - * @param obj the object reference to check for nullity. - * @param message the exception message. - * @return {@code obj} if not {@code null}. - * @throws NullPointerException if {@code obj} is {@code null}. - * @throws IllegalArgumentException if {@code obj} is empty per - * {@link #isEmpty(Object)}. - * @see #isEmpty(Object) - * @since 3.12.0 - */ - public static T requireNonEmpty(final T obj, final String message) { - // check for null first to give the most precise exception. - Objects.requireNonNull(obj, message); - if (isEmpty(obj)) { - throw new IllegalArgumentException(message); - } - return obj; - } - - // ToString - // ----------------------------------------------------------------------- - /** - *

- * Gets the {@code toString} of an {@code Object} returning an empty string ("") - * if {@code null} input. - *

- * - *
-	 * ObjectUtils.toString(null)         = ""
-	 * ObjectUtils.toString("")           = ""
-	 * ObjectUtils.toString("bat")        = "bat"
-	 * ObjectUtils.toString(Boolean.TRUE) = "true"
-	 * 
- * - * @see StringUtils#defaultString(String) - * @see String#valueOf(Object) - * @param obj the Object to {@code toString}, may be null - * @return the passed in Object's toString, or {@code ""} if {@code null} input - * @since 2.0 - * @deprecated this method has been replaced by - * {@code java.util.Objects.toString(Object)} in Java 7 and will be - * removed in future releases. Note however that said method will - * return "null" for null references, while this method returns an - * empty String. To preserve behavior use - * {@code java.util.Objects.toString(myObject, "")} - */ - @Deprecated - public static String toString(final Object obj) { - return obj == null ? StringUtils.EMPTY : obj.toString(); - } - - /** - *

- * Gets the {@code toString} of an {@code Object} returning a specified text if - * {@code null} input. - *

- * - *
-	 * ObjectUtils.toString(null, null)           = null
-	 * ObjectUtils.toString(null, "null")         = "null"
-	 * ObjectUtils.toString("", "null")           = ""
-	 * ObjectUtils.toString("bat", "null")        = "bat"
-	 * ObjectUtils.toString(Boolean.TRUE, "null") = "true"
-	 * 
- * - * @see StringUtils#defaultString(String,String) - * @see String#valueOf(Object) - * @param obj the Object to {@code toString}, may be null - * @param nullStr the String to return if {@code null} input, may be null - * @return the passed in Object's toString, or {@code nullStr} if {@code null} - * input - * @since 2.0 - * @deprecated this method has been replaced by - * {@code java.util.Objects.toString(Object, String)} in Java 7 and - * will be removed in future releases. - */ - @Deprecated - public static String toString(final Object obj, final String nullStr) { - return obj == null ? nullStr : obj.toString(); - } - - /** - *

- * Gets the {@code toString} of an {@code Object} returning a specified text if - * {@code null} input. - *

- * - *
-	 * ObjectUtils.toString(obj, () -> expensive())
-	 * 
- * - *
-	 * ObjectUtils.toString(null, () -> expensive())         = result of expensive()
-	 * ObjectUtils.toString(null, () -> expensive())         = result of expensive()
-	 * ObjectUtils.toString("", () -> expensive())           = ""
-	 * ObjectUtils.toString("bat", () -> expensive())        = "bat"
-	 * ObjectUtils.toString(Boolean.TRUE, () -> expensive()) = "true"
-	 * 
- * - * @param obj the Object to {@code toString}, may be null - * @param supplier the Supplier of String used on {@code null} input, may be - * null - * @return the passed in Object's toString, or {@code nullStr} if {@code null} - * input - * @since 3.11 - */ - public static String toString(final Object obj, final Supplier supplier) { - return obj == null ? supplier == null ? null : supplier.get() : obj.toString(); - } - - /** - * Calls {@link Object#wait(long, int)} for the given Duration. - * - * @param obj The receiver of the wait call. - * @param duration How long to wait. - * @throws IllegalArgumentException if the timeout duration is negative. - * @throws IllegalMonitorStateException if the current thread is not the owner - * of the {@code obj}'s monitor. - * @throws InterruptedException if any thread interrupted the current - * thread before or while the current - * thread was waiting for a notification. - * The interrupted status of the - * current thread is cleared when this - * exception is thrown. - * @see Object#wait(long, int) - * @since 3.12.0 - */ - public static void wait(final Object obj, final Duration duration) throws InterruptedException { - DurationUtils.accept(obj::wait, DurationUtils.zeroIfNull(duration)); - } - - /** - *

- * {@code ObjectUtils} instances should NOT be constructed in standard - * programming. Instead, the static methods on the class should be used, such as - * {@code ObjectUtils.defaultIfNull("a","b");}. - *

- * - *

- * This constructor is public to permit tools that require a JavaBean instance - * to operate. - *

- */ - public ObjectUtils() { - } - -} diff --git a/src/main/java/org/apache/commons/lang3/RandomStringUtils.java b/src/main/java/org/apache/commons/lang3/RandomStringUtils.java deleted file mode 100755 index b886677f..00000000 --- a/src/main/java/org/apache/commons/lang3/RandomStringUtils.java +++ /dev/null @@ -1,577 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; - -/** - *

- * Generates random {@code String}s. - *

- * - *

- * Caveat: Instances of {@link EaglercraftRandom}, upon which the implementation of this - * class relies, are not cryptographically secure. - *

- * - *

- * RandomStringUtils is intended for simple use cases. For more advanced use - * cases consider using Apache Commons Text's - * RandomStringGenerator instead. - *

- * - *

- * The Apache Commons project provides - * Commons RNG dedicated to - * pseudo-random number generation, that may be a better choice for applications - * with more stringent requirements (performance and/or correctness). - *

- * - *

- * Note that private high surrogate characters are ignored. These are - * Unicode characters that fall between the values 56192 (db80) and 56319 (dbff) - * as we don't know how to handle them. High and low surrogates are correctly - * dealt with - that is if a high surrogate is randomly chosen, 55296 (d800) to - * 56191 (db7f) then it is followed by a low surrogate. If a low surrogate is - * chosen, 56320 (dc00) to 57343 (dfff) then it is placed after a randomly - * chosen high surrogate. - *

- * - *

- * #ThreadSafe# - *

- * - * @since 1.0 - */ -public class RandomStringUtils { - - /** - *

- * Random object used by random method. This has to be not local to the random - * method so as to not return the same value in the same millisecond. - *

- */ - private static final EaglercraftRandom RANDOM = new EaglercraftRandom(); - - /** - *

- * {@code RandomStringUtils} instances should NOT be constructed in standard - * programming. Instead, the class should be used as - * {@code RandomStringUtils.random(5);}. - *

- * - *

- * This constructor is public to permit tools that require a JavaBean instance - * to operate. - *

- */ - public RandomStringUtils() { - } - - // Random - // ----------------------------------------------------------------------- - /** - *

- * Creates a random string whose length is the number of characters specified. - *

- * - *

- * Characters will be chosen from the set of all characters. - *

- * - * @param count the length of random string to create - * @return the random string - */ - public static String random(final int count) { - return random(count, false, false); - } - - /** - *

- * Creates a random string whose length is the number of characters specified. - *

- * - *

- * Characters will be chosen from the set of characters whose ASCII value is - * between {@code 32} and {@code 126} (inclusive). - *

- * - * @param count the length of random string to create - * @return the random string - */ - public static String randomAscii(final int count) { - return random(count, 32, 127, false, false); - } - - /** - *

- * Creates a random string whose length is between the inclusive minimum and the - * exclusive maximum. - *

- * - *

- * Characters will be chosen from the set of characters whose ASCII value is - * between {@code 32} and {@code 126} (inclusive). - *

- * - * @param minLengthInclusive the inclusive minimum length of the string to - * generate - * @param maxLengthExclusive the exclusive maximum length of the string to - * generate - * @return the random string - * @since 3.5 - */ - public static String randomAscii(final int minLengthInclusive, final int maxLengthExclusive) { - return randomAscii(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive)); - } - - /** - *

- * Creates a random string whose length is the number of characters specified. - *

- * - *

- * Characters will be chosen from the set of Latin alphabetic characters (a-z, - * A-Z). - *

- * - * @param count the length of random string to create - * @return the random string - */ - public static String randomAlphabetic(final int count) { - return random(count, true, false); - } - - /** - *

- * Creates a random string whose length is between the inclusive minimum and the - * exclusive maximum. - *

- * - *

- * Characters will be chosen from the set of Latin alphabetic characters (a-z, - * A-Z). - *

- * - * @param minLengthInclusive the inclusive minimum length of the string to - * generate - * @param maxLengthExclusive the exclusive maximum length of the string to - * generate - * @return the random string - * @since 3.5 - */ - public static String randomAlphabetic(final int minLengthInclusive, final int maxLengthExclusive) { - return randomAlphabetic(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive)); - } - - /** - *

- * Creates a random string whose length is the number of characters specified. - *

- * - *

- * Characters will be chosen from the set of Latin alphabetic characters (a-z, - * A-Z) and the digits 0-9. - *

- * - * @param count the length of random string to create - * @return the random string - */ - public static String randomAlphanumeric(final int count) { - return random(count, true, true); - } - - /** - *

- * Creates a random string whose length is between the inclusive minimum and the - * exclusive maximum. - *

- * - *

- * Characters will be chosen from the set of Latin alphabetic characters (a-z, - * A-Z) and the digits 0-9. - *

- * - * @param minLengthInclusive the inclusive minimum length of the string to - * generate - * @param maxLengthExclusive the exclusive maximum length of the string to - * generate - * @return the random string - * @since 3.5 - */ - public static String randomAlphanumeric(final int minLengthInclusive, final int maxLengthExclusive) { - return randomAlphanumeric(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive)); - } - - /** - *

- * Creates a random string whose length is the number of characters specified. - *

- * - *

- * Characters will be chosen from the set of characters which match the POSIX - * [:graph:] regular expression character class. This class contains all visible - * ASCII characters (i.e. anything except spaces and control characters). - *

- * - * @param count the length of random string to create - * @return the random string - * @since 3.5 - */ - public static String randomGraph(final int count) { - return random(count, 33, 126, false, false); - } - - /** - *

- * Creates a random string whose length is between the inclusive minimum and the - * exclusive maximum. - *

- * - *

- * Characters will be chosen from the set of \p{Graph} characters. - *

- * - * @param minLengthInclusive the inclusive minimum length of the string to - * generate - * @param maxLengthExclusive the exclusive maximum length of the string to - * generate - * @return the random string - * @since 3.5 - */ - public static String randomGraph(final int minLengthInclusive, final int maxLengthExclusive) { - return randomGraph(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive)); - } - - /** - *

- * Creates a random string whose length is the number of characters specified. - *

- * - *

- * Characters will be chosen from the set of numeric characters. - *

- * - * @param count the length of random string to create - * @return the random string - */ - public static String randomNumeric(final int count) { - return random(count, false, true); - } - - /** - *

- * Creates a random string whose length is between the inclusive minimum and the - * exclusive maximum. - *

- * - *

- * Characters will be chosen from the set of \p{Digit} characters. - *

- * - * @param minLengthInclusive the inclusive minimum length of the string to - * generate - * @param maxLengthExclusive the exclusive maximum length of the string to - * generate - * @return the random string - * @since 3.5 - */ - public static String randomNumeric(final int minLengthInclusive, final int maxLengthExclusive) { - return randomNumeric(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive)); - } - - /** - *

- * Creates a random string whose length is the number of characters specified. - *

- * - *

- * Characters will be chosen from the set of characters which match the POSIX - * [:print:] regular expression character class. This class includes all visible - * ASCII characters and spaces (i.e. anything except control characters). - *

- * - * @param count the length of random string to create - * @return the random string - * @since 3.5 - */ - public static String randomPrint(final int count) { - return random(count, 32, 126, false, false); - } - - /** - *

- * Creates a random string whose length is between the inclusive minimum and the - * exclusive maximum. - *

- * - *

- * Characters will be chosen from the set of \p{Print} characters. - *

- * - * @param minLengthInclusive the inclusive minimum length of the string to - * generate - * @param maxLengthExclusive the exclusive maximum length of the string to - * generate - * @return the random string - * @since 3.5 - */ - public static String randomPrint(final int minLengthInclusive, final int maxLengthExclusive) { - return randomPrint(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive)); - } - - /** - *

- * Creates a random string whose length is the number of characters specified. - *

- * - *

- * Characters will be chosen from the set of alpha-numeric characters as - * indicated by the arguments. - *

- * - * @param count the length of random string to create - * @param letters if {@code true}, generated string may include alphabetic - * characters - * @param numbers if {@code true}, generated string may include numeric - * characters - * @return the random string - */ - public static String random(final int count, final boolean letters, final boolean numbers) { - return random(count, 0, 0, letters, numbers); - } - - /** - *

- * Creates a random string whose length is the number of characters specified. - *

- * - *

- * Characters will be chosen from the set of alpha-numeric characters as - * indicated by the arguments. - *

- * - * @param count the length of random string to create - * @param start the position in set of chars to start at - * @param end the position in set of chars to end before - * @param letters if {@code true}, generated string may include alphabetic - * characters - * @param numbers if {@code true}, generated string may include numeric - * characters - * @return the random string - */ - public static String random(final int count, final int start, final int end, final boolean letters, - final boolean numbers) { - return random(count, start, end, letters, numbers, null, RANDOM); - } - - /** - *

- * Creates a random string based on a variety of options, using default source - * of randomness. - *

- * - *

- * This method has exactly the same semantics as - * {@link #random(int,int,int,boolean,boolean,char[],EaglercraftRandom)}, but instead of - * using an externally supplied source of randomness, it uses the internal - * static {@link EaglercraftRandom} instance. - *

- * - * @param count the length of random string to create - * @param start the position in set of chars to start at - * @param end the position in set of chars to end before - * @param letters if {@code true}, generated string may include alphabetic - * characters - * @param numbers if {@code true}, generated string may include numeric - * characters - * @param chars the set of chars to choose randoms from. If {@code null}, then - * it will use the set of all chars. - * @return the random string - * @throws ArrayIndexOutOfBoundsException if there are not - * {@code (end - start) + 1} characters - * in the set array. - */ - public static String random(final int count, final int start, final int end, final boolean letters, - final boolean numbers, final char... chars) { - return random(count, start, end, letters, numbers, chars, RANDOM); - } - - /** - *

- * Creates a random string based on a variety of options, using supplied source - * of randomness. - *

- * - *

- * If start and end are both {@code 0}, start and end are set to {@code ' '} and - * {@code 'z'}, the ASCII printable characters, will be used, unless letters and - * numbers are both {@code false}, in which case, start and end are set to - * {@code 0} and {@link Character#MAX_CODE_POINT}. - * - *

- * If set is not {@code null}, characters between start and end are chosen. - *

- * - *

- * This method accepts a user-supplied {@link EaglercraftRandom} instance to use as a - * source of randomness. By seeding a single {@link EaglercraftRandom} instance with a - * fixed seed and using it for each call, the same random sequence of strings - * can be generated repeatedly and predictably. - *

- * - * @param count the length of random string to create - * @param start the position in set of chars to start at (inclusive) - * @param end the position in set of chars to end before (exclusive) - * @param letters if {@code true}, generated string may include alphabetic - * characters - * @param numbers if {@code true}, generated string may include numeric - * characters - * @param chars the set of chars to choose randoms from, must not be empty. If - * {@code null}, then it will use the set of all chars. - * @param random a source of randomness. - * @return the random string - * @throws ArrayIndexOutOfBoundsException if there are not - * {@code (end - start) + 1} characters - * in the set array. - * @throws IllegalArgumentException if {@code count} < 0 or the - * provided chars array is empty. - * @since 2.0 - */ - public static String random(int count, int start, int end, final boolean letters, final boolean numbers, - final char[] chars, final EaglercraftRandom random) { - if (count == 0) { - return StringUtils.EMPTY; - } else if (count < 0) { - throw new IllegalArgumentException("Requested random string length " + count + " is less than 0."); - } - if (chars != null && chars.length == 0) { - throw new IllegalArgumentException("The chars array must not be empty"); - } - - if (start == 0 && end == 0) { - if (chars != null) { - end = chars.length; - } else if (!letters && !numbers) { - end = Character.MAX_CODE_POINT; - } else { - end = 'z' + 1; - start = ' '; - } - } else if (end <= start) { - throw new IllegalArgumentException( - "Parameter end (" + end + ") must be greater than start (" + start + ")"); - } - - final int zero_digit_ascii = 48; - final int first_letter_ascii = 65; - - if (chars == null && (numbers && end <= zero_digit_ascii || letters && end <= first_letter_ascii)) { - throw new IllegalArgumentException( - "Parameter end (" + end + ") must be greater then (" + zero_digit_ascii + ") for generating digits " - + "or greater then (" + first_letter_ascii + ") for generating letters."); - } - - final StringBuilder builder = new StringBuilder(count); - final int gap = end - start; - - while (count-- != 0) { - final int codePoint; - if (chars == null) { - codePoint = random.nextInt(gap) + start; - - switch (Character.getType(codePoint)) { - case Character.UNASSIGNED: - case Character.PRIVATE_USE: - case Character.SURROGATE: - count++; - continue; - } - - } else { - codePoint = chars[random.nextInt(gap) + start]; - } - - final int numberOfChars = Character.charCount(codePoint); - if (count == 0 && numberOfChars > 1) { - count++; - continue; - } - - if (letters && Character.isLetter(codePoint) || numbers && Character.isDigit(codePoint) - || !letters && !numbers) { - builder.appendCodePoint(codePoint); - - if (numberOfChars == 2) { - count--; - } - - } else { - count++; - } - } - return builder.toString(); - } - - /** - *

- * Creates a random string whose length is the number of characters specified. - *

- * - *

- * Characters will be chosen from the set of characters specified by the string, - * must not be empty. If null, the set of all characters is used. - *

- * - * @param count the length of random string to create - * @param chars the String containing the set of characters to use, may be null, - * but must not be empty - * @return the random string - * @throws IllegalArgumentException if {@code count} < 0 or the string is - * empty. - */ - public static String random(final int count, final String chars) { - if (chars == null) { - return random(count, 0, 0, false, false, null, RANDOM); - } - return random(count, chars.toCharArray()); - } - - /** - *

- * Creates a random string whose length is the number of characters specified. - *

- * - *

- * Characters will be chosen from the set of characters specified. - *

- * - * @param count the length of random string to create - * @param chars the character array containing the set of characters to use, may - * be null - * @return the random string - * @throws IllegalArgumentException if {@code count} < 0. - */ - public static String random(final int count, final char... chars) { - if (chars == null) { - return random(count, 0, 0, false, false, null, RANDOM); - } - return random(count, 0, chars.length, false, false, chars, RANDOM); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/RandomUtils.java b/src/main/java/org/apache/commons/lang3/RandomUtils.java deleted file mode 100755 index c6f00101..00000000 --- a/src/main/java/org/apache/commons/lang3/RandomUtils.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; - -/** - *

- * Utility library that supplements the standard {@link EaglercraftRandom} class. - *

- * - *

- * Caveat: Instances of {@link EaglercraftRandom} are not cryptographically secure. - *

- * - *

- * Please note that the Apache Commons project provides a component dedicated to - * pseudo-random number generation, namely - * Commons RNG, that may be a - * better choice for applications with more stringent requirements (performance - * and/or correctness). - *

- * - * @since 3.3 - */ -public class RandomUtils { - - /** - * Random object used by random method. This has to be not local to the random - * method so as to not return the same value in the same millisecond. - */ - private static final EaglercraftRandom RANDOM = new EaglercraftRandom(); - - /** - *

- * {@code RandomUtils} instances should NOT be constructed in standard - * programming. Instead, the class should be used as - * {@code RandomUtils.nextBytes(5);}. - *

- * - *

- * This constructor is public to permit tools that require a JavaBean instance - * to operate. - *

- */ - public RandomUtils() { - } - - /** - *

- * Returns a random boolean value - *

- * - * @return the random boolean - * @since 3.5 - */ - public static boolean nextBoolean() { - return RANDOM.nextBoolean(); - } - - /** - *

- * Creates an array of random bytes. - *

- * - * @param count the size of the returned array - * @return the random byte array - * @throws IllegalArgumentException if {@code count} is negative - */ - public static byte[] nextBytes(final int count) { - Validate.isTrue(count >= 0, "Count cannot be negative."); - - final byte[] result = new byte[count]; - RANDOM.nextBytes(result); - return result; - } - - /** - *

- * Returns a random integer within the specified range. - *

- * - * @param startInclusive the smallest value that can be returned, must be - * non-negative - * @param endExclusive the upper bound (not included) - * @throws IllegalArgumentException if {@code startInclusive > endExclusive} or - * if {@code startInclusive} is negative - * @return the random integer - */ - public static int nextInt(final int startInclusive, final int endExclusive) { - Validate.isTrue(endExclusive >= startInclusive, "Start value must be smaller or equal to end value."); - Validate.isTrue(startInclusive >= 0, "Both range values must be non-negative."); - - if (startInclusive == endExclusive) { - return startInclusive; - } - - return startInclusive + RANDOM.nextInt(endExclusive - startInclusive); - } - - /** - *

- * Returns a random int within 0 - Integer.MAX_VALUE - *

- * - * @return the random integer - * @see #nextInt(int, int) - * @since 3.5 - */ - public static int nextInt() { - return nextInt(0, Integer.MAX_VALUE); - } - - /** - *

- * Returns a random long within the specified range. - *

- * - * @param startInclusive the smallest value that can be returned, must be - * non-negative - * @param endExclusive the upper bound (not included) - * @throws IllegalArgumentException if {@code startInclusive > endExclusive} or - * if {@code startInclusive} is negative - * @return the random long - */ - public static long nextLong(final long startInclusive, final long endExclusive) { - Validate.isTrue(endExclusive >= startInclusive, "Start value must be smaller or equal to end value."); - Validate.isTrue(startInclusive >= 0, "Both range values must be non-negative."); - - if (startInclusive == endExclusive) { - return startInclusive; - } - - return startInclusive + nextLong(endExclusive - startInclusive); - } - - /** - *

- * Returns a random long within 0 - Long.MAX_VALUE - *

- * - * @return the random long - * @see #nextLong(long, long) - * @since 3.5 - */ - public static long nextLong() { - return nextLong(Long.MAX_VALUE); - } - - /** - * Generates a {@code long} value between 0 (inclusive) and the specified value - * (exclusive). - * - * @param n Bound on the random number to be returned. Must be positive. - * @return a random {@code long} value between 0 (inclusive) and {@code n} - * (exclusive). - */ - private static long nextLong(final long n) { - // Extracted from o.a.c.rng.core.BaseProvider.nextLong(long) - long bits; - long val; - do { - bits = RANDOM.nextLong() >>> 1; - val = bits % n; - } while (bits - val + (n - 1) < 0); - - return val; - } - - /** - *

- * Returns a random double within the specified range. - *

- * - * @param startInclusive the smallest value that can be returned, must be - * non-negative - * @param endExclusive the upper bound (not included) - * @throws IllegalArgumentException if {@code startInclusive > endExclusive} or - * if {@code startInclusive} is negative - * @return the random double - */ - public static double nextDouble(final double startInclusive, final double endExclusive) { - Validate.isTrue(endExclusive >= startInclusive, "Start value must be smaller or equal to end value."); - Validate.isTrue(startInclusive >= 0, "Both range values must be non-negative."); - - if (startInclusive == endExclusive) { - return startInclusive; - } - - return startInclusive + ((endExclusive - startInclusive) * RANDOM.nextDouble()); - } - - /** - *

- * Returns a random double within 0 - Double.MAX_VALUE - *

- * - * @return the random double - * @see #nextDouble(double, double) - * @since 3.5 - */ - public static double nextDouble() { - return nextDouble(0, Double.MAX_VALUE); - } - - /** - *

- * Returns a random float within the specified range. - *

- * - * @param startInclusive the smallest value that can be returned, must be - * non-negative - * @param endExclusive the upper bound (not included) - * @throws IllegalArgumentException if {@code startInclusive > endExclusive} or - * if {@code startInclusive} is negative - * @return the random float - */ - public static float nextFloat(final float startInclusive, final float endExclusive) { - Validate.isTrue(endExclusive >= startInclusive, "Start value must be smaller or equal to end value."); - Validate.isTrue(startInclusive >= 0, "Both range values must be non-negative."); - - if (startInclusive == endExclusive) { - return startInclusive; - } - - return startInclusive + ((endExclusive - startInclusive) * RANDOM.nextFloat()); - } - - /** - *

- * Returns a random float within 0 - Float.MAX_VALUE - *

- * - * @return the random float - * @see #nextFloat(float, float) - * @since 3.5 - */ - public static float nextFloat() { - return nextFloat(0, Float.MAX_VALUE); - } -} diff --git a/src/main/java/org/apache/commons/lang3/Range.java b/src/main/java/org/apache/commons/lang3/Range.java deleted file mode 100755 index d17ae470..00000000 --- a/src/main/java/org/apache/commons/lang3/Range.java +++ /dev/null @@ -1,616 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import java.io.Serializable; -import java.util.Comparator; - -import net.lax1dude.eaglercraft.v1_8.HString; - -/** - *

- * An immutable range of objects from a minimum to maximum point inclusive. - *

- * - *

- * The objects need to either be implementations of {@code Comparable} or you - * need to supply a {@code Comparator}. - *

- * - *

- * #ThreadSafe# if the objects and comparator are thread-safe - *

- * - * @param The type of range values. - * @since 3.0 - */ -public final class Range implements Serializable { - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private enum ComparableComparator implements Comparator { - INSTANCE; - - /** - * Comparable based compare implementation. - * - * @param obj1 left hand side of comparison - * @param obj2 right hand side of comparison - * @return negative, 0, positive comparison value - */ - @Override - public int compare(final Object obj1, final Object obj2) { - return ((Comparable) obj1).compareTo(obj2); - } - } - - /** - * Serialization version. - * - * @see java.io.Serializable - */ - private static final long serialVersionUID = 1L; - - /** - *

- * Obtains a range with the specified minimum and maximum values (both - * inclusive). - *

- * - *

- * The range uses the natural ordering of the elements to determine where values - * lie in the range. - *

- * - *

- * The arguments may be passed in the order (min,max) or (max,min). The - * getMinimum and getMaximum methods will return the correct values. - *

- * - * @param the type of the elements in this range - * @param fromInclusive the first value that defines the edge of the range, - * inclusive - * @param toInclusive the second value that defines the edge of the range, - * inclusive - * @return the range object, not null - * @throws IllegalArgumentException if either element is null - * @throws ClassCastException if the elements are not {@code Comparable} - */ - public static > Range between(final T fromInclusive, final T toInclusive) { - return between(fromInclusive, toInclusive, null); - } - - /** - *

- * Obtains a range with the specified minimum and maximum values (both - * inclusive). - *

- * - *

- * The range uses the specified {@code Comparator} to determine where values lie - * in the range. - *

- * - *

- * The arguments may be passed in the order (min,max) or (max,min). The - * getMinimum and getMaximum methods will return the correct values. - *

- * - * @param the type of the elements in this range - * @param fromInclusive the first value that defines the edge of the range, - * inclusive - * @param toInclusive the second value that defines the edge of the range, - * inclusive - * @param comparator the comparator to be used, null for natural ordering - * @return the range object, not null - * @throws IllegalArgumentException if either element is null - * @throws ClassCastException if using natural ordering and the elements - * are not {@code Comparable} - */ - public static Range between(final T fromInclusive, final T toInclusive, final Comparator comparator) { - return new Range<>(fromInclusive, toInclusive, comparator); - } - - /** - *

- * Obtains a range using the specified element as both the minimum and maximum - * in this range. - *

- * - *

- * The range uses the natural ordering of the elements to determine where values - * lie in the range. - *

- * - * @param the type of the elements in this range - * @param element the value to use for this range, not null - * @return the range object, not null - * @throws IllegalArgumentException if the element is null - * @throws ClassCastException if the element is not {@code Comparable} - */ - public static > Range is(final T element) { - return between(element, element, null); - } - - /** - *

- * Obtains a range using the specified element as both the minimum and maximum - * in this range. - *

- * - *

- * The range uses the specified {@code Comparator} to determine where values lie - * in the range. - *

- * - * @param the type of the elements in this range - * @param element the value to use for this range, must not be {@code null} - * @param comparator the comparator to be used, null for natural ordering - * @return the range object, not null - * @throws IllegalArgumentException if the element is null - * @throws ClassCastException if using natural ordering and the elements - * are not {@code Comparable} - */ - public static Range is(final T element, final Comparator comparator) { - return between(element, element, comparator); - } - - /** - * The ordering scheme used in this range. - */ - private final Comparator comparator; - - /** - * Cached output hashCode (class is immutable). - */ - private transient int hashCode; - - /** - * The maximum value in this range (inclusive). - */ - private final T maximum; - - /** - * The minimum value in this range (inclusive). - */ - private final T minimum; - - /** - * Cached output toString (class is immutable). - */ - private transient String toString; - - /** - * Creates an instance. - * - * @param element1 the first element, not null - * @param element2 the second element, not null - * @param comp the comparator to be used, null for natural ordering - */ - @SuppressWarnings("unchecked") - private Range(final T element1, final T element2, final Comparator comp) { - if (element1 == null || element2 == null) { - throw new IllegalArgumentException( - "Elements in a range must not be null: element1=" + element1 + ", element2=" + element2); - } - if (comp == null) { - this.comparator = ComparableComparator.INSTANCE; - } else { - this.comparator = comp; - } - if (this.comparator.compare(element1, element2) < 1) { - this.minimum = element1; - this.maximum = element2; - } else { - this.minimum = element2; - this.maximum = element1; - } - } - - /** - *

- * Checks whether the specified element occurs within this range. - *

- * - * @param element the element to check for, null returns false - * @return true if the specified element occurs within this range - */ - public boolean contains(final T element) { - if (element == null) { - return false; - } - return comparator.compare(element, minimum) > -1 && comparator.compare(element, maximum) < 1; - } - - /** - *

- * Checks whether this range contains all the elements of the specified range. - *

- * - *

- * This method may fail if the ranges have two different comparators or element - * types. - *

- * - * @param otherRange the range to check, null returns false - * @return true if this range contains the specified range - * @throws RuntimeException if ranges cannot be compared - */ - public boolean containsRange(final Range otherRange) { - if (otherRange == null) { - return false; - } - return contains(otherRange.minimum) && contains(otherRange.maximum); - } - - /** - *

- * Checks where the specified element occurs relative to this range. - *

- * - *

- * The API is reminiscent of the Comparable interface returning {@code -1} if - * the element is before the range, {@code 0} if contained within the range and - * {@code 1} if the element is after the range. - *

- * - * @param element the element to check for, not null - * @return -1, 0 or +1 depending on the element's location relative to the range - */ - public int elementCompareTo(final T element) { - // Comparable API says throw NPE on null - Validate.notNull(element, "element"); - if (isAfter(element)) { - return -1; - } else if (isBefore(element)) { - return 1; - } else { - return 0; - } - } - - // Element tests - // -------------------------------------------------------------------- - - /** - *

- * Compares this range to another object to test if they are equal. - *

- * . - * - *

- * To be equal, the minimum and maximum values must be equal, which ignores any - * differences in the comparator. - *

- * - * @param obj the reference object with which to compare - * @return true if this object is equal - */ - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } else if (obj == null || obj.getClass() != getClass()) { - return false; - } else { - @SuppressWarnings("unchecked") // OK because we checked the class above - final Range range = (Range) obj; - return minimum.equals(range.minimum) && maximum.equals(range.maximum); - } - } - - /** - *

- * Gets the comparator being used to determine if objects are within the range. - *

- * - *

- * Natural ordering uses an internal comparator implementation, thus this method - * never returns null. See {@link #isNaturalOrdering()}. - *

- * - * @return the comparator being used, not null - */ - public Comparator getComparator() { - return comparator; - } - - /** - *

- * Gets the maximum value in this range. - *

- * - * @return the maximum value in this range, not null - */ - public T getMaximum() { - return maximum; - } - - /** - *

- * Gets the minimum value in this range. - *

- * - * @return the minimum value in this range, not null - */ - public T getMinimum() { - return minimum; - } - - /** - *

- * Gets a suitable hash code for the range. - *

- * - * @return a hash code value for this object - */ - @Override - public int hashCode() { - int result = hashCode; - if (hashCode == 0) { - result = 17; - result = 37 * result + getClass().hashCode(); - result = 37 * result + minimum.hashCode(); - result = 37 * result + maximum.hashCode(); - hashCode = result; - } - return result; - } - - /** - * Calculate the intersection of {@code this} and an overlapping Range. - * - * @param other overlapping Range - * @return range representing the intersection of {@code this} and {@code other} - * ({@code this} if equal) - * @throws IllegalArgumentException if {@code other} does not overlap - * {@code this} - * @since 3.0.1 - */ - public Range intersectionWith(final Range other) { - if (!this.isOverlappedBy(other)) { - throw new IllegalArgumentException( - HString.format("Cannot calculate intersection with non-overlapping range %s", other)); - } - if (this.equals(other)) { - return this; - } - final T min = getComparator().compare(minimum, other.minimum) < 0 ? other.minimum : minimum; - final T max = getComparator().compare(maximum, other.maximum) < 0 ? maximum : other.maximum; - return between(min, max, getComparator()); - } - - /** - *

- * Checks whether this range is after the specified element. - *

- * - * @param element the element to check for, null returns false - * @return true if this range is entirely after the specified element - */ - public boolean isAfter(final T element) { - if (element == null) { - return false; - } - return comparator.compare(element, minimum) < 0; - } - - /** - *

- * Checks whether this range is completely after the specified range. - *

- * - *

- * This method may fail if the ranges have two different comparators or element - * types. - *

- * - * @param otherRange the range to check, null returns false - * @return true if this range is completely after the specified range - * @throws RuntimeException if ranges cannot be compared - */ - public boolean isAfterRange(final Range otherRange) { - if (otherRange == null) { - return false; - } - return isAfter(otherRange.maximum); - } - - /** - *

- * Checks whether this range is before the specified element. - *

- * - * @param element the element to check for, null returns false - * @return true if this range is entirely before the specified element - */ - public boolean isBefore(final T element) { - if (element == null) { - return false; - } - return comparator.compare(element, maximum) > 0; - } - - /** - *

- * Checks whether this range is completely before the specified range. - *

- * - *

- * This method may fail if the ranges have two different comparators or element - * types. - *

- * - * @param otherRange the range to check, null returns false - * @return true if this range is completely before the specified range - * @throws RuntimeException if ranges cannot be compared - */ - public boolean isBeforeRange(final Range otherRange) { - if (otherRange == null) { - return false; - } - return isBefore(otherRange.minimum); - } - - /** - *

- * Checks whether this range ends with the specified element. - *

- * - * @param element the element to check for, null returns false - * @return true if the specified element occurs within this range - */ - public boolean isEndedBy(final T element) { - if (element == null) { - return false; - } - return comparator.compare(element, maximum) == 0; - } - - /** - *

- * Whether or not the Range is using the natural ordering of the elements. - *

- * - *

- * Natural ordering uses an internal comparator implementation, thus this method - * is the only way to check if a null comparator was specified. - *

- * - * @return true if using natural ordering - */ - public boolean isNaturalOrdering() { - return comparator == ComparableComparator.INSTANCE; - } - - /** - *

- * Checks whether this range is overlapped by the specified range. - *

- * - *

- * Two ranges overlap if there is at least one element in common. - *

- * - *

- * This method may fail if the ranges have two different comparators or element - * types. - *

- * - * @param otherRange the range to test, null returns false - * @return true if the specified range overlaps with this range; otherwise, - * {@code false} - * @throws RuntimeException if ranges cannot be compared - */ - public boolean isOverlappedBy(final Range otherRange) { - if (otherRange == null) { - return false; - } - return otherRange.contains(minimum) || otherRange.contains(maximum) || contains(otherRange.minimum); - } - - /** - *

- * Checks whether this range starts with the specified element. - *

- * - * @param element the element to check for, null returns false - * @return true if the specified element occurs within this range - */ - public boolean isStartedBy(final T element) { - if (element == null) { - return false; - } - return comparator.compare(element, minimum) == 0; - } - - /** - *

- * Fits the given element into this range by returning the given element or, if - * out of bounds, the range minimum if below, or the range maximum if above. - *

- * - *
-	 * Range<Integer> range = Range.between(16, 64);
-	 * range.fit(-9) -->  16
-	 * range.fit(0)  -->  16
-	 * range.fit(15) -->  16
-	 * range.fit(16) -->  16
-	 * range.fit(17) -->  17
-	 * ...
-	 * range.fit(63) -->  63
-	 * range.fit(64) -->  64
-	 * range.fit(99) -->  64
-	 * 
- * - * @param element the element to check for, not null - * @return the minimum, the element, or the maximum depending on the element's - * location relative to the range - * @since 3.10 - */ - public T fit(final T element) { - // Comparable API says throw NPE on null - Validate.notNull(element, "element"); - if (isAfter(element)) { - return minimum; - } else if (isBefore(element)) { - return maximum; - } else { - return element; - } - } - - /** - *

- * Gets the range as a {@code String}. - *

- * - *

- * The format of the String is '[min..max]'. - *

- * - * @return the {@code String} representation of this range - */ - @Override - public String toString() { - if (toString == null) { - toString = "[" + minimum + ".." + maximum + "]"; - } - return toString; - } - - /** - *

- * Formats the receiver using the given format. - *

- * - *

- * This uses {@link java.util.Formattable} to perform the formatting. Three - * variables may be used to embed the minimum, maximum and comparator. Use - * {@code %1$s} for the minimum element, {@code %2$s} for the maximum element - * and {@code %3$s} for the comparator. The default format used by - * {@code toString()} is {@code [%1$s..%2$s]}. - *

- * - * @param format the format string, optionally containing {@code %1$s}, - * {@code %2$s} and {@code %3$s}, not null - * @return the formatted string, not null - */ - public String toString(final String format) { - return HString.format(format, minimum, maximum, comparator); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/RegExUtils.java b/src/main/java/org/apache/commons/lang3/RegExUtils.java deleted file mode 100755 index 4a72f0b3..00000000 --- a/src/main/java/org/apache/commons/lang3/RegExUtils.java +++ /dev/null @@ -1,517 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import java.util.regex.Pattern; - -/** - *

- * Helpers to process Strings using regular expressions. - *

- * - * @see java.util.regex.Pattern - * @since 3.8 - */ -public class RegExUtils { - - /** - *

- * Removes each substring of the text String that matches the given regular - * expression pattern. - *

- * - * This method is a {@code null} safe equivalent to: - *
    - *
  • {@code pattern.matcher(text).replaceAll(StringUtils.EMPTY)}
  • - *
- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *
-	 * StringUtils.removeAll(null, *)      = null
-	 * StringUtils.removeAll("any", (Pattern) null)  = "any"
-	 * StringUtils.removeAll("any", Pattern.compile(""))    = "any"
-	 * StringUtils.removeAll("any", Pattern.compile(".*"))  = ""
-	 * StringUtils.removeAll("any", Pattern.compile(".+"))  = ""
-	 * StringUtils.removeAll("abc", Pattern.compile(".?"))  = ""
-	 * StringUtils.removeAll("A<__>\n<__>B", Pattern.compile("<.*>"))      = "A\nB"
-	 * StringUtils.removeAll("A<__>\n<__>B", Pattern.compile("(?s)<.*>"))  = "AB"
-	 * StringUtils.removeAll("A<__>\n<__>B", Pattern.compile("<.*>", Pattern.DOTALL))  = "AB"
-	 * StringUtils.removeAll("ABCabc123abc", Pattern.compile("[a-z]"))     = "ABC123"
-	 * 
- * - * @param text text to remove from, may be null - * @param regex the regular expression to which this string is to be matched - * @return the text with any removes processed, {@code null} if null String - * input - * - * @see #replaceAll(String, Pattern, String) - * @see java.util.regex.Matcher#replaceAll(String) - * @see java.util.regex.Pattern - */ - public static String removeAll(final String text, final Pattern regex) { - return replaceAll(text, regex, StringUtils.EMPTY); - } - - /** - *

- * Removes each substring of the text String that matches the given regular - * expression. - *

- * - * This method is a {@code null} safe equivalent to: - *
    - *
  • {@code text.replaceAll(regex, StringUtils.EMPTY)}
  • - *
  • {@code Pattern.compile(regex).matcher(text).replaceAll(StringUtils.EMPTY)}
  • - *
- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *

- * Unlike in the {@link #removePattern(String, String)} method, the - * {@link Pattern#DOTALL} option is NOT automatically added. To use the DOTALL - * option prepend {@code "(?s)"} to the regex. DOTALL is also known as - * single-line mode in Perl. - *

- * - *
-	 * StringUtils.removeAll(null, *)      = null
-	 * StringUtils.removeAll("any", (String) null)  = "any"
-	 * StringUtils.removeAll("any", "")    = "any"
-	 * StringUtils.removeAll("any", ".*")  = ""
-	 * StringUtils.removeAll("any", ".+")  = ""
-	 * StringUtils.removeAll("abc", ".?")  = ""
-	 * StringUtils.removeAll("A<__>\n<__>B", "<.*>")      = "A\nB"
-	 * StringUtils.removeAll("A<__>\n<__>B", "(?s)<.*>")  = "AB"
-	 * StringUtils.removeAll("ABCabc123abc", "[a-z]")     = "ABC123"
-	 * 
- * - * @param text text to remove from, may be null - * @param regex the regular expression to which this string is to be matched - * @return the text with any removes processed, {@code null} if null String - * input - * - * @throws java.util.regex.PatternSyntaxException if the regular expression's - * syntax is invalid - * - * @see #replaceAll(String, String, String) - * @see #removePattern(String, String) - * @see String#replaceAll(String, String) - * @see java.util.regex.Pattern - * @see java.util.regex.Pattern#DOTALL - */ - public static String removeAll(final String text, final String regex) { - return replaceAll(text, regex, StringUtils.EMPTY); - } - - /** - *

- * Removes the first substring of the text string that matches the given regular - * expression pattern. - *

- * - * This method is a {@code null} safe equivalent to: - *
    - *
  • {@code pattern.matcher(text).replaceFirst(StringUtils.EMPTY)}
  • - *
- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *
-	 * StringUtils.removeFirst(null, *)      = null
-	 * StringUtils.removeFirst("any", (Pattern) null)  = "any"
-	 * StringUtils.removeFirst("any", Pattern.compile(""))    = "any"
-	 * StringUtils.removeFirst("any", Pattern.compile(".*"))  = ""
-	 * StringUtils.removeFirst("any", Pattern.compile(".+"))  = ""
-	 * StringUtils.removeFirst("abc", Pattern.compile(".?"))  = "bc"
-	 * StringUtils.removeFirst("A<__>\n<__>B", Pattern.compile("<.*>"))      = "A\n<__>B"
-	 * StringUtils.removeFirst("A<__>\n<__>B", Pattern.compile("(?s)<.*>"))  = "AB"
-	 * StringUtils.removeFirst("ABCabc123", Pattern.compile("[a-z]"))          = "ABCbc123"
-	 * StringUtils.removeFirst("ABCabc123abc", Pattern.compile("[a-z]+"))      = "ABC123abc"
-	 * 
- * - * @param text text to remove from, may be null - * @param regex the regular expression pattern to which this string is to be - * matched - * @return the text with the first replacement processed, {@code null} if null - * String input - * - * @see #replaceFirst(String, Pattern, String) - * @see java.util.regex.Matcher#replaceFirst(String) - * @see java.util.regex.Pattern - */ - public static String removeFirst(final String text, final Pattern regex) { - return replaceFirst(text, regex, StringUtils.EMPTY); - } - - /** - *

- * Removes the first substring of the text string that matches the given regular - * expression. - *

- * - * This method is a {@code null} safe equivalent to: - *
    - *
  • {@code text.replaceFirst(regex, StringUtils.EMPTY)}
  • - *
  • {@code Pattern.compile(regex).matcher(text).replaceFirst(StringUtils.EMPTY)}
  • - *
- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *

- * The {@link Pattern#DOTALL} option is NOT automatically added. To use the - * DOTALL option prepend {@code "(?s)"} to the regex. DOTALL is also known as - * single-line mode in Perl. - *

- * - *
-	 * StringUtils.removeFirst(null, *)      = null
-	 * StringUtils.removeFirst("any", (String) null)  = "any"
-	 * StringUtils.removeFirst("any", "")    = "any"
-	 * StringUtils.removeFirst("any", ".*")  = ""
-	 * StringUtils.removeFirst("any", ".+")  = ""
-	 * StringUtils.removeFirst("abc", ".?")  = "bc"
-	 * StringUtils.removeFirst("A<__>\n<__>B", "<.*>")      = "A\n<__>B"
-	 * StringUtils.removeFirst("A<__>\n<__>B", "(?s)<.*>")  = "AB"
-	 * StringUtils.removeFirst("ABCabc123", "[a-z]")          = "ABCbc123"
-	 * StringUtils.removeFirst("ABCabc123abc", "[a-z]+")      = "ABC123abc"
-	 * 
- * - * @param text text to remove from, may be null - * @param regex the regular expression to which this string is to be matched - * @return the text with the first replacement processed, {@code null} if null - * String input - * - * @throws java.util.regex.PatternSyntaxException if the regular expression's - * syntax is invalid - * - * @see #replaceFirst(String, String, String) - * @see String#replaceFirst(String, String) - * @see java.util.regex.Pattern - * @see java.util.regex.Pattern#DOTALL - */ - public static String removeFirst(final String text, final String regex) { - return replaceFirst(text, regex, StringUtils.EMPTY); - } - - /** - *

- * Removes each substring of the source String that matches the given regular - * expression using the DOTALL option. - *

- * - * This call is a {@code null} safe equivalent to: - *
    - *
  • {@code text.replaceAll("(?s)" + regex, StringUtils.EMPTY)}
  • - *
  • {@code Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(StringUtils.EMPTY)}
  • - *
- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *
-	 * StringUtils.removePattern(null, *)       = null
-	 * StringUtils.removePattern("any", (String) null)   = "any"
-	 * StringUtils.removePattern("A<__>\n<__>B", "<.*>")  = "AB"
-	 * StringUtils.removePattern("ABCabc123", "[a-z]")    = "ABC123"
-	 * 
- * - * @param text the source string - * @param regex the regular expression to which this string is to be matched - * @return The resulting {@code String} - * @see #replacePattern(String, String, String) - * @see String#replaceAll(String, String) - * @see Pattern#DOTALL - */ - public static String removePattern(final String text, final String regex) { - return replacePattern(text, regex, StringUtils.EMPTY); - } - - /** - *

- * Replaces each substring of the text String that matches the given regular - * expression pattern with the given replacement. - *

- * - * This method is a {@code null} safe equivalent to: - *
    - *
  • {@code pattern.matcher(text).replaceAll(replacement)}
  • - *
- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *
-	 * StringUtils.replaceAll(null, *, *)       = null
-	 * StringUtils.replaceAll("any", (Pattern) null, *)   = "any"
-	 * StringUtils.replaceAll("any", *, null)   = "any"
-	 * StringUtils.replaceAll("", Pattern.compile(""), "zzz")    = "zzz"
-	 * StringUtils.replaceAll("", Pattern.compile(".*"), "zzz")  = "zzz"
-	 * StringUtils.replaceAll("", Pattern.compile(".+"), "zzz")  = ""
-	 * StringUtils.replaceAll("abc", Pattern.compile(""), "ZZ")  = "ZZaZZbZZcZZ"
-	 * StringUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>"), "z")                 = "z\nz"
-	 * StringUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>", Pattern.DOTALL), "z") = "z"
-	 * StringUtils.replaceAll("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z")             = "z"
-	 * StringUtils.replaceAll("ABCabc123", Pattern.compile("[a-z]"), "_")       = "ABC___123"
-	 * StringUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "_")  = "ABC_123"
-	 * StringUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "")   = "ABC123"
-	 * StringUtils.replaceAll("Lorem ipsum  dolor   sit", Pattern.compile("( +)([a-z]+)"), "_$2")  = "Lorem_ipsum_dolor_sit"
-	 * 
- * - * @param text text to search and replace in, may be null - * @param regex the regular expression pattern to which this string is to - * be matched - * @param replacement the string to be substituted for each match - * @return the text with any replacements processed, {@code null} if null String - * input - * - * @see java.util.regex.Matcher#replaceAll(String) - * @see java.util.regex.Pattern - */ - public static String replaceAll(final String text, final Pattern regex, final String replacement) { - if (ObjectUtils.anyNull(text, regex, replacement)) { - return text; - } - return regex.matcher(text).replaceAll(replacement); - } - - /** - *

- * Replaces each substring of the text String that matches the given regular - * expression with the given replacement. - *

- * - * This method is a {@code null} safe equivalent to: - *
    - *
  • {@code text.replaceAll(regex, replacement)}
  • - *
  • {@code Pattern.compile(regex).matcher(text).replaceAll(replacement)}
  • - *
- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *

- * Unlike in the {@link #replacePattern(String, String, String)} method, the - * {@link Pattern#DOTALL} option is NOT automatically added. To use the DOTALL - * option prepend {@code "(?s)"} to the regex. DOTALL is also known as - * single-line mode in Perl. - *

- * - *
-	 * StringUtils.replaceAll(null, *, *)       = null
-	 * StringUtils.replaceAll("any", (String) null, *)   = "any"
-	 * StringUtils.replaceAll("any", *, null)   = "any"
-	 * StringUtils.replaceAll("", "", "zzz")    = "zzz"
-	 * StringUtils.replaceAll("", ".*", "zzz")  = "zzz"
-	 * StringUtils.replaceAll("", ".+", "zzz")  = ""
-	 * StringUtils.replaceAll("abc", "", "ZZ")  = "ZZaZZbZZcZZ"
-	 * StringUtils.replaceAll("<__>\n<__>", "<.*>", "z")      = "z\nz"
-	 * StringUtils.replaceAll("<__>\n<__>", "(?s)<.*>", "z")  = "z"
-	 * StringUtils.replaceAll("ABCabc123", "[a-z]", "_")       = "ABC___123"
-	 * StringUtils.replaceAll("ABCabc123", "[^A-Z0-9]+", "_")  = "ABC_123"
-	 * StringUtils.replaceAll("ABCabc123", "[^A-Z0-9]+", "")   = "ABC123"
-	 * StringUtils.replaceAll("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum_dolor_sit"
-	 * 
- * - * @param text text to search and replace in, may be null - * @param regex the regular expression to which this string is to be - * matched - * @param replacement the string to be substituted for each match - * @return the text with any replacements processed, {@code null} if null String - * input - * - * @throws java.util.regex.PatternSyntaxException if the regular expression's - * syntax is invalid - * - * @see #replacePattern(String, String, String) - * @see String#replaceAll(String, String) - * @see java.util.regex.Pattern - * @see java.util.regex.Pattern#DOTALL - */ - public static String replaceAll(final String text, final String regex, final String replacement) { - if (ObjectUtils.anyNull(text, regex, replacement)) { - return text; - } - return text.replaceAll(regex, replacement); - } - - /** - *

- * Replaces the first substring of the text string that matches the given - * regular expression pattern with the given replacement. - *

- * - * This method is a {@code null} safe equivalent to: - *
    - *
  • {@code pattern.matcher(text).replaceFirst(replacement)}
  • - *
- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *
-	 * StringUtils.replaceFirst(null, *, *)       = null
-	 * StringUtils.replaceFirst("any", (Pattern) null, *)   = "any"
-	 * StringUtils.replaceFirst("any", *, null)   = "any"
-	 * StringUtils.replaceFirst("", Pattern.compile(""), "zzz")    = "zzz"
-	 * StringUtils.replaceFirst("", Pattern.compile(".*"), "zzz")  = "zzz"
-	 * StringUtils.replaceFirst("", Pattern.compile(".+"), "zzz")  = ""
-	 * StringUtils.replaceFirst("abc", Pattern.compile(""), "ZZ")  = "ZZabc"
-	 * StringUtils.replaceFirst("<__>\n<__>", Pattern.compile("<.*>"), "z")      = "z\n<__>"
-	 * StringUtils.replaceFirst("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z")  = "z"
-	 * StringUtils.replaceFirst("ABCabc123", Pattern.compile("[a-z]"), "_")          = "ABC_bc123"
-	 * StringUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "_")  = "ABC_123abc"
-	 * StringUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "")   = "ABC123abc"
-	 * StringUtils.replaceFirst("Lorem ipsum  dolor   sit", Pattern.compile("( +)([a-z]+)"), "_$2")  = "Lorem_ipsum  dolor   sit"
-	 * 
- * - * @param text text to search and replace in, may be null - * @param regex the regular expression pattern to which this string is to - * be matched - * @param replacement the string to be substituted for the first match - * @return the text with the first replacement processed, {@code null} if null - * String input - * - * @see java.util.regex.Matcher#replaceFirst(String) - * @see java.util.regex.Pattern - */ - public static String replaceFirst(final String text, final Pattern regex, final String replacement) { - if (text == null || regex == null || replacement == null) { - return text; - } - return regex.matcher(text).replaceFirst(replacement); - } - - /** - *

- * Replaces the first substring of the text string that matches the given - * regular expression with the given replacement. - *

- * - * This method is a {@code null} safe equivalent to: - *
    - *
  • {@code text.replaceFirst(regex, replacement)}
  • - *
  • {@code Pattern.compile(regex).matcher(text).replaceFirst(replacement)}
  • - *
- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *

- * The {@link Pattern#DOTALL} option is NOT automatically added. To use the - * DOTALL option prepend {@code "(?s)"} to the regex. DOTALL is also known as - * single-line mode in Perl. - *

- * - *
-	 * StringUtils.replaceFirst(null, *, *)       = null
-	 * StringUtils.replaceFirst("any", (String) null, *)   = "any"
-	 * StringUtils.replaceFirst("any", *, null)   = "any"
-	 * StringUtils.replaceFirst("", "", "zzz")    = "zzz"
-	 * StringUtils.replaceFirst("", ".*", "zzz")  = "zzz"
-	 * StringUtils.replaceFirst("", ".+", "zzz")  = ""
-	 * StringUtils.replaceFirst("abc", "", "ZZ")  = "ZZabc"
-	 * StringUtils.replaceFirst("<__>\n<__>", "<.*>", "z")      = "z\n<__>"
-	 * StringUtils.replaceFirst("<__>\n<__>", "(?s)<.*>", "z")  = "z"
-	 * StringUtils.replaceFirst("ABCabc123", "[a-z]", "_")          = "ABC_bc123"
-	 * StringUtils.replaceFirst("ABCabc123abc", "[^A-Z0-9]+", "_")  = "ABC_123abc"
-	 * StringUtils.replaceFirst("ABCabc123abc", "[^A-Z0-9]+", "")   = "ABC123abc"
-	 * StringUtils.replaceFirst("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum  dolor   sit"
-	 * 
- * - * @param text text to search and replace in, may be null - * @param regex the regular expression to which this string is to be - * matched - * @param replacement the string to be substituted for the first match - * @return the text with the first replacement processed, {@code null} if null - * String input - * - * @throws java.util.regex.PatternSyntaxException if the regular expression's - * syntax is invalid - * - * @see String#replaceFirst(String, String) - * @see java.util.regex.Pattern - * @see java.util.regex.Pattern#DOTALL - */ - public static String replaceFirst(final String text, final String regex, final String replacement) { - if (text == null || regex == null || replacement == null) { - return text; - } - return text.replaceFirst(regex, replacement); - } - - /** - *

- * Replaces each substring of the source String that matches the given regular - * expression with the given replacement using the {@link Pattern#DOTALL} - * option. DOTALL is also known as single-line mode in Perl. - *

- * - * This call is a {@code null} safe equivalent to: - *
    - *
  • {@code text.replaceAll("(?s)" + regex, replacement)}
  • - *
  • {@code Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(replacement)}
  • - *
- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *
-	 * StringUtils.replacePattern(null, *, *)       = null
-	 * StringUtils.replacePattern("any", (String) null, *)   = "any"
-	 * StringUtils.replacePattern("any", *, null)   = "any"
-	 * StringUtils.replacePattern("", "", "zzz")    = "zzz"
-	 * StringUtils.replacePattern("", ".*", "zzz")  = "zzz"
-	 * StringUtils.replacePattern("", ".+", "zzz")  = ""
-	 * StringUtils.replacePattern("<__>\n<__>", "<.*>", "z")       = "z"
-	 * StringUtils.replacePattern("ABCabc123", "[a-z]", "_")       = "ABC___123"
-	 * StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "_")  = "ABC_123"
-	 * StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "")   = "ABC123"
-	 * StringUtils.replacePattern("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum_dolor_sit"
-	 * 
- * - * @param text the source string - * @param regex the regular expression to which this string is to be - * matched - * @param replacement the string to be substituted for each match - * @return The resulting {@code String} - * @see #replaceAll(String, String, String) - * @see String#replaceAll(String, String) - * @see Pattern#DOTALL - */ - public static String replacePattern(final String text, final String regex, final String replacement) { - if (ObjectUtils.anyNull(text, regex, replacement)) { - return text; - } - return Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(replacement); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/SerializationException.java b/src/main/java/org/apache/commons/lang3/SerializationException.java deleted file mode 100755 index f5f3e341..00000000 --- a/src/main/java/org/apache/commons/lang3/SerializationException.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -/** - *

- * Exception thrown when the Serialization process fails. - *

- * - *

- * The original error is wrapped within this one. - *

- * - *

- * #NotThreadSafe# because Throwable is not thread-safe - *

- * - * @since 1.0 - */ -public class SerializationException extends RuntimeException { - - /** - * Required for serialization support. - * - * @see java.io.Serializable - */ - private static final long serialVersionUID = 4029025366392702726L; - - /** - *

- * Constructs a new {@code SerializationException} without specified detail - * message. - *

- */ - public SerializationException() { - } - - /** - *

- * Constructs a new {@code SerializationException} with specified detail - * message. - *

- * - * @param msg The error message. - */ - public SerializationException(final String msg) { - super(msg); - } - - /** - *

- * Constructs a new {@code SerializationException} with specified nested - * {@code Throwable}. - *

- * - * @param cause The {@code Exception} or {@code Error} that caused this - * exception to be thrown. - */ - public SerializationException(final Throwable cause) { - super(cause); - } - - /** - *

- * Constructs a new {@code SerializationException} with specified detail message - * and nested {@code Throwable}. - *

- * - * @param msg The error message. - * @param cause The {@code Exception} or {@code Error} that caused this - * exception to be thrown. - */ - public SerializationException(final String msg, final Throwable cause) { - super(msg, cause); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/SerializationUtils.java b/src/main/java/org/apache/commons/lang3/SerializationUtils.java deleted file mode 100755 index e435e8f4..00000000 --- a/src/main/java/org/apache/commons/lang3/SerializationUtils.java +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.ObjectStreamClass; -import java.io.OutputStream; -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; - -/** - *

- * Assists with the serialization process and performs additional functionality - * based on serialization. - *

- * - *
    - *
  • Deep clone using serialization - *
  • Serialize managing finally and IOException - *
  • Deserialize managing finally and IOException - *
- * - *

- * This class throws exceptions for invalid {@code null} inputs. Each method - * documents its behavior in more detail. - *

- * - *

- * #ThreadSafe# - *

- * - * @since 1.0 - */ -public class SerializationUtils { - - /** - *

- * Custom specialization of the standard JDK {@link java.io.ObjectInputStream} - * that uses a custom {@code ClassLoader} to resolve a class. If the specified - * {@code ClassLoader} is not able to resolve the class, the context classloader - * of the current thread will be used. This way, the standard deserialization - * work also in web-application containers and application servers, no matter in - * which of the {@code ClassLoader} the particular class that encapsulates - * serialization/deserialization lives. - *

- * - *

- * For more in-depth information about the problem for which this class here is - * a workaround, see the JIRA issue LANG-626. - *

- */ - static class ClassLoaderAwareObjectInputStream extends ObjectInputStream { - private static final Map> primitiveTypes = new HashMap<>(); - - static { - primitiveTypes.put("byte", byte.class); - primitiveTypes.put("short", short.class); - primitiveTypes.put("int", int.class); - primitiveTypes.put("long", long.class); - primitiveTypes.put("float", float.class); - primitiveTypes.put("double", double.class); - primitiveTypes.put("boolean", boolean.class); - primitiveTypes.put("char", char.class); - primitiveTypes.put("void", void.class); - } - - private final ClassLoader classLoader; - - /** - * Constructor. - * - * @param in The {@code InputStream}. - * @param classLoader classloader to use - * @throws IOException if an I/O error occurs while reading stream header. - * @see java.io.ObjectInputStream - */ - ClassLoaderAwareObjectInputStream(final InputStream in, final ClassLoader classLoader) throws IOException { - super(in); - this.classLoader = classLoader; - } - - /** - * Overridden version that uses the parameterized {@code ClassLoader} or the - * {@code ClassLoader} of the current {@code Thread} to resolve the class. - * - * @param desc An instance of class {@code ObjectStreamClass}. - * @return A {@code Class} object corresponding to {@code desc}. - * @throws IOException Any of the usual Input/Output exceptions. - * @throws ClassNotFoundException If class of a serialized object cannot be - * found. - */ - @Override - protected Class resolveClass(final ObjectStreamClass desc) throws IOException, ClassNotFoundException { - final String name = desc.getName(); - try { - return Class.forName(name, false, classLoader); - } catch (final ClassNotFoundException ex) { - try { - return Class.forName(name, false, Thread.currentThread().getContextClassLoader()); - } catch (final ClassNotFoundException cnfe) { - final Class cls = primitiveTypes.get(name); - if (cls != null) { - return cls; - } - throw cnfe; - } - } - } - - } - - /** - *

- * Deep clone an {@code Object} using serialization. - *

- * - *

- * This is many times slower than writing clone methods by hand on all objects - * in your object graph. However, for complex object graphs, or for those that - * don't support deep cloning this can be a simple alternative implementation. - * Of course all the objects must be {@code Serializable}. - *

- * - * @param the type of the object involved - * @param object the {@code Serializable} object to clone - * @return the cloned object - * @throws SerializationException (runtime) if the serialization fails - */ - public static T clone(final T object) { - if (object == null) { - return null; - } - final byte[] objectData = serialize(object); - final EaglerInputStream bais = new EaglerInputStream(objectData); - - try (ClassLoaderAwareObjectInputStream in = new ClassLoaderAwareObjectInputStream(bais, - object.getClass().getClassLoader())) { - /* - * when we serialize and deserialize an object, it is reasonable to assume the - * deserialized object is of the same type as the original serialized object - */ - @SuppressWarnings("unchecked") // see above - final T readObject = (T) in.readObject(); - return readObject; - - } catch (final ClassNotFoundException ex) { - throw new SerializationException("ClassNotFoundException while reading cloned object data", ex); - } catch (final IOException ex) { - throw new SerializationException("IOException while reading or closing cloned object data", ex); - } - } - - /** - *

- * Deserializes a single {@code Object} from an array of bytes. - *

- * - *

- * If the call site incorrectly types the return value, a - * {@link ClassCastException} is thrown from the call site. Without Generics in - * this declaration, the call site must type cast and can cause the same - * ClassCastException. Note that in both cases, the ClassCastException is in the - * call site, not in this method. - *

- * - * @param the object type to be deserialized - * @param objectData the serialized object, must not be null - * @return the deserialized object - * @throws NullPointerException if {@code objectData} is {@code null} - * @throws SerializationException (runtime) if the serialization fails - */ - public static T deserialize(final byte[] objectData) { - Validate.notNull(objectData, "objectData"); - return deserialize(new EaglerInputStream(objectData)); - } - - /** - *

- * Deserializes an {@code Object} from the specified stream. - *

- * - *

- * The stream will be closed once the object is written. This avoids the need - * for a finally clause, and maybe also exception handling, in the application - * code. - *

- * - *

- * The stream passed in is not buffered internally within this method. This is - * the responsibility of your application if desired. - *

- * - *

- * If the call site incorrectly types the return value, a - * {@link ClassCastException} is thrown from the call site. Without Generics in - * this declaration, the call site must type cast and can cause the same - * ClassCastException. Note that in both cases, the ClassCastException is in the - * call site, not in this method. - *

- * - * @param the object type to be deserialized - * @param inputStream the serialized object input stream, must not be null - * @return the deserialized object - * @throws NullPointerException if {@code inputStream} is {@code null} - * @throws SerializationException (runtime) if the serialization fails - */ - @SuppressWarnings("resource") // inputStream is managed by the caller - public static T deserialize(final InputStream inputStream) { - Validate.notNull(inputStream, "inputStream"); - try (ObjectInputStream in = new ObjectInputStream(inputStream)) { - @SuppressWarnings("unchecked") - final T obj = (T) in.readObject(); - return obj; - } catch (final ClassNotFoundException | IOException ex) { - throw new SerializationException(ex); - } - } - - /** - * Performs a serialization roundtrip. Serializes and deserializes the given - * object, great for testing objects that implement {@link Serializable}. - * - * @param the type of the object involved - * @param obj the object to roundtrip - * @return the serialized and deserialized object - * @since 3.3 - */ - @SuppressWarnings("unchecked") // OK, because we serialized a type `T` - public static T roundtrip(final T obj) { - return (T) deserialize(serialize(obj)); - } - - /** - *

- * Serializes an {@code Object} to a byte array for storage/serialization. - *

- * - * @param obj the object to serialize to bytes - * @return a byte[] with the converted Serializable - * @throws SerializationException (runtime) if the serialization fails - */ - public static byte[] serialize(final Serializable obj) { - final ByteArrayOutputStream baos = new ByteArrayOutputStream(512); - serialize(obj, baos); - return baos.toByteArray(); - } - - /** - *

- * Serializes an {@code Object} to the specified stream. - *

- * - *

- * The stream will be closed once the object is written. This avoids the need - * for a finally clause, and maybe also exception handling, in the application - * code. - *

- * - *

- * The stream passed in is not buffered internally within this method. This is - * the responsibility of your application if desired. - *

- * - * @param obj the object to serialize to bytes, may be null - * @param outputStream the stream to write to, must not be null - * @throws NullPointerException if {@code outputStream} is {@code null} - * @throws SerializationException (runtime) if the serialization fails - */ - @SuppressWarnings("resource") // outputStream is managed by the caller - public static void serialize(final Serializable obj, final OutputStream outputStream) { - Validate.notNull(outputStream, "outputStream"); - try (ObjectOutputStream out = new ObjectOutputStream(outputStream)) { - out.writeObject(obj); - } catch (final IOException ex) { - throw new SerializationException(ex); - } - } - - /** - *

- * SerializationUtils instances should NOT be constructed in standard - * programming. Instead, the class should be used as - * {@code SerializationUtils.clone(object)}. - *

- * - *

- * This constructor is public to permit tools that require a JavaBean instance - * to operate. - *

- * - * @since 2.0 - */ - public SerializationUtils() { - } - -} diff --git a/src/main/java/org/apache/commons/lang3/Streams.java b/src/main/java/org/apache/commons/lang3/Streams.java deleted file mode 100755 index b31b6ec4..00000000 --- a/src/main/java/org/apache/commons/lang3/Streams.java +++ /dev/null @@ -1,565 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.BinaryOperator; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.stream.Collector; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.apache.commons.lang3.Functions.FailableConsumer; -import org.apache.commons.lang3.Functions.FailableFunction; -import org.apache.commons.lang3.Functions.FailablePredicate; - -/** - * Provides utility functions, and classes for working with the - * {@code java.util.stream} package, or more generally, with Java 8 lambdas. - * More specifically, it attempts to address the fact that lambdas are supposed - * not to throw Exceptions, at least not checked Exceptions, AKA instances of - * {@link Exception}. This enforces the use of constructs like - * - *
- * Consumer<java.lang.reflect.Method> consumer = m -> {
- * 	try {
- * 		m.invoke(o, args);
- * 	} catch (Throwable t) {
- * 		throw Functions.rethrow(t);
- * 	}
- * };
- * stream.forEach(consumer);
- * 
- * - * Using a {@link FailableStream}, this can be rewritten as follows: - * - *
- * Streams.failable(stream).forEach((m) -> m.invoke(o, args));
- * 
- * - * Obviously, the second version is much more concise and the spirit of Lambda - * expressions is met better than in the first version. - * - * @see Stream - * @see Functions - * @since 3.10 - * @deprecated Use {@link org.apache.commons.lang3.stream.Streams}. - */ -@Deprecated -public class Streams { - - /** - * A reduced, and simplified version of a {@link Stream} with failable method - * signatures. - * - * @param The streams element type. - * @deprecated Use - * {@link org.apache.commons.lang3.stream.Streams.FailableStream}. - */ - @Deprecated - public static class FailableStream { - - private Stream stream; - private boolean terminated; - - /** - * Constructs a new instance with the given {@code stream}. - * - * @param stream The stream. - */ - public FailableStream(final Stream stream) { - this.stream = stream; - } - - protected void assertNotTerminated() { - if (terminated) { - throw new IllegalStateException("This stream is already terminated."); - } - } - - protected void makeTerminated() { - assertNotTerminated(); - terminated = true; - } - - /** - * Returns a FailableStream consisting of the elements of this stream that match - * the given FailablePredicate. - * - *

- * This is an intermediate operation. - * - * @param predicate a non-interfering, stateless predicate to apply to each - * element to determine if it should be included. - * @return the new stream - */ - public FailableStream filter(final FailablePredicate predicate) { - assertNotTerminated(); - stream = stream.filter(Functions.asPredicate(predicate)); - return this; - } - - /** - * Performs an action for each element of this stream. - * - *

- * This is a terminal operation. - * - *

- * The behavior of this operation is explicitly nondeterministic. For parallel - * stream pipelines, this operation does not guarantee to respect the - * encounter order of the stream, as doing so would sacrifice the benefit of - * parallelism. For any given element, the action may be performed at whatever - * time and in whatever thread the library chooses. If the action accesses - * shared state, it is responsible for providing the required synchronization. - * - * @param action a non-interfering action to perform on the elements - */ - public void forEach(final FailableConsumer action) { - makeTerminated(); - stream().forEach(Functions.asConsumer(action)); - } - - /** - * Performs a mutable reduction operation on the elements of this stream using a - * {@code Collector}. A {@code Collector} encapsulates the functions used as - * arguments to {@link #collect(Supplier, BiConsumer, BiConsumer)}, allowing for - * reuse of collection strategies and composition of collect operations such as - * multiple-level grouping or partitioning. - * - *

- * If the underlying stream is parallel, and the {@code Collector} is - * concurrent, and either the stream is unordered or the collector is unordered, - * then a concurrent reduction will be performed (see {@link Collector} for - * details on concurrent reduction.) - * - *

- * This is a terminal operation. - * - *

- * When executed in parallel, multiple intermediate results may be instantiated, - * populated, and merged so as to maintain isolation of mutable data structures. - * Therefore, even when executed in parallel with non-thread-safe data - * structures (such as {@code ArrayList}), no additional synchronization is - * needed for a parallel reduction. - * - * Note The following will accumulate strings into an ArrayList: - * - *

-		 * {
-		 * 	@code
-		 * 	List asList = stringStream.collect(Collectors.toList());
-		 * }
-		 * 
- * - *

- * The following will classify {@code Person} objects by city: - * - *

-		 * {
-		 * 	@code
-		 * 	Map> peopleByCity = personStream.collect(Collectors.groupingBy(Person::getCity));
-		 * }
-		 * 
- * - *

- * The following will classify {@code Person} objects by state and city, - * cascading two {@code Collector}s together: - * - *

-		 * {
-		 * 	@code
-		 * 	Map>> peopleByStateAndCity = personStream
-		 * 			.collect(Collectors.groupingBy(Person::getState, Collectors.groupingBy(Person::getCity)));
-		 * }
-		 * 
- * - * @param the type of the result - * @param the intermediate accumulation type of the {@code Collector} - * @param collector the {@code Collector} describing the reduction - * @return the result of the reduction - * @see #collect(Supplier, BiConsumer, BiConsumer) - * @see Collectors - */ - public R collect(final Collector collector) { - makeTerminated(); - return stream().collect(collector); - } - - /** - * Performs a mutable reduction operation on the elements of this - * FailableStream. A mutable reduction is one in which the reduced value is a - * mutable result container, such as an {@code ArrayList}, and elements are - * incorporated by updating the state of the result rather than by replacing the - * result. This produces a result equivalent to: - * - *
-		 * {@code
-		 *     R result = supplier.get();
-		 *     for (T element : this stream)
-		 *         accumulator.accept(result, element);
-		 *     return result;
-		 * }
-		 * 
- * - *

- * Like {@link #reduce(Object, BinaryOperator)}, {@code collect} operations can - * be parallelized without requiring additional synchronization. - * - *

- * This is a terminal operation. - * - * Note There are many existing classes in the JDK whose signatures are - * well-suited for use with method references as arguments to {@code collect()}. - * For example, the following will accumulate strings into an {@code ArrayList}: - * - *

-		 * {
-		 * 	@code
-		 * 	List asList = stringStream.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
-		 * }
-		 * 
- * - *

- * The following will take a stream of strings and concatenates them into a - * single string: - * - *

-		 * {
-		 * 	@code
-		 * 	String concat = stringStream.collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
-		 * 			.toString();
-		 * }
-		 * 
- * - * @param type of the result - * @param
Type of the accumulator. - * @param pupplier a function that creates a new result container. For a - * parallel execution, this function may be called multiple - * times and must return a fresh value each time. - * @param accumulator An associative, non-interfering, stateless function for - * incorporating an additional element into a result - * @param combiner An associative, non-interfering, stateless function for - * combining two values, which must be compatible with the - * accumulator function - * @return The result of the reduction - */ - public R collect(final Supplier pupplier, final BiConsumer accumulator, - final BiConsumer combiner) { - makeTerminated(); - return stream().collect(pupplier, accumulator, combiner); - } - - /** - * Performs a reduction on the elements of this stream, using the provided - * identity value and an associative accumulation function, and returns the - * reduced value. This is equivalent to: - * - *
-		 * {@code
-		 *     T result = identity;
-		 *     for (T element : this stream)
-		 *         result = accumulator.apply(result, element)
-		 *     return result;
-		 * }
-		 * 
- * - * but is not constrained to execute sequentially. - * - *

- * The {@code identity} value must be an identity for the accumulator function. - * This means that for all {@code t}, {@code accumulator.apply(identity, t)} is - * equal to {@code t}. The {@code accumulator} function must be an associative - * function. - * - *

- * This is a terminal operation. - * - * Note Sum, min, max, average, and string concatenation are all special cases - * of reduction. Summing a stream of numbers can be expressed as: - * - *

-		 * {
-		 * 	@code
-		 * 	Integer sum = integers.reduce(0, (a, b) -> a + b);
-		 * }
-		 * 
- * - * or: - * - *
-		 * {
-		 * 	@code
-		 * 	Integer sum = integers.reduce(0, Integer::sum);
-		 * }
-		 * 
- * - *

- * While this may seem a more roundabout way to perform an aggregation compared - * to simply mutating a running total in a loop, reduction operations - * parallelize more gracefully, without needing additional synchronization and - * with greatly reduced risk of data races. - * - * @param identity the identity value for the accumulating function - * @param accumulator an associative, non-interfering, stateless function for - * combining two values - * @return the result of the reduction - */ - public O reduce(final O identity, final BinaryOperator accumulator) { - makeTerminated(); - return stream().reduce(identity, accumulator); - } - - /** - * Returns a stream consisting of the results of applying the given function to - * the elements of this stream. - * - *

- * This is an intermediate operation. - * - * @param The element type of the new stream - * @param mapper A non-interfering, stateless function to apply to each element - * @return the new stream - */ - public FailableStream map(final FailableFunction mapper) { - assertNotTerminated(); - return new FailableStream<>(stream.map(Functions.asFunction(mapper))); - } - - /** - * Converts the FailableStream into an equivalent stream. - * - * @return A stream, which will return the same elements, which this - * FailableStream would return. - */ - public Stream stream() { - return stream; - } - - /** - * Returns whether all elements of this stream match the provided predicate. May - * not evaluate the predicate on all elements if not necessary for determining - * the result. If the stream is empty then {@code true} is returned and the - * predicate is not evaluated. - * - *

- * This is a short-circuiting terminal operation. - * - * Note This method evaluates the universal quantification of the - * predicate over the elements of the stream (for all x P(x)). If the stream is - * empty, the quantification is said to be vacuously satisfied and is - * always {@code true} (regardless of P(x)). - * - * @param predicate A non-interfering, stateless predicate to apply to elements - * of this stream - * @return {@code true} If either all elements of the stream match the provided - * predicate or the stream is empty, otherwise {@code false}. - */ - public boolean allMatch(final FailablePredicate predicate) { - assertNotTerminated(); - return stream().allMatch(Functions.asPredicate(predicate)); - } - - /** - * Returns whether any elements of this stream match the provided predicate. May - * not evaluate the predicate on all elements if not necessary for determining - * the result. If the stream is empty then {@code false} is returned and the - * predicate is not evaluated. - * - *

- * This is a short-circuiting terminal operation. - * - * Note This method evaluates the existential quantification of the - * predicate over the elements of the stream (for some x P(x)). - * - * @param predicate A non-interfering, stateless predicate to apply to elements - * of this stream - * @return {@code true} if any elements of the stream match the provided - * predicate, otherwise {@code false} - */ - public boolean anyMatch(final FailablePredicate predicate) { - assertNotTerminated(); - return stream().anyMatch(Functions.asPredicate(predicate)); - } - } - - /** - * Converts the given {@link Stream stream} into a {@link FailableStream}. This - * is basically a simplified, reduced version of the {@link Stream} class, with - * the same underlying element stream, except that failable objects, like - * {@link FailablePredicate}, {@link FailableFunction}, or - * {@link FailableConsumer} may be applied, instead of {@link Predicate}, - * {@link Function}, or {@link Consumer}. The idea is to rewrite a code snippet - * like this: - * - *

-	 * final List<O> list;
-	 * final Method m;
-	 * final Function<O, String> mapper = (o) -> {
-	 * 	try {
-	 * 		return (String) m.invoke(o);
-	 * 	} catch (Throwable t) {
-	 * 		throw Functions.rethrow(t);
-	 * 	}
-	 * };
-	 * final List<String> strList = list.stream().map(mapper).collect(Collectors.toList());
-	 * 
- * - * as follows: - * - *
-	 * final List<O> list;
-	 * final Method m;
-	 * final List<String> strList = Functions.stream(list.stream()).map((o) -> (String) m.invoke(o))
-	 * 		.collect(Collectors.toList());
-	 * 
- * - * While the second version may not be quite as efficient (because it - * depends on the creation of additional, intermediate objects, of type - * FailableStream), it is much more concise, and readable, and meets the spirit - * of Lambdas better than the first version. - * - * @param The streams element type. - * @param stream The stream, which is being converted. - * @return The {@link FailableStream}, which has been created by converting the - * stream. - */ - public static FailableStream stream(final Stream stream) { - return new FailableStream<>(stream); - } - - /** - * Converts the given {@link Collection} into a {@link FailableStream}. This is - * basically a simplified, reduced version of the {@link Stream} class, with the - * same underlying element stream, except that failable objects, like - * {@link FailablePredicate}, {@link FailableFunction}, or - * {@link FailableConsumer} may be applied, instead of {@link Predicate}, - * {@link Function}, or {@link Consumer}. The idea is to rewrite a code snippet - * like this: - * - *
-	 * final List<O> list;
-	 * final Method m;
-	 * final Function<O, String> mapper = (o) -> {
-	 * 	try {
-	 * 		return (String) m.invoke(o);
-	 * 	} catch (Throwable t) {
-	 * 		throw Functions.rethrow(t);
-	 * 	}
-	 * };
-	 * final List<String> strList = list.stream().map(mapper).collect(Collectors.toList());
-	 * 
- * - * as follows: - * - *
-	 * final List<O> list;
-	 * final Method m;
-	 * final List<String> strList = Functions.stream(list.stream()).map((o) -> (String) m.invoke(o))
-	 * 		.collect(Collectors.toList());
-	 * 
- * - * While the second version may not be quite as efficient (because it - * depends on the creation of additional, intermediate objects, of type - * FailableStream), it is much more concise, and readable, and meets the spirit - * of Lambdas better than the first version. - * - * @param The streams element type. - * @param stream The stream, which is being converted. - * @return The {@link FailableStream}, which has been created by converting the - * stream. - */ - public static FailableStream stream(final Collection stream) { - return stream(stream.stream()); - } - - /** - * A Collector type for arrays. - * - * @param The array type. - * @deprecated Use - * {@link org.apache.commons.lang3.stream.Streams.ArrayCollector}. - */ - @Deprecated - public static class ArrayCollector implements Collector, O[]> { - private static final Set characteristics = Collections.emptySet(); - private final Class elementType; - - /** - * Constructs a new instance for the given element type. - * - * @param elementType The element type. - */ - public ArrayCollector(final Class elementType) { - this.elementType = elementType; - } - - @Override - public Supplier> supplier() { - return ArrayList::new; - } - - @Override - public BiConsumer, O> accumulator() { - return List::add; - } - - @Override - public BinaryOperator> combiner() { - return (left, right) -> { - left.addAll(right); - return left; - }; - } - - @Override - public Function, O[]> finisher() { - return list -> { - @SuppressWarnings("unchecked") - final O[] array = (O[]) Array.newInstance(elementType, list.size()); - return list.toArray(array); - }; - } - - @Override - public Set characteristics() { - return characteristics; - } - } - - /** - * Returns a {@code Collector} that accumulates the input elements into a new - * array. - * - * @param pElementType Type of an element in the array. - * @param the type of the input elements - * @return a {@code Collector} which collects all the input elements into an - * array, in encounter order - */ - public static Collector toArray(final Class pElementType) { - return new ArrayCollector<>(pElementType); - } -} diff --git a/src/main/java/org/apache/commons/lang3/StringEscapeUtils.java b/src/main/java/org/apache/commons/lang3/StringEscapeUtils.java deleted file mode 100755 index dd7799ac..00000000 --- a/src/main/java/org/apache/commons/lang3/StringEscapeUtils.java +++ /dev/null @@ -1,849 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import java.io.IOException; -import java.io.Writer; - -import org.apache.commons.lang3.text.translate.AggregateTranslator; -import org.apache.commons.lang3.text.translate.CharSequenceTranslator; -import org.apache.commons.lang3.text.translate.EntityArrays; -import org.apache.commons.lang3.text.translate.JavaUnicodeEscaper; -import org.apache.commons.lang3.text.translate.LookupTranslator; -import org.apache.commons.lang3.text.translate.NumericEntityEscaper; -import org.apache.commons.lang3.text.translate.NumericEntityUnescaper; -import org.apache.commons.lang3.text.translate.OctalUnescaper; -import org.apache.commons.lang3.text.translate.UnicodeUnescaper; -import org.apache.commons.lang3.text.translate.UnicodeUnpairedSurrogateRemover; - -/** - *

- * Escapes and unescapes {@code String}s for Java, Java Script, HTML and XML. - *

- * - *

- * #ThreadSafe# - *

- * - * @since 2.0 - * @version $Id: StringEscapeUtils.java 1583482 2014-03-31 22:54:57Z niallp $ - */ -public class StringEscapeUtils { - - /* ESCAPE TRANSLATORS */ - - /** - * Translator object for escaping Java. - * - * While {@link #escapeJava(String)} is the expected method of use, this object - * allows the Java escaping functionality to be used as the foundation for a - * custom translator. - * - * @since 3.0 - */ - public static final CharSequenceTranslator ESCAPE_JAVA = new LookupTranslator( - new String[][] { { "\"", "\\\"" }, { "\\", "\\\\" }, }) - .with(new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE())) - .with(JavaUnicodeEscaper.outsideOf(32, 0x7f)); - - /** - * Translator object for escaping EcmaScript/JavaScript. - * - * While {@link #escapeEcmaScript(String)} is the expected method of use, this - * object allows the EcmaScript escaping functionality to be used as the - * foundation for a custom translator. - * - * @since 3.0 - */ - public static final CharSequenceTranslator ESCAPE_ECMASCRIPT = new AggregateTranslator( - new LookupTranslator(new String[][] { { "'", "\\'" }, { "\"", "\\\"" }, { "\\", "\\\\" }, { "/", "\\/" } }), - new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE()), JavaUnicodeEscaper.outsideOf(32, 0x7f)); - - /** - * Translator object for escaping Json. - * - * While {@link #escapeJson(String)} is the expected method of use, this object - * allows the Json escaping functionality to be used as the foundation for a - * custom translator. - * - * @since 3.2 - */ - public static final CharSequenceTranslator ESCAPE_JSON = new AggregateTranslator( - new LookupTranslator(new String[][] { { "\"", "\\\"" }, { "\\", "\\\\" }, { "/", "\\/" } }), - new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE()), JavaUnicodeEscaper.outsideOf(32, 0x7f)); - - /** - * Translator object for escaping XML. - * - * While {@link #escapeXml(String)} is the expected method of use, this object - * allows the XML escaping functionality to be used as the foundation for a - * custom translator. - * - * @since 3.0 - * @deprecated use {@link #ESCAPE_XML10} or {@link #ESCAPE_XML11} instead. - */ - @Deprecated - public static final CharSequenceTranslator ESCAPE_XML = new AggregateTranslator( - new LookupTranslator(EntityArrays.BASIC_ESCAPE()), new LookupTranslator(EntityArrays.APOS_ESCAPE())); - - /** - * Translator object for escaping XML 1.0. - * - * While {@link #escapeXml10(String)} is the expected method of use, this object - * allows the XML escaping functionality to be used as the foundation for a - * custom translator. - * - * @since 3.3 - */ - public static final CharSequenceTranslator ESCAPE_XML10 = new AggregateTranslator( - new LookupTranslator(EntityArrays.BASIC_ESCAPE()), new LookupTranslator(EntityArrays.APOS_ESCAPE()), - new LookupTranslator(new String[][] { { "\u0000", "" }, { "\u0001", "" }, { "\u0002", "" }, - { "\u0003", "" }, { "\u0004", "" }, { "\u0005", "" }, { "\u0006", "" }, { "\u0007", "" }, - { "\u0008", "" }, { "\u000b", "" }, { "\u000c", "" }, { "\u000e", "" }, { "\u000f", "" }, - { "\u0010", "" }, { "\u0011", "" }, { "\u0012", "" }, { "\u0013", "" }, { "\u0014", "" }, - { "\u0015", "" }, { "\u0016", "" }, { "\u0017", "" }, { "\u0018", "" }, { "\u0019", "" }, - { "\u001a", "" }, { "\u001b", "" }, { "\u001c", "" }, { "\u001d", "" }, { "\u001e", "" }, - { "\u001f", "" }, { "\ufffe", "" }, { "\uffff", "" } }), - NumericEntityEscaper.between(0x7f, 0x84), NumericEntityEscaper.between(0x86, 0x9f), - new UnicodeUnpairedSurrogateRemover()); - - /** - * Translator object for escaping XML 1.1. - * - * While {@link #escapeXml11(String)} is the expected method of use, this object - * allows the XML escaping functionality to be used as the foundation for a - * custom translator. - * - * @since 3.3 - */ - public static final CharSequenceTranslator ESCAPE_XML11 = new AggregateTranslator( - new LookupTranslator(EntityArrays.BASIC_ESCAPE()), new LookupTranslator(EntityArrays.APOS_ESCAPE()), - new LookupTranslator(new String[][] { { "\u0000", "" }, { "\u000b", " " }, { "\u000c", " " }, - { "\ufffe", "" }, { "\uffff", "" } }), - NumericEntityEscaper.between(0x1, 0x8), NumericEntityEscaper.between(0xe, 0x1f), - NumericEntityEscaper.between(0x7f, 0x84), NumericEntityEscaper.between(0x86, 0x9f), - new UnicodeUnpairedSurrogateRemover()); - - /** - * Translator object for escaping HTML version 3.0. - * - * While {@link #escapeHtml3(String)} is the expected method of use, this object - * allows the HTML escaping functionality to be used as the foundation for a - * custom translator. - * - * @since 3.0 - */ - public static final CharSequenceTranslator ESCAPE_HTML3 = new AggregateTranslator( - new LookupTranslator(EntityArrays.BASIC_ESCAPE()), new LookupTranslator(EntityArrays.ISO8859_1_ESCAPE())); - - /** - * Translator object for escaping HTML version 4.0. - * - * While {@link #escapeHtml4(String)} is the expected method of use, this object - * allows the HTML escaping functionality to be used as the foundation for a - * custom translator. - * - * @since 3.0 - */ - public static final CharSequenceTranslator ESCAPE_HTML4 = new AggregateTranslator( - new LookupTranslator(EntityArrays.BASIC_ESCAPE()), new LookupTranslator(EntityArrays.ISO8859_1_ESCAPE()), - new LookupTranslator(EntityArrays.HTML40_EXTENDED_ESCAPE())); - - /** - * Translator object for escaping individual Comma Separated Values. - * - * While {@link #escapeCsv(String)} is the expected method of use, this object - * allows the CSV escaping functionality to be used as the foundation for a - * custom translator. - * - * @since 3.0 - */ - public static final CharSequenceTranslator ESCAPE_CSV = new CsvEscaper(); - - // TODO: Create a parent class - 'SinglePassTranslator' ? - // It would handle the index checking + length returning, - // and could also have an optimization check method. - static class CsvEscaper extends CharSequenceTranslator { - - private static final char CSV_DELIMITER = ','; - private static final char CSV_QUOTE = '"'; - private static final String CSV_QUOTE_STR = String.valueOf(CSV_QUOTE); - private static final char[] CSV_SEARCH_CHARS = new char[] { CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, - CharUtils.LF }; - - @Override - public int translate(final CharSequence input, final int index, final Writer out) throws IOException { - - if (index != 0) { - throw new IllegalStateException("CsvEscaper should never reach the [1] index"); - } - - if (StringUtils.containsNone(input.toString(), CSV_SEARCH_CHARS)) { - out.write(input.toString()); - } else { - out.write(CSV_QUOTE); - out.write(StringUtils.replace(input.toString(), CSV_QUOTE_STR, CSV_QUOTE_STR + CSV_QUOTE_STR)); - out.write(CSV_QUOTE); - } - return Character.codePointCount(input, 0, input.length()); - } - } - - /* UNESCAPE TRANSLATORS */ - - /** - * Translator object for unescaping escaped Java. - * - * While {@link #unescapeJava(String)} is the expected method of use, this - * object allows the Java unescaping functionality to be used as the foundation - * for a custom translator. - * - * @since 3.0 - */ - // TODO: throw "illegal character: \92" as an Exception if a \ on the end of the - // Java (as per the compiler)? - public static final CharSequenceTranslator UNESCAPE_JAVA = new AggregateTranslator(new OctalUnescaper(), // .between('\1', - // '\377'), - new UnicodeUnescaper(), new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_UNESCAPE()), - new LookupTranslator(new String[][] { { "\\\\", "\\" }, { "\\\"", "\"" }, { "\\'", "'" }, { "\\", "" } })); - - /** - * Translator object for unescaping escaped EcmaScript. - * - * While {@link #unescapeEcmaScript(String)} is the expected method of use, this - * object allows the EcmaScript unescaping functionality to be used as the - * foundation for a custom translator. - * - * @since 3.0 - */ - public static final CharSequenceTranslator UNESCAPE_ECMASCRIPT = UNESCAPE_JAVA; - - /** - * Translator object for unescaping escaped Json. - * - * While {@link #unescapeJson(String)} is the expected method of use, this - * object allows the Json unescaping functionality to be used as the foundation - * for a custom translator. - * - * @since 3.2 - */ - public static final CharSequenceTranslator UNESCAPE_JSON = UNESCAPE_JAVA; - - /** - * Translator object for unescaping escaped HTML 3.0. - * - * While {@link #unescapeHtml3(String)} is the expected method of use, this - * object allows the HTML unescaping functionality to be used as the foundation - * for a custom translator. - * - * @since 3.0 - */ - public static final CharSequenceTranslator UNESCAPE_HTML3 = new AggregateTranslator( - new LookupTranslator(EntityArrays.BASIC_UNESCAPE()), - new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE()), new NumericEntityUnescaper()); - - /** - * Translator object for unescaping escaped HTML 4.0. - * - * While {@link #unescapeHtml4(String)} is the expected method of use, this - * object allows the HTML unescaping functionality to be used as the foundation - * for a custom translator. - * - * @since 3.0 - */ - public static final CharSequenceTranslator UNESCAPE_HTML4 = new AggregateTranslator( - new LookupTranslator(EntityArrays.BASIC_UNESCAPE()), - new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE()), - new LookupTranslator(EntityArrays.HTML40_EXTENDED_UNESCAPE()), new NumericEntityUnescaper()); - - /** - * Translator object for unescaping escaped XML. - * - * While {@link #unescapeXml(String)} is the expected method of use, this object - * allows the XML unescaping functionality to be used as the foundation for a - * custom translator. - * - * @since 3.0 - */ - public static final CharSequenceTranslator UNESCAPE_XML = new AggregateTranslator( - new LookupTranslator(EntityArrays.BASIC_UNESCAPE()), new LookupTranslator(EntityArrays.APOS_UNESCAPE()), - new NumericEntityUnescaper()); - - /** - * Translator object for unescaping escaped Comma Separated Value entries. - * - * While {@link #unescapeCsv(String)} is the expected method of use, this object - * allows the CSV unescaping functionality to be used as the foundation for a - * custom translator. - * - * @since 3.0 - */ - public static final CharSequenceTranslator UNESCAPE_CSV = new CsvUnescaper(); - - static class CsvUnescaper extends CharSequenceTranslator { - - private static final char CSV_DELIMITER = ','; - private static final char CSV_QUOTE = '"'; - private static final String CSV_QUOTE_STR = String.valueOf(CSV_QUOTE); - private static final char[] CSV_SEARCH_CHARS = new char[] { CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, - CharUtils.LF }; - - @Override - public int translate(final CharSequence input, final int index, final Writer out) throws IOException { - - if (index != 0) { - throw new IllegalStateException("CsvUnescaper should never reach the [1] index"); - } - - if (input.charAt(0) != CSV_QUOTE || input.charAt(input.length() - 1) != CSV_QUOTE) { - out.write(input.toString()); - return Character.codePointCount(input, 0, input.length()); - } - - // strip quotes - final String quoteless = input.subSequence(1, input.length() - 1).toString(); - - if (StringUtils.containsAny(quoteless, CSV_SEARCH_CHARS)) { - // deal with escaped quotes; ie) "" - out.write(StringUtils.replace(quoteless, CSV_QUOTE_STR + CSV_QUOTE_STR, CSV_QUOTE_STR)); - } else { - out.write(input.toString()); - } - return Character.codePointCount(input, 0, input.length()); - } - } - - /* Helper functions */ - - /** - *

- * {@code StringEscapeUtils} instances should NOT be constructed in standard - * programming. - *

- * - *

- * Instead, the class should be used as: - *

- * - *
-	 * StringEscapeUtils.escapeJava("foo");
-	 * 
- * - *

- * This constructor is public to permit tools that require a JavaBean instance - * to operate. - *

- */ - public StringEscapeUtils() { - super(); - } - - // Java and JavaScript - // -------------------------------------------------------------------------- - /** - *

- * Escapes the characters in a {@code String} using Java String rules. - *

- * - *

- * Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) - *

- * - *

- * So a tab becomes the characters {@code '\\'} and {@code 't'}. - *

- * - *

- * The only difference between Java strings and JavaScript strings is that in - * JavaScript, a single quote and forward-slash (/) are escaped. - *

- * - *

- * Example: - *

- * - *
-	 * input string: He didn't say, "Stop!"
-	 * output string: He didn't say, \"Stop!\"
-	 * 
- * - * @param input String to escape values in, may be null - * @return String with escaped values, {@code null} if null string input - */ - public static final String escapeJava(final String input) { - return ESCAPE_JAVA.translate(input); - } - - /** - *

- * Escapes the characters in a {@code String} using EcmaScript String rules. - *

- *

- * Escapes any values it finds into their EcmaScript String form. Deals - * correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) - *

- * - *

- * So a tab becomes the characters {@code '\\'} and {@code 't'}. - *

- * - *

- * The only difference between Java strings and EcmaScript strings is that in - * EcmaScript, a single quote and forward-slash (/) are escaped. - *

- * - *

- * Note that EcmaScript is best known by the JavaScript and ActionScript - * dialects. - *

- * - *

- * Example: - *

- * - *
-	 * input string: He didn't say, "Stop!"
-	 * output string: He didn\'t say, \"Stop!\"
-	 * 
- * - * @param input String to escape values in, may be null - * @return String with escaped values, {@code null} if null string input - * - * @since 3.0 - */ - public static final String escapeEcmaScript(final String input) { - return ESCAPE_ECMASCRIPT.translate(input); - } - - /** - *

- * Escapes the characters in a {@code String} using Json String rules. - *

- *

- * Escapes any values it finds into their Json String form. Deals correctly with - * quotes and control-chars (tab, backslash, cr, ff, etc.) - *

- * - *

- * So a tab becomes the characters {@code '\\'} and {@code 't'}. - *

- * - *

- * The only difference between Java strings and Json strings is that in Json, - * forward-slash (/) is escaped. - *

- * - *

- * See http://www.ietf.org/rfc/rfc4627.txt for further details. - *

- * - *

- * Example: - *

- * - *
-	 * input string: He didn't say, "Stop!"
-	 * output string: He didn't say, \"Stop!\"
-	 * 
- * - * @param input String to escape values in, may be null - * @return String with escaped values, {@code null} if null string input - * - * @since 3.2 - */ - public static final String escapeJson(final String input) { - return ESCAPE_JSON.translate(input); - } - - /** - *

- * Unescapes any Java literals found in the {@code String}. For example, it will - * turn a sequence of {@code '\'} and {@code 'n'} into a newline character, - * unless the {@code '\'} is preceded by another {@code '\'}. - *

- * - * @param input the {@code String} to unescape, may be null - * @return a new unescaped {@code String}, {@code null} if null string input - */ - public static final String unescapeJava(final String input) { - return UNESCAPE_JAVA.translate(input); - } - - /** - *

- * Unescapes any EcmaScript literals found in the {@code String}. - *

- * - *

- * For example, it will turn a sequence of {@code '\'} and {@code 'n'} into a - * newline character, unless the {@code '\'} is preceded by another {@code '\'}. - *

- * - * @see #unescapeJava(String) - * @param input the {@code String} to unescape, may be null - * @return A new unescaped {@code String}, {@code null} if null string input - * - * @since 3.0 - */ - public static final String unescapeEcmaScript(final String input) { - return UNESCAPE_ECMASCRIPT.translate(input); - } - - /** - *

- * Unescapes any Json literals found in the {@code String}. - *

- * - *

- * For example, it will turn a sequence of {@code '\'} and {@code 'n'} into a - * newline character, unless the {@code '\'} is preceded by another {@code '\'}. - *

- * - * @see #unescapeJava(String) - * @param input the {@code String} to unescape, may be null - * @return A new unescaped {@code String}, {@code null} if null string input - * - * @since 3.2 - */ - public static final String unescapeJson(final String input) { - return UNESCAPE_JSON.translate(input); - } - - // HTML and XML - // -------------------------------------------------------------------------- - /** - *

- * Escapes the characters in a {@code String} using HTML entities. - *

- * - *

- * For example: - *

- *

- * "bread" & "butter" - *

- * becomes: - *

- * &quot;bread&quot; &amp; &quot;butter&quot;. - *

- * - *

- * Supports all known HTML 4.0 entities, including funky accents. Note that the - * commonly used apostrophe escape character (&apos;) is not a legal entity - * and so is not supported). - *

- * - * @param input the {@code String} to escape, may be null - * @return a new escaped {@code String}, {@code null} if null string input - * - * @see
ISO - * Entities - * @see HTML 3.2 Character - * Entities for ISO Latin-1 - * @see HTML 4.0 - * Character entity references - * @see HTML 4.01 - * Character References - * @see HTML - * 4.01 Code positions - * - * @since 3.0 - */ - public static final String escapeHtml4(final String input) { - return ESCAPE_HTML4.translate(input); - } - - /** - *

- * Escapes the characters in a {@code String} using HTML entities. - *

- *

- * Supports only the HTML 3.0 entities. - *

- * - * @param input the {@code String} to escape, may be null - * @return a new escaped {@code String}, {@code null} if null string input - * - * @since 3.0 - */ - public static final String escapeHtml3(final String input) { - return ESCAPE_HTML3.translate(input); - } - - // ----------------------------------------------------------------------- - /** - *

- * Unescapes a string containing entity escapes to a string containing the - * actual Unicode characters corresponding to the escapes. Supports HTML 4.0 - * entities. - *

- * - *

- * For example, the string "&lt;Fran&ccedil;ais&gt;" will become - * "<Français>" - *

- * - *

- * If an entity is unrecognized, it is left alone, and inserted verbatim into - * the result string. e.g. "&gt;&zzzz;x" will become ">&zzzz;x". - *

- * - * @param input the {@code String} to unescape, may be null - * @return a new unescaped {@code String}, {@code null} if null string input - * - * @since 3.0 - */ - public static final String unescapeHtml4(final String input) { - return UNESCAPE_HTML4.translate(input); - } - - /** - *

- * Unescapes a string containing entity escapes to a string containing the - * actual Unicode characters corresponding to the escapes. Supports only HTML - * 3.0 entities. - *

- * - * @param input the {@code String} to unescape, may be null - * @return a new unescaped {@code String}, {@code null} if null string input - * - * @since 3.0 - */ - public static final String unescapeHtml3(final String input) { - return UNESCAPE_HTML3.translate(input); - } - - // ----------------------------------------------------------------------- - /** - *

- * Escapes the characters in a {@code String} using XML entities. - *

- * - *

- * For example: "bread" & "butter" => - * &quot;bread&quot; &amp; &quot;butter&quot;. - *

- * - *

- * Supports only the five basic XML entities (gt, lt, quot, amp, apos). Does not - * support DTDs or external entities. - *

- * - *

- * Note that Unicode characters greater than 0x7f are as of 3.0, no longer - * escaped. If you still wish this functionality, you can achieve it via the - * following: - * {@code StringEscapeUtils.ESCAPE_XML.with( NumericEntityEscaper.between(0x7f, Integer.MAX_VALUE) );} - *

- * - * @param input the {@code String} to escape, may be null - * @return a new escaped {@code String}, {@code null} if null string input - * @see #unescapeXml(java.lang.String) - * @deprecated use {@link #escapeXml10(java.lang.String)} or - * {@link #escapeXml11(java.lang.String)} instead. - */ - @Deprecated - public static final String escapeXml(final String input) { - return ESCAPE_XML.translate(input); - } - - /** - *

- * Escapes the characters in a {@code String} using XML entities. - *

- * - *

- * For example: "bread" & "butter" => - * &quot;bread&quot; &amp; &quot;butter&quot;. - *

- * - *

- * Note that XML 1.0 is a text-only format: it cannot represent control - * characters or unpaired Unicode surrogate codepoints, even after escaping. - * {@code escapeXml10} will remove characters that do not fit in the following - * ranges: - *

- * - *

- * {@code #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]} - *

- * - *

- * Though not strictly necessary, {@code escapeXml10} will escape characters in - * the following ranges: - *

- * - *

- * {@code [#x7F-#x84] | [#x86-#x9F]} - *

- * - *

- * The returned string can be inserted into a valid XML 1.0 or XML 1.1 document. - * If you want to allow more non-text characters in an XML 1.1 document, use - * {@link #escapeXml11(String)}. - *

- * - * @param input the {@code String} to escape, may be null - * @return a new escaped {@code String}, {@code null} if null string input - * @see #unescapeXml(java.lang.String) - * @since 3.3 - */ - public static String escapeXml10(final String input) { - return ESCAPE_XML10.translate(input); - } - - /** - *

- * Escapes the characters in a {@code String} using XML entities. - *

- * - *

- * For example: "bread" & "butter" => - * &quot;bread&quot; &amp; &quot;butter&quot;. - *

- * - *

- * XML 1.1 can represent certain control characters, but it cannot represent the - * null byte or unpaired Unicode surrogate codepoints, even after escaping. - * {@code escapeXml11} will remove characters that do not fit in the following - * ranges: - *

- * - *

- * {@code [#x1-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]} - *

- * - *

- * {@code escapeXml11} will escape characters in the following ranges: - *

- * - *

- * {@code [#x1-#x8] | [#xB-#xC] | [#xE-#x1F] | [#x7F-#x84] | [#x86-#x9F]} - *

- * - *

- * The returned string can be inserted into a valid XML 1.1 document. Do not use - * it for XML 1.0 documents. - *

- * - * @param input the {@code String} to escape, may be null - * @return a new escaped {@code String}, {@code null} if null string input - * @see #unescapeXml(java.lang.String) - * @since 3.3 - */ - public static String escapeXml11(final String input) { - return ESCAPE_XML11.translate(input); - } - - // ----------------------------------------------------------------------- - /** - *

- * Unescapes a string containing XML entity escapes to a string containing the - * actual Unicode characters corresponding to the escapes. - *

- * - *

- * Supports only the five basic XML entities (gt, lt, quot, amp, apos). Does not - * support DTDs or external entities. - *

- * - *

- * Note that numerical \\u Unicode codes are unescaped to their respective - * Unicode characters. This may change in future releases. - *

- * - * @param input the {@code String} to unescape, may be null - * @return a new unescaped {@code String}, {@code null} if null string input - * @see #escapeXml(String) - * @see #escapeXml10(String) - * @see #escapeXml11(String) - */ - public static final String unescapeXml(final String input) { - return UNESCAPE_XML.translate(input); - } - - // ----------------------------------------------------------------------- - - /** - *

- * Returns a {@code String} value for a CSV column enclosed in double quotes, if - * required. - *

- * - *

- * If the value contains a comma, newline or double quote, then the String value - * is returned enclosed in double quotes. - *

- * - *

- * Any double quote characters in the value are escaped with another double - * quote. - *

- * - *

- * If the value does not contain a comma, newline or double quote, then the - * String value is returned unchanged. - *

- * - * see - * Wikipedia - * and RFC 4180. - * - * @param input the input CSV column String, may be null - * @return the input String, enclosed in double quotes if the value contains a - * comma, newline or double quote, {@code null} if null string input - * @since 2.4 - */ - public static final String escapeCsv(final String input) { - return ESCAPE_CSV.translate(input); - } - - /** - *

- * Returns a {@code String} value for an unescaped CSV column. - *

- * - *

- * If the value is enclosed in double quotes, and contains a comma, newline or - * double quote, then quotes are removed. - *

- * - *

- * Any double quote escaped characters (a pair of double quotes) are unescaped - * to just one double quote. - *

- * - *

- * If the value is not enclosed in double quotes, or is and does not contain a - * comma, newline or double quote, then the String value is returned unchanged. - *

- * - * see - * Wikipedia - * and RFC 4180. - * - * @param input the input CSV column String, may be null - * @return the input String, with enclosing double quotes removed and embedded - * double quotes unescaped, {@code null} if null string input - * @since 2.4 - */ - public static final String unescapeCsv(final String input) { - return UNESCAPE_CSV.translate(input); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/StringUtils.java b/src/main/java/org/apache/commons/lang3/StringUtils.java deleted file mode 100755 index 078902e6..00000000 --- a/src/main/java/org/apache/commons/lang3/StringUtils.java +++ /dev/null @@ -1,10513 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import java.io.UnsupportedEncodingException; -import java.nio.charset.Charset; -import java.text.Normalizer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Objects; -import java.util.Set; -import java.util.StringJoiner; -import java.util.function.Supplier; -import java.util.regex.Pattern; - -import net.lax1dude.eaglercraft.v1_8.HString; - -import org.apache.commons.lang3.function.ToBooleanBiFunction; - -/** - *

- * Operations on {@link java.lang.String} that are {@code null} safe. - *

- * - *
    - *
  • IsEmpty/IsBlank - checks if a String contains text
  • - *
  • Trim/Strip - removes leading and trailing whitespace
  • - *
  • Equals/Compare - compares two strings in a null-safe manner
  • - *
  • startsWith - check if a String starts with a prefix in a null-safe - * manner
  • - *
  • endsWith - check if a String ends with a suffix in a null-safe - * manner
  • - *
  • IndexOf/LastIndexOf/Contains - null-safe index-of checks - *
  • IndexOfAny/LastIndexOfAny/IndexOfAnyBut/LastIndexOfAnyBut - - * index-of any of a set of Strings
  • - *
  • ContainsOnly/ContainsNone/ContainsAny - checks if String contains - * only/none/any of these characters
  • - *
  • Substring/Left/Right/Mid - null-safe substring extractions
  • - *
  • SubstringBefore/SubstringAfter/SubstringBetween - substring - * extraction relative to other strings
  • - *
  • Split/Join - splits a String into an array of substrings and vice - * versa
  • - *
  • Remove/Delete - removes part of a String
  • - *
  • Replace/Overlay - Searches a String and replaces one String with - * another
  • - *
  • Chomp/Chop - removes the last part of a String
  • - *
  • AppendIfMissing - appends a suffix to the end of the String if not - * present
  • - *
  • PrependIfMissing - prepends a prefix to the start of the String if - * not present
  • - *
  • LeftPad/RightPad/Center/Repeat - pads a String
  • - *
  • UpperCase/LowerCase/SwapCase/Capitalize/Uncapitalize - changes the - * case of a String
  • - *
  • CountMatches - counts the number of occurrences of one String in - * another
  • - *
  • IsAlpha/IsNumeric/IsWhitespace/IsAsciiPrintable - checks the - * characters in a String
  • - *
  • DefaultString - protects against a null input String
  • - *
  • Rotate - rotate (circular shift) a String
  • - *
  • Reverse/ReverseDelimited - reverses a String
  • - *
  • Abbreviate - abbreviates a string using ellipses or another given - * String
  • - *
  • Difference - compares Strings and reports on their - * differences
  • - *
  • LevenshteinDistance - the number of changes needed to change one - * String into another
  • - *
- * - *

- * The {@code StringUtils} class defines certain words related to String - * handling. - *

- * - *
    - *
  • null - {@code null}
  • - *
  • empty - a zero-length string ({@code ""})
  • - *
  • space - the space character ({@code ' '}, char 32)
  • - *
  • whitespace - the characters defined by - * {@link Character#isWhitespace(char)}
  • - *
  • trim - the characters <= 32 as in {@link String#trim()}
  • - *
- * - *

- * {@code StringUtils} handles {@code null} input Strings quietly. That is to - * say that a {@code null} input will return {@code null}. Where a - * {@code boolean} or {@code int} is being returned details vary by method. - *

- * - *

- * A side effect of the {@code null} handling is that a - * {@code NullPointerException} should be considered a bug in - * {@code StringUtils}. - *

- * - *

- * Methods in this class include sample code in their Javadoc comments to - * explain their operation. The symbol {@code *} is used to indicate any input - * including {@code null}. - *

- * - *

- * #ThreadSafe# - *

- * - * @see java.lang.String - * @since 1.0 - */ -//@Immutable -public class StringUtils { - - private static final int STRING_BUILDER_SIZE = 256; - - // Performance testing notes (JDK 1.4, Jul03, scolebourne) - // Whitespace: - // Character.isWhitespace() is faster than WHITESPACE.indexOf() - // where WHITESPACE is a string of all whitespace characters - // - // Character access: - // String.charAt(n) versus toCharArray(), then array[n] - // String.charAt(n) is about 15% worse for a 10K string - // They are about equal for a length 50 string - // String.charAt(n) is about 4 times better for a length 3 string - // String.charAt(n) is best bet overall - // - // Append: - // String.concat about twice as fast as StringBuffer.append - // (not sure who tested this) - - /** - * A String for a space character. - * - * @since 3.2 - */ - public static final String SPACE = " "; - - /** - * The empty String {@code ""}. - * - * @since 2.0 - */ - public static final String EMPTY = ""; - - /** - * A String for linefeed LF ("\n"). - * - * @see JLF: - * Escape Sequences for Character and String Literals - * @since 3.2 - */ - public static final String LF = "\n"; - - /** - * A String for carriage return CR ("\r"). - * - * @see JLF: - * Escape Sequences for Character and String Literals - * @since 3.2 - */ - public static final String CR = "\r"; - - /** - * Represents a failed index search. - * - * @since 2.1 - */ - public static final int INDEX_NOT_FOUND = -1; - - /** - *

- * The maximum size to which the padding constant(s) can expand. - *

- */ - private static final int PAD_LIMIT = 8192; - - /** - * Pattern used in {@link #stripAccents(String)}. - */ - private static final Pattern STRIP_ACCENTS_PATTERN = Pattern.compile("\\p{InCombiningDiacriticalMarks}+"); //$NON-NLS-1$ - - /** - *

- * Abbreviates a String using ellipses. This will turn "Now is the time for all - * good men" into "Now is the time for..." - *

- * - *

- * Specifically: - *

- *
    - *
  • If the number of characters in {@code str} is less than or equal to - * {@code maxWidth}, return {@code str}.
  • - *
  • Else abbreviate it to {@code (substring(str, 0, max-3) + "...")}.
  • - *
  • If {@code maxWidth} is less than {@code 4}, throw an - * {@code IllegalArgumentException}.
  • - *
  • In no case will it return a String of length greater than - * {@code maxWidth}.
  • - *
- * - *
-	 * StringUtils.abbreviate(null, *)      = null
-	 * StringUtils.abbreviate("", 4)        = ""
-	 * StringUtils.abbreviate("abcdefg", 6) = "abc..."
-	 * StringUtils.abbreviate("abcdefg", 7) = "abcdefg"
-	 * StringUtils.abbreviate("abcdefg", 8) = "abcdefg"
-	 * StringUtils.abbreviate("abcdefg", 4) = "a..."
-	 * StringUtils.abbreviate("abcdefg", 3) = IllegalArgumentException
-	 * 
- * - * @param str the String to check, may be null - * @param maxWidth maximum length of result String, must be at least 4 - * @return abbreviated String, {@code null} if null String input - * @throws IllegalArgumentException if the width is too small - * @since 2.0 - */ - public static String abbreviate(final String str, final int maxWidth) { - return abbreviate(str, "...", 0, maxWidth); - } - - /** - *

- * Abbreviates a String using ellipses. This will turn "Now is the time for all - * good men" into "...is the time for..." - *

- * - *

- * Works like {@code abbreviate(String, int)}, but allows you to specify a "left - * edge" offset. Note that this left edge is not necessarily going to be the - * leftmost character in the result, or the first character following the - * ellipses, but it will appear somewhere in the result. - * - *

- * In no case will it return a String of length greater than {@code maxWidth}. - *

- * - *
-	 * StringUtils.abbreviate(null, *, *)                = null
-	 * StringUtils.abbreviate("", 0, 4)                  = ""
-	 * StringUtils.abbreviate("abcdefghijklmno", -1, 10) = "abcdefg..."
-	 * StringUtils.abbreviate("abcdefghijklmno", 0, 10)  = "abcdefg..."
-	 * StringUtils.abbreviate("abcdefghijklmno", 1, 10)  = "abcdefg..."
-	 * StringUtils.abbreviate("abcdefghijklmno", 4, 10)  = "abcdefg..."
-	 * StringUtils.abbreviate("abcdefghijklmno", 5, 10)  = "...fghi..."
-	 * StringUtils.abbreviate("abcdefghijklmno", 6, 10)  = "...ghij..."
-	 * StringUtils.abbreviate("abcdefghijklmno", 8, 10)  = "...ijklmno"
-	 * StringUtils.abbreviate("abcdefghijklmno", 10, 10) = "...ijklmno"
-	 * StringUtils.abbreviate("abcdefghijklmno", 12, 10) = "...ijklmno"
-	 * StringUtils.abbreviate("abcdefghij", 0, 3)        = IllegalArgumentException
-	 * StringUtils.abbreviate("abcdefghij", 5, 6)        = IllegalArgumentException
-	 * 
- * - * @param str the String to check, may be null - * @param offset left edge of source String - * @param maxWidth maximum length of result String, must be at least 4 - * @return abbreviated String, {@code null} if null String input - * @throws IllegalArgumentException if the width is too small - * @since 2.0 - */ - public static String abbreviate(final String str, final int offset, final int maxWidth) { - return abbreviate(str, "...", offset, maxWidth); - } - - /** - *

- * Abbreviates a String using another given String as replacement marker. This - * will turn "Now is the time for all good men" into "Now is the time for..." if - * "..." was defined as the replacement marker. - *

- * - *

- * Specifically: - *

- *
    - *
  • If the number of characters in {@code str} is less than or equal to - * {@code maxWidth}, return {@code str}.
  • - *
  • Else abbreviate it to - * {@code (substring(str, 0, max-abbrevMarker.length) + abbrevMarker)}.
  • - *
  • If {@code maxWidth} is less than {@code abbrevMarker.length + 1}, throw - * an {@code IllegalArgumentException}.
  • - *
  • In no case will it return a String of length greater than - * {@code maxWidth}.
  • - *
- * - *
-	 * StringUtils.abbreviate(null, "...", *)      = null
-	 * StringUtils.abbreviate("abcdefg", null, *)  = "abcdefg"
-	 * StringUtils.abbreviate("", "...", 4)        = ""
-	 * StringUtils.abbreviate("abcdefg", ".", 5)   = "abcd."
-	 * StringUtils.abbreviate("abcdefg", ".", 7)   = "abcdefg"
-	 * StringUtils.abbreviate("abcdefg", ".", 8)   = "abcdefg"
-	 * StringUtils.abbreviate("abcdefg", "..", 4)  = "ab.."
-	 * StringUtils.abbreviate("abcdefg", "..", 3)  = "a.."
-	 * StringUtils.abbreviate("abcdefg", "..", 2)  = IllegalArgumentException
-	 * StringUtils.abbreviate("abcdefg", "...", 3) = IllegalArgumentException
-	 * 
- * - * @param str the String to check, may be null - * @param abbrevMarker the String used as replacement marker - * @param maxWidth maximum length of result String, must be at least - * {@code abbrevMarker.length + 1} - * @return abbreviated String, {@code null} if null String input - * @throws IllegalArgumentException if the width is too small - * @since 3.6 - */ - public static String abbreviate(final String str, final String abbrevMarker, final int maxWidth) { - return abbreviate(str, abbrevMarker, 0, maxWidth); - } - - /** - *

- * Abbreviates a String using a given replacement marker. This will turn "Now is - * the time for all good men" into "...is the time for..." if "..." was defined - * as the replacement marker. - *

- * - *

- * Works like {@code abbreviate(String, String, int)}, but allows you to specify - * a "left edge" offset. Note that this left edge is not necessarily going to be - * the leftmost character in the result, or the first character following the - * replacement marker, but it will appear somewhere in the result. - * - *

- * In no case will it return a String of length greater than {@code maxWidth}. - *

- * - *
-	 * StringUtils.abbreviate(null, null, *, *)                 = null
-	 * StringUtils.abbreviate("abcdefghijklmno", null, *, *)    = "abcdefghijklmno"
-	 * StringUtils.abbreviate("", "...", 0, 4)                  = ""
-	 * StringUtils.abbreviate("abcdefghijklmno", "---", -1, 10) = "abcdefg---"
-	 * StringUtils.abbreviate("abcdefghijklmno", ",", 0, 10)    = "abcdefghi,"
-	 * StringUtils.abbreviate("abcdefghijklmno", ",", 1, 10)    = "abcdefghi,"
-	 * StringUtils.abbreviate("abcdefghijklmno", ",", 2, 10)    = "abcdefghi,"
-	 * StringUtils.abbreviate("abcdefghijklmno", "::", 4, 10)   = "::efghij::"
-	 * StringUtils.abbreviate("abcdefghijklmno", "...", 6, 10)  = "...ghij..."
-	 * StringUtils.abbreviate("abcdefghijklmno", "*", 9, 10)    = "*ghijklmno"
-	 * StringUtils.abbreviate("abcdefghijklmno", "'", 10, 10)   = "'ghijklmno"
-	 * StringUtils.abbreviate("abcdefghijklmno", "!", 12, 10)   = "!ghijklmno"
-	 * StringUtils.abbreviate("abcdefghij", "abra", 0, 4)       = IllegalArgumentException
-	 * StringUtils.abbreviate("abcdefghij", "...", 5, 6)        = IllegalArgumentException
-	 * 
- * - * @param str the String to check, may be null - * @param abbrevMarker the String used as replacement marker - * @param offset left edge of source String - * @param maxWidth maximum length of result String, must be at least 4 - * @return abbreviated String, {@code null} if null String input - * @throws IllegalArgumentException if the width is too small - * @since 3.6 - */ - public static String abbreviate(final String str, final String abbrevMarker, int offset, final int maxWidth) { - if (isNotEmpty(str) && EMPTY.equals(abbrevMarker) && maxWidth > 0) { - return substring(str, 0, maxWidth); - } else if (isAnyEmpty(str, abbrevMarker)) { - return str; - } - final int abbrevMarkerLength = abbrevMarker.length(); - final int minAbbrevWidth = abbrevMarkerLength + 1; - final int minAbbrevWidthOffset = abbrevMarkerLength + abbrevMarkerLength + 1; - - if (maxWidth < minAbbrevWidth) { - throw new IllegalArgumentException(HString.format("Minimum abbreviation width is %d", minAbbrevWidth)); - } - final int strLen = str.length(); - if (strLen <= maxWidth) { - return str; - } - if (offset > strLen) { - offset = strLen; - } - if (strLen - offset < maxWidth - abbrevMarkerLength) { - offset = strLen - (maxWidth - abbrevMarkerLength); - } - if (offset <= abbrevMarkerLength + 1) { - return str.substring(0, maxWidth - abbrevMarkerLength) + abbrevMarker; - } - if (maxWidth < minAbbrevWidthOffset) { - throw new IllegalArgumentException( - HString.format("Minimum abbreviation width with offset is %d", minAbbrevWidthOffset)); - } - if (offset + maxWidth - abbrevMarkerLength < strLen) { - return abbrevMarker + abbreviate(str.substring(offset), abbrevMarker, maxWidth - abbrevMarkerLength); - } - return abbrevMarker + str.substring(strLen - (maxWidth - abbrevMarkerLength)); - } - - /** - *

- * Abbreviates a String to the length passed, replacing the middle characters - * with the supplied replacement String. - *

- * - *

- * This abbreviation only occurs if the following criteria is met: - *

- *
    - *
  • Neither the String for abbreviation nor the replacement String are null - * or empty
  • - *
  • The length to truncate to is less than the length of the supplied - * String
  • - *
  • The length to truncate to is greater than 0
  • - *
  • The abbreviated String will have enough room for the length supplied - * replacement String and the first and last characters of the supplied String - * for abbreviation
  • - *
- *

- * Otherwise, the returned String will be the same as the supplied String for - * abbreviation. - *

- * - *
-	 * StringUtils.abbreviateMiddle(null, null, 0)      = null
-	 * StringUtils.abbreviateMiddle("abc", null, 0)      = "abc"
-	 * StringUtils.abbreviateMiddle("abc", ".", 0)      = "abc"
-	 * StringUtils.abbreviateMiddle("abc", ".", 3)      = "abc"
-	 * StringUtils.abbreviateMiddle("abcdef", ".", 4)     = "ab.f"
-	 * 
- * - * @param str the String to abbreviate, may be null - * @param middle the String to replace the middle characters with, may be null - * @param length the length to abbreviate {@code str} to. - * @return the abbreviated String if the above criteria is met, or the original - * String supplied for abbreviation. - * @since 2.5 - */ - public static String abbreviateMiddle(final String str, final String middle, final int length) { - if (isAnyEmpty(str, middle) || length >= str.length() || length < middle.length() + 2) { - return str; - } - - final int targetSting = length - middle.length(); - final int startOffset = targetSting / 2 + targetSting % 2; - final int endOffset = str.length() - targetSting / 2; - - return str.substring(0, startOffset) + middle + str.substring(endOffset); - } - - /** - *

- * Capitalizes a String changing the first character to title case as per - * {@link Character#toTitleCase(int)}. No other characters are changed. - *

- * - *

- * For a word based algorithm, see - * {@link org.apache.commons.lang3.text.WordUtils#capitalize(String)}. A - * {@code null} input String returns {@code null}. - *

- * - *
-	 * StringUtils.capitalize(null)  = null
-	 * StringUtils.capitalize("")    = ""
-	 * StringUtils.capitalize("cat") = "Cat"
-	 * StringUtils.capitalize("cAt") = "CAt"
-	 * StringUtils.capitalize("'cat'") = "'cat'"
-	 * 
- * - * @param str the String to capitalize, may be null - * @return the capitalized String, {@code null} if null String input - * @see org.apache.commons.lang3.text.WordUtils#capitalize(String) - * @see #uncapitalize(String) - * @since 2.0 - */ - public static String capitalize(final String str) { - final int strLen = length(str); - if (strLen == 0) { - return str; - } - - final int firstCodepoint = str.codePointAt(0); - final int newCodePoint = Character.toTitleCase(firstCodepoint); - if (firstCodepoint == newCodePoint) { - // already capitalized - return str; - } - - final int[] newCodePoints = new int[strLen]; // cannot be longer than the char array - int outOffset = 0; - newCodePoints[outOffset++] = newCodePoint; // copy the first codepoint - for (int inOffset = Character.charCount(firstCodepoint); inOffset < strLen;) { - final int codepoint = str.codePointAt(inOffset); - newCodePoints[outOffset++] = codepoint; // copy the remaining ones - inOffset += Character.charCount(codepoint); - } - return new String(newCodePoints, 0, outOffset); - } - - /** - *

- * Centers a String in a larger String of size {@code size} using the space - * character (' '). - *

- * - *

- * If the size is less than the String length, the original String is returned. - * A {@code null} String returns {@code null}. A negative size is treated as - * zero. - *

- * - *

- * Equivalent to {@code center(str, size, " ")}. - *

- * - *
-	 * StringUtils.center(null, *)   = null
-	 * StringUtils.center("", 4)     = "    "
-	 * StringUtils.center("ab", -1)  = "ab"
-	 * StringUtils.center("ab", 4)   = " ab "
-	 * StringUtils.center("abcd", 2) = "abcd"
-	 * StringUtils.center("a", 4)    = " a  "
-	 * 
- * - * @param str the String to center, may be null - * @param size the int size of new String, negative treated as zero - * @return centered String, {@code null} if null String input - */ - public static String center(final String str, final int size) { - return center(str, size, ' '); - } - - /** - *

- * Centers a String in a larger String of size {@code size}. Uses a supplied - * character as the value to pad the String with. - *

- * - *

- * If the size is less than the String length, the String is returned. A - * {@code null} String returns {@code null}. A negative size is treated as zero. - *

- * - *
-	 * StringUtils.center(null, *, *)     = null
-	 * StringUtils.center("", 4, ' ')     = "    "
-	 * StringUtils.center("ab", -1, ' ')  = "ab"
-	 * StringUtils.center("ab", 4, ' ')   = " ab "
-	 * StringUtils.center("abcd", 2, ' ') = "abcd"
-	 * StringUtils.center("a", 4, ' ')    = " a  "
-	 * StringUtils.center("a", 4, 'y')    = "yayy"
-	 * 
- * - * @param str the String to center, may be null - * @param size the int size of new String, negative treated as zero - * @param padChar the character to pad the new String with - * @return centered String, {@code null} if null String input - * @since 2.0 - */ - public static String center(String str, final int size, final char padChar) { - if (str == null || size <= 0) { - return str; - } - final int strLen = str.length(); - final int pads = size - strLen; - if (pads <= 0) { - return str; - } - str = leftPad(str, strLen + pads / 2, padChar); - str = rightPad(str, size, padChar); - return str; - } - - /** - *

- * Centers a String in a larger String of size {@code size}. Uses a supplied - * String as the value to pad the String with. - *

- * - *

- * If the size is less than the String length, the String is returned. A - * {@code null} String returns {@code null}. A negative size is treated as zero. - *

- * - *
-	 * StringUtils.center(null, *, *)     = null
-	 * StringUtils.center("", 4, " ")     = "    "
-	 * StringUtils.center("ab", -1, " ")  = "ab"
-	 * StringUtils.center("ab", 4, " ")   = " ab "
-	 * StringUtils.center("abcd", 2, " ") = "abcd"
-	 * StringUtils.center("a", 4, " ")    = " a  "
-	 * StringUtils.center("a", 4, "yz")   = "yayz"
-	 * StringUtils.center("abc", 7, null) = "  abc  "
-	 * StringUtils.center("abc", 7, "")   = "  abc  "
-	 * 
- * - * @param str the String to center, may be null - * @param size the int size of new String, negative treated as zero - * @param padStr the String to pad the new String with, must not be null or - * empty - * @return centered String, {@code null} if null String input - * @throws IllegalArgumentException if padStr is {@code null} or empty - */ - public static String center(String str, final int size, String padStr) { - if (str == null || size <= 0) { - return str; - } - if (isEmpty(padStr)) { - padStr = SPACE; - } - final int strLen = str.length(); - final int pads = size - strLen; - if (pads <= 0) { - return str; - } - str = leftPad(str, strLen + pads / 2, padStr); - str = rightPad(str, size, padStr); - return str; - } - - /** - *

- * Removes one newline from end of a String if it's there, otherwise leave it - * alone. A newline is "{@code \n}", "{@code \r}", or - * "{@code \r\n}". - *

- * - *

- * NOTE: This method changed in 2.0. It now more closely matches Perl chomp. - *

- * - *
-	 * StringUtils.chomp(null)          = null
-	 * StringUtils.chomp("")            = ""
-	 * StringUtils.chomp("abc \r")      = "abc "
-	 * StringUtils.chomp("abc\n")       = "abc"
-	 * StringUtils.chomp("abc\r\n")     = "abc"
-	 * StringUtils.chomp("abc\r\n\r\n") = "abc\r\n"
-	 * StringUtils.chomp("abc\n\r")     = "abc\n"
-	 * StringUtils.chomp("abc\n\rabc")  = "abc\n\rabc"
-	 * StringUtils.chomp("\r")          = ""
-	 * StringUtils.chomp("\n")          = ""
-	 * StringUtils.chomp("\r\n")        = ""
-	 * 
- * - * @param str the String to chomp a newline from, may be null - * @return String without newline, {@code null} if null String input - */ - public static String chomp(final String str) { - if (isEmpty(str)) { - return str; - } - - if (str.length() == 1) { - final char ch = str.charAt(0); - if (ch == CharUtils.CR || ch == CharUtils.LF) { - return EMPTY; - } - return str; - } - - int lastIdx = str.length() - 1; - final char last = str.charAt(lastIdx); - - if (last == CharUtils.LF) { - if (str.charAt(lastIdx - 1) == CharUtils.CR) { - lastIdx--; - } - } else if (last != CharUtils.CR) { - lastIdx++; - } - return str.substring(0, lastIdx); - } - - /** - *

- * Removes {@code separator} from the end of {@code str} if it's there, - * otherwise leave it alone. - *

- * - *

- * NOTE: This method changed in version 2.0. It now more closely matches Perl - * chomp. For the previous behavior, use - * {@link #substringBeforeLast(String, String)}. This method uses - * {@link String#endsWith(String)}. - *

- * - *
-	 * StringUtils.chomp(null, *)         = null
-	 * StringUtils.chomp("", *)           = ""
-	 * StringUtils.chomp("foobar", "bar") = "foo"
-	 * StringUtils.chomp("foobar", "baz") = "foobar"
-	 * StringUtils.chomp("foo", "foo")    = ""
-	 * StringUtils.chomp("foo ", "foo")   = "foo "
-	 * StringUtils.chomp(" foo", "foo")   = " "
-	 * StringUtils.chomp("foo", "foooo")  = "foo"
-	 * StringUtils.chomp("foo", "")       = "foo"
-	 * StringUtils.chomp("foo", null)     = "foo"
-	 * 
- * - * @param str the String to chomp from, may be null - * @param separator separator String, may be null - * @return String without trailing separator, {@code null} if null String input - * @deprecated This feature will be removed in Lang 4.0, use - * {@link StringUtils#removeEnd(String, String)} instead - */ - @Deprecated - public static String chomp(final String str, final String separator) { - return removeEnd(str, separator); - } - - /** - *

- * Remove the last character from a String. - *

- * - *

- * If the String ends in {@code \r\n}, then remove both of them. - *

- * - *
-	 * StringUtils.chop(null)          = null
-	 * StringUtils.chop("")            = ""
-	 * StringUtils.chop("abc \r")      = "abc "
-	 * StringUtils.chop("abc\n")       = "abc"
-	 * StringUtils.chop("abc\r\n")     = "abc"
-	 * StringUtils.chop("abc")         = "ab"
-	 * StringUtils.chop("abc\nabc")    = "abc\nab"
-	 * StringUtils.chop("a")           = ""
-	 * StringUtils.chop("\r")          = ""
-	 * StringUtils.chop("\n")          = ""
-	 * StringUtils.chop("\r\n")        = ""
-	 * 
- * - * @param str the String to chop last character from, may be null - * @return String without last character, {@code null} if null String input - */ - public static String chop(final String str) { - if (str == null) { - return null; - } - final int strLen = str.length(); - if (strLen < 2) { - return EMPTY; - } - final int lastIdx = strLen - 1; - final String ret = str.substring(0, lastIdx); - final char last = str.charAt(lastIdx); - if (last == CharUtils.LF && ret.charAt(lastIdx - 1) == CharUtils.CR) { - return ret.substring(0, lastIdx - 1); - } - return ret; - } - - /** - *

- * Compare two Strings lexicographically, as per - * {@link String#compareTo(String)}, returning : - *

- *
    - *
  • {@code int = 0}, if {@code str1} is equal to {@code str2} (or both - * {@code null})
  • - *
  • {@code int < 0}, if {@code str1} is less than {@code str2}
  • - *
  • {@code int > 0}, if {@code str1} is greater than {@code str2}
  • - *
- * - *

- * This is a {@code null} safe version of : - *

- *
- * - *
-	 * str1.compareTo(str2)
-	 * 
- * - *
- * - *

- * {@code null} value is considered less than non-{@code null} value. Two - * {@code null} references are considered equal. - *

- * - *
-	 * StringUtils.compare(null, null)   = 0
-	 * StringUtils.compare(null , "a")   < 0
-	 * StringUtils.compare("a", null)    > 0
-	 * StringUtils.compare("abc", "abc") = 0
-	 * StringUtils.compare("a", "b")     < 0
-	 * StringUtils.compare("b", "a")     > 0
-	 * StringUtils.compare("a", "B")     > 0
-	 * StringUtils.compare("ab", "abc")  < 0
-	 * 
- * - * @see #compare(String, String, boolean) - * @see String#compareTo(String) - * @param str1 the String to compare from - * @param str2 the String to compare to - * @return < 0, 0, > 0, if {@code str1} is respectively less, equal or - * greater than {@code str2} - * @since 3.5 - */ - public static int compare(final String str1, final String str2) { - return compare(str1, str2, true); - } - - /** - *

- * Compare two Strings lexicographically, as per - * {@link String#compareTo(String)}, returning : - *

- *
    - *
  • {@code int = 0}, if {@code str1} is equal to {@code str2} (or both - * {@code null})
  • - *
  • {@code int < 0}, if {@code str1} is less than {@code str2}
  • - *
  • {@code int > 0}, if {@code str1} is greater than {@code str2}
  • - *
- * - *

- * This is a {@code null} safe version of : - *

- *
- * - *
-	 * str1.compareTo(str2)
-	 * 
- * - *
- * - *

- * {@code null} inputs are handled according to the {@code nullIsLess} - * parameter. Two {@code null} references are considered equal. - *

- * - *
-	 * StringUtils.compare(null, null, *)     = 0
-	 * StringUtils.compare(null , "a", true)  < 0
-	 * StringUtils.compare(null , "a", false) > 0
-	 * StringUtils.compare("a", null, true)   > 0
-	 * StringUtils.compare("a", null, false)  < 0
-	 * StringUtils.compare("abc", "abc", *)   = 0
-	 * StringUtils.compare("a", "b", *)       < 0
-	 * StringUtils.compare("b", "a", *)       > 0
-	 * StringUtils.compare("a", "B", *)       > 0
-	 * StringUtils.compare("ab", "abc", *)    < 0
-	 * 
- * - * @see String#compareTo(String) - * @param str1 the String to compare from - * @param str2 the String to compare to - * @param nullIsLess whether consider {@code null} value less than - * non-{@code null} value - * @return < 0, 0, > 0, if {@code str1} is respectively less, equal ou - * greater than {@code str2} - * @since 3.5 - */ - public static int compare(final String str1, final String str2, final boolean nullIsLess) { - if (str1 == str2) { // NOSONARLINT this intentionally uses == to allow for both null - return 0; - } - if (str1 == null) { - return nullIsLess ? -1 : 1; - } - if (str2 == null) { - return nullIsLess ? 1 : -1; - } - return str1.compareTo(str2); - } - - /** - *

- * Compare two Strings lexicographically, ignoring case differences, as per - * {@link String#compareToIgnoreCase(String)}, returning : - *

- *
    - *
  • {@code int = 0}, if {@code str1} is equal to {@code str2} (or both - * {@code null})
  • - *
  • {@code int < 0}, if {@code str1} is less than {@code str2}
  • - *
  • {@code int > 0}, if {@code str1} is greater than {@code str2}
  • - *
- * - *

- * This is a {@code null} safe version of : - *

- *
- * - *
-	 * str1.compareToIgnoreCase(str2)
-	 * 
- * - *
- * - *

- * {@code null} value is considered less than non-{@code null} value. Two - * {@code null} references are considered equal. Comparison is case insensitive. - *

- * - *
-	 * StringUtils.compareIgnoreCase(null, null)   = 0
-	 * StringUtils.compareIgnoreCase(null , "a")   < 0
-	 * StringUtils.compareIgnoreCase("a", null)    > 0
-	 * StringUtils.compareIgnoreCase("abc", "abc") = 0
-	 * StringUtils.compareIgnoreCase("abc", "ABC") = 0
-	 * StringUtils.compareIgnoreCase("a", "b")     < 0
-	 * StringUtils.compareIgnoreCase("b", "a")     > 0
-	 * StringUtils.compareIgnoreCase("a", "B")     < 0
-	 * StringUtils.compareIgnoreCase("A", "b")     < 0
-	 * StringUtils.compareIgnoreCase("ab", "ABC")  < 0
-	 * 
- * - * @see #compareIgnoreCase(String, String, boolean) - * @see String#compareToIgnoreCase(String) - * @param str1 the String to compare from - * @param str2 the String to compare to - * @return < 0, 0, > 0, if {@code str1} is respectively less, equal ou - * greater than {@code str2}, ignoring case differences. - * @since 3.5 - */ - public static int compareIgnoreCase(final String str1, final String str2) { - return compareIgnoreCase(str1, str2, true); - } - - /** - *

- * Compare two Strings lexicographically, ignoring case differences, as per - * {@link String#compareToIgnoreCase(String)}, returning : - *

- *
    - *
  • {@code int = 0}, if {@code str1} is equal to {@code str2} (or both - * {@code null})
  • - *
  • {@code int < 0}, if {@code str1} is less than {@code str2}
  • - *
  • {@code int > 0}, if {@code str1} is greater than {@code str2}
  • - *
- * - *

- * This is a {@code null} safe version of : - *

- *
- * - *
-	 * str1.compareToIgnoreCase(str2)
-	 * 
- * - *
- * - *

- * {@code null} inputs are handled according to the {@code nullIsLess} - * parameter. Two {@code null} references are considered equal. Comparison is - * case insensitive. - *

- * - *
-	 * StringUtils.compareIgnoreCase(null, null, *)     = 0
-	 * StringUtils.compareIgnoreCase(null , "a", true)  < 0
-	 * StringUtils.compareIgnoreCase(null , "a", false) > 0
-	 * StringUtils.compareIgnoreCase("a", null, true)   > 0
-	 * StringUtils.compareIgnoreCase("a", null, false)  < 0
-	 * StringUtils.compareIgnoreCase("abc", "abc", *)   = 0
-	 * StringUtils.compareIgnoreCase("abc", "ABC", *)   = 0
-	 * StringUtils.compareIgnoreCase("a", "b", *)       < 0
-	 * StringUtils.compareIgnoreCase("b", "a", *)       > 0
-	 * StringUtils.compareIgnoreCase("a", "B", *)       < 0
-	 * StringUtils.compareIgnoreCase("A", "b", *)       < 0
-	 * StringUtils.compareIgnoreCase("ab", "abc", *)    < 0
-	 * 
- * - * @see String#compareToIgnoreCase(String) - * @param str1 the String to compare from - * @param str2 the String to compare to - * @param nullIsLess whether consider {@code null} value less than - * non-{@code null} value - * @return < 0, 0, > 0, if {@code str1} is respectively less, equal ou - * greater than {@code str2}, ignoring case differences. - * @since 3.5 - */ - public static int compareIgnoreCase(final String str1, final String str2, final boolean nullIsLess) { - if (str1 == str2) { // NOSONARLINT this intentionally uses == to allow for both null - return 0; - } - if (str1 == null) { - return nullIsLess ? -1 : 1; - } - if (str2 == null) { - return nullIsLess ? 1 : -1; - } - return str1.compareToIgnoreCase(str2); - } - - /** - *

- * Checks if CharSequence contains a search CharSequence, handling {@code null}. - * This method uses {@link String#indexOf(String)} if possible. - *

- * - *

- * A {@code null} CharSequence will return {@code false}. - *

- * - *
-	 * StringUtils.contains(null, *)     = false
-	 * StringUtils.contains(*, null)     = false
-	 * StringUtils.contains("", "")      = true
-	 * StringUtils.contains("abc", "")   = true
-	 * StringUtils.contains("abc", "a")  = true
-	 * StringUtils.contains("abc", "z")  = false
-	 * 
- * - * @param seq the CharSequence to check, may be null - * @param searchSeq the CharSequence to find, may be null - * @return true if the CharSequence contains the search CharSequence, false if - * not or {@code null} string input - * @since 2.0 - * @since 3.0 Changed signature from contains(String, String) to - * contains(CharSequence, CharSequence) - */ - public static boolean contains(final CharSequence seq, final CharSequence searchSeq) { - if (seq == null || searchSeq == null) { - return false; - } - return CharSequenceUtils.indexOf(seq, searchSeq, 0) >= 0; - } - - /** - *

- * Checks if CharSequence contains a search character, handling {@code null}. - * This method uses {@link String#indexOf(int)} if possible. - *

- * - *

- * A {@code null} or empty ("") CharSequence will return {@code false}. - *

- * - *
-	 * StringUtils.contains(null, *)    = false
-	 * StringUtils.contains("", *)      = false
-	 * StringUtils.contains("abc", 'a') = true
-	 * StringUtils.contains("abc", 'z') = false
-	 * 
- * - * @param seq the CharSequence to check, may be null - * @param searchChar the character to find - * @return true if the CharSequence contains the search character, false if not - * or {@code null} string input - * @since 2.0 - * @since 3.0 Changed signature from contains(String, int) to - * contains(CharSequence, int) - */ - public static boolean contains(final CharSequence seq, final int searchChar) { - if (isEmpty(seq)) { - return false; - } - return CharSequenceUtils.indexOf(seq, searchChar, 0) >= 0; - } - - /** - *

- * Checks if the CharSequence contains any character in the given set of - * characters. - *

- * - *

- * A {@code null} CharSequence will return {@code false}. A {@code null} or zero - * length search array will return {@code false}. - *

- * - *
-	 * StringUtils.containsAny(null, *)                  = false
-	 * StringUtils.containsAny("", *)                    = false
-	 * StringUtils.containsAny(*, null)                  = false
-	 * StringUtils.containsAny(*, [])                    = false
-	 * StringUtils.containsAny("zzabyycdxx", ['z', 'a']) = true
-	 * StringUtils.containsAny("zzabyycdxx", ['b', 'y']) = true
-	 * StringUtils.containsAny("zzabyycdxx", ['z', 'y']) = true
-	 * StringUtils.containsAny("aba", ['z'])             = false
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @param searchChars the chars to search for, may be null - * @return the {@code true} if any of the chars are found, {@code false} if no - * match or null input - * @since 2.4 - * @since 3.0 Changed signature from containsAny(String, char[]) to - * containsAny(CharSequence, char...) - */ - public static boolean containsAny(final CharSequence cs, final char... searchChars) { - if (isEmpty(cs) || searchChars.length == 0) { - return false; - } - final int csLength = cs.length(); - final int searchLength = searchChars.length; - final int csLast = csLength - 1; - final int searchLast = searchLength - 1; - for (int i = 0; i < csLength; i++) { - final char ch = cs.charAt(i); - for (int j = 0; j < searchLength; j++) { - if (searchChars[j] == ch) { - if (Character.isHighSurrogate(ch)) { - if (j == searchLast) { - // missing low surrogate, fine, like String.indexOf(String) - return true; - } - if (i < csLast && searchChars[j + 1] == cs.charAt(i + 1)) { - return true; - } - } else { - // ch is in the Basic Multilingual Plane - return true; - } - } - } - } - return false; - } - - /** - *

- * Checks if the CharSequence contains any character in the given set of - * characters. - *

- * - *

- * A {@code null} CharSequence will return {@code false}. A {@code null} search - * CharSequence will return {@code false}. - *

- * - *
-	 * StringUtils.containsAny(null, *)               = false
-	 * StringUtils.containsAny("", *)                 = false
-	 * StringUtils.containsAny(*, null)               = false
-	 * StringUtils.containsAny(*, "")                 = false
-	 * StringUtils.containsAny("zzabyycdxx", "za")    = true
-	 * StringUtils.containsAny("zzabyycdxx", "by")    = true
-	 * StringUtils.containsAny("zzabyycdxx", "zy")    = true
-	 * StringUtils.containsAny("zzabyycdxx", "\tx")   = true
-	 * StringUtils.containsAny("zzabyycdxx", "$.#yF") = true
-	 * StringUtils.containsAny("aba", "z")            = false
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @param searchChars the chars to search for, may be null - * @return the {@code true} if any of the chars are found, {@code false} if no - * match or null input - * @since 2.4 - * @since 3.0 Changed signature from containsAny(String, String) to - * containsAny(CharSequence, CharSequence) - */ - public static boolean containsAny(final CharSequence cs, final CharSequence searchChars) { - if (searchChars == null) { - return false; - } - return containsAny(cs, CharSequenceUtils.toCharArray(searchChars)); - } - - /** - *

- * Checks if the CharSequence contains any of the CharSequences in the given - * array. - *

- * - *

- * A {@code null} {@code cs} CharSequence will return {@code false}. A - * {@code null} or zero length search array will return {@code false}. - *

- * - *
-	 * StringUtils.containsAny(null, *)            = false
-	 * StringUtils.containsAny("", *)              = false
-	 * StringUtils.containsAny(*, null)            = false
-	 * StringUtils.containsAny(*, [])              = false
-	 * StringUtils.containsAny("abcd", "ab", null) = true
-	 * StringUtils.containsAny("abcd", "ab", "cd") = true
-	 * StringUtils.containsAny("abc", "d", "abc")  = true
-	 * 
- * - * - * @param cs The CharSequence to check, may be null - * @param searchCharSequences The array of CharSequences to search for, may be - * null. Individual CharSequences may be null as - * well. - * @return {@code true} if any of the search CharSequences are found, - * {@code false} otherwise - * @since 3.4 - */ - public static boolean containsAny(final CharSequence cs, final CharSequence... searchCharSequences) { - return containsAny(StringUtils::contains, cs, searchCharSequences); - } - - /** - *

- * Checks if the CharSequence contains any of the CharSequences in the given - * array. - *

- * - *

- * A {@code null} {@code cs} CharSequence will return {@code false}. A - * {@code null} or zero length search array will return {@code false}. - *

- * - * @param cs The CharSequence to check, may be null - * @param searchCharSequences The array of CharSequences to search for, may be - * null. Individual CharSequences may be null as - * well. - * @return {@code true} if any of the search CharSequences are found, - * {@code false} otherwise - * @since 3.12.0 - */ - private static boolean containsAny(final ToBooleanBiFunction test, - final CharSequence cs, final CharSequence... searchCharSequences) { - if (isEmpty(cs) || searchCharSequences.length == 0) { - return false; - } - for (final CharSequence searchCharSequence : searchCharSequences) { - if (test.applyAsBoolean(cs, searchCharSequence)) { - return true; - } - } - return false; - } - - /** - *

- * Checks if the CharSequence contains any of the CharSequences in the given - * array, ignoring case. - *

- * - *

- * A {@code null} {@code cs} CharSequence will return {@code false}. A - * {@code null} or zero length search array will return {@code false}. - *

- * - *
-	 * StringUtils.containsAny(null, *)            = false
-	 * StringUtils.containsAny("", *)              = false
-	 * StringUtils.containsAny(*, null)            = false
-	 * StringUtils.containsAny(*, [])              = false
-	 * StringUtils.containsAny("abcd", "ab", null) = true
-	 * StringUtils.containsAny("abcd", "ab", "cd") = true
-	 * StringUtils.containsAny("abc", "d", "abc")  = true
-	 * StringUtils.containsAny("abc", "D", "ABC")  = true
-	 * StringUtils.containsAny("ABC", "d", "abc")  = true
-	 * 
- * - * - * @param cs The CharSequence to check, may be null - * @param searchCharSequences The array of CharSequences to search for, may be - * null. Individual CharSequences may be null as - * well. - * @return {@code true} if any of the search CharSequences are found, - * {@code false} otherwise - * @since 3.12.0 - */ - public static boolean containsAnyIgnoreCase(final CharSequence cs, final CharSequence... searchCharSequences) { - return containsAny(StringUtils::containsIgnoreCase, cs, searchCharSequences); - } - - /** - *

- * Checks if CharSequence contains a search CharSequence irrespective of case, - * handling {@code null}. Case-insensitivity is defined as by - * {@link String#equalsIgnoreCase(String)}. - * - *

- * A {@code null} CharSequence will return {@code false}. - *

- * - *
-	 * StringUtils.containsIgnoreCase(null, *) = false
-	 * StringUtils.containsIgnoreCase(*, null) = false
-	 * StringUtils.containsIgnoreCase("", "") = true
-	 * StringUtils.containsIgnoreCase("abc", "") = true
-	 * StringUtils.containsIgnoreCase("abc", "a") = true
-	 * StringUtils.containsIgnoreCase("abc", "z") = false
-	 * StringUtils.containsIgnoreCase("abc", "A") = true
-	 * StringUtils.containsIgnoreCase("abc", "Z") = false
-	 * 
- * - * @param str the CharSequence to check, may be null - * @param searchStr the CharSequence to find, may be null - * @return true if the CharSequence contains the search CharSequence - * irrespective of case or false if not or {@code null} string input - * @since 3.0 Changed signature from containsIgnoreCase(String, String) to - * containsIgnoreCase(CharSequence, CharSequence) - */ - public static boolean containsIgnoreCase(final CharSequence str, final CharSequence searchStr) { - if (str == null || searchStr == null) { - return false; - } - final int len = searchStr.length(); - final int max = str.length() - len; - for (int i = 0; i <= max; i++) { - if (CharSequenceUtils.regionMatches(str, true, i, searchStr, 0, len)) { - return true; - } - } - return false; - } - - /** - *

- * Checks that the CharSequence does not contain certain characters. - *

- * - *

- * A {@code null} CharSequence will return {@code true}. A {@code null} invalid - * character array will return {@code true}. An empty CharSequence (length()=0) - * always returns true. - *

- * - *
-	 * StringUtils.containsNone(null, *)       = true
-	 * StringUtils.containsNone(*, null)       = true
-	 * StringUtils.containsNone("", *)         = true
-	 * StringUtils.containsNone("ab", '')      = true
-	 * StringUtils.containsNone("abab", 'xyz') = true
-	 * StringUtils.containsNone("ab1", 'xyz')  = true
-	 * StringUtils.containsNone("abz", 'xyz')  = false
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @param searchChars an array of invalid chars, may be null - * @return true if it contains none of the invalid chars, or is null - * @since 2.0 - * @since 3.0 Changed signature from containsNone(String, char[]) to - * containsNone(CharSequence, char...) - */ - public static boolean containsNone(final CharSequence cs, final char... searchChars) { - if (cs == null || searchChars == null) { - return true; - } - final int csLen = cs.length(); - final int csLast = csLen - 1; - final int searchLen = searchChars.length; - final int searchLast = searchLen - 1; - for (int i = 0; i < csLen; i++) { - final char ch = cs.charAt(i); - for (int j = 0; j < searchLen; j++) { - if (searchChars[j] == ch) { - if (Character.isHighSurrogate(ch)) { - if (j == searchLast) { - // missing low surrogate, fine, like String.indexOf(String) - return false; - } - if (i < csLast && searchChars[j + 1] == cs.charAt(i + 1)) { - return false; - } - } else { - // ch is in the Basic Multilingual Plane - return false; - } - } - } - } - return true; - } - - /** - *

- * Checks that the CharSequence does not contain certain characters. - *

- * - *

- * A {@code null} CharSequence will return {@code true}. A {@code null} invalid - * character array will return {@code true}. An empty String ("") always returns - * true. - *

- * - *
-	 * StringUtils.containsNone(null, *)       = true
-	 * StringUtils.containsNone(*, null)       = true
-	 * StringUtils.containsNone("", *)         = true
-	 * StringUtils.containsNone("ab", "")      = true
-	 * StringUtils.containsNone("abab", "xyz") = true
-	 * StringUtils.containsNone("ab1", "xyz")  = true
-	 * StringUtils.containsNone("abz", "xyz")  = false
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @param invalidChars a String of invalid chars, may be null - * @return true if it contains none of the invalid chars, or is null - * @since 2.0 - * @since 3.0 Changed signature from containsNone(String, String) to - * containsNone(CharSequence, String) - */ - public static boolean containsNone(final CharSequence cs, final String invalidChars) { - if (invalidChars == null) { - return true; - } - return containsNone(cs, invalidChars.toCharArray()); - } - - /** - *

- * Checks if the CharSequence contains only certain characters. - *

- * - *

- * A {@code null} CharSequence will return {@code false}. A {@code null} valid - * character array will return {@code false}. An empty CharSequence (length()=0) - * always returns {@code true}. - *

- * - *
-	 * StringUtils.containsOnly(null, *)       = false
-	 * StringUtils.containsOnly(*, null)       = false
-	 * StringUtils.containsOnly("", *)         = true
-	 * StringUtils.containsOnly("ab", '')      = false
-	 * StringUtils.containsOnly("abab", 'abc') = true
-	 * StringUtils.containsOnly("ab1", 'abc')  = false
-	 * StringUtils.containsOnly("abz", 'abc')  = false
-	 * 
- * - * @param cs the String to check, may be null - * @param valid an array of valid chars, may be null - * @return true if it only contains valid chars and is non-null - * @since 3.0 Changed signature from containsOnly(String, char[]) to - * containsOnly(CharSequence, char...) - */ - public static boolean containsOnly(final CharSequence cs, final char... valid) { - // All these pre-checks are to maintain API with an older version - if (valid == null || cs == null) { - return false; - } - if (cs.length() == 0) { - return true; - } - if (valid.length == 0) { - return false; - } - return indexOfAnyBut(cs, valid) == INDEX_NOT_FOUND; - } - - /** - *

- * Checks if the CharSequence contains only certain characters. - *

- * - *

- * A {@code null} CharSequence will return {@code false}. A {@code null} valid - * character String will return {@code false}. An empty String (length()=0) - * always returns {@code true}. - *

- * - *
-	 * StringUtils.containsOnly(null, *)       = false
-	 * StringUtils.containsOnly(*, null)       = false
-	 * StringUtils.containsOnly("", *)         = true
-	 * StringUtils.containsOnly("ab", "")      = false
-	 * StringUtils.containsOnly("abab", "abc") = true
-	 * StringUtils.containsOnly("ab1", "abc")  = false
-	 * StringUtils.containsOnly("abz", "abc")  = false
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @param validChars a String of valid chars, may be null - * @return true if it only contains valid chars and is non-null - * @since 2.0 - * @since 3.0 Changed signature from containsOnly(String, String) to - * containsOnly(CharSequence, String) - */ - public static boolean containsOnly(final CharSequence cs, final String validChars) { - if (cs == null || validChars == null) { - return false; - } - return containsOnly(cs, validChars.toCharArray()); - } - - /** - *

- * Check whether the given CharSequence contains any whitespace characters. - *

- * - *

- * Whitespace is defined by {@link Character#isWhitespace(char)}. - *

- * - * @param seq the CharSequence to check (may be {@code null}) - * @return {@code true} if the CharSequence is not empty and contains at least 1 - * (breaking) whitespace character - * @since 3.0 - */ - // From org.springframework.util.StringUtils, under Apache License 2.0 - public static boolean containsWhitespace(final CharSequence seq) { - if (isEmpty(seq)) { - return false; - } - final int strLen = seq.length(); - for (int i = 0; i < strLen; i++) { - if (Character.isWhitespace(seq.charAt(i))) { - return true; - } - } - return false; - } - - private static void convertRemainingAccentCharacters(final StringBuilder decomposed) { - for (int i = 0; i < decomposed.length(); i++) { - if (decomposed.charAt(i) == '\u0141') { - decomposed.setCharAt(i, 'L'); - } else if (decomposed.charAt(i) == '\u0142') { - decomposed.setCharAt(i, 'l'); - } - } - } - - /** - *

- * Counts how many times the char appears in the given string. - *

- * - *

- * A {@code null} or empty ("") String input returns {@code 0}. - *

- * - *
-	 * StringUtils.countMatches(null, *)       = 0
-	 * StringUtils.countMatches("", *)         = 0
-	 * StringUtils.countMatches("abba", 0)  = 0
-	 * StringUtils.countMatches("abba", 'a')   = 2
-	 * StringUtils.countMatches("abba", 'b')  = 2
-	 * StringUtils.countMatches("abba", 'x') = 0
-	 * 
- * - * @param str the CharSequence to check, may be null - * @param ch the char to count - * @return the number of occurrences, 0 if the CharSequence is {@code null} - * @since 3.4 - */ - public static int countMatches(final CharSequence str, final char ch) { - if (isEmpty(str)) { - return 0; - } - int count = 0; - // We could also call str.toCharArray() for faster look ups but that would - // generate more garbage. - for (int i = 0; i < str.length(); i++) { - if (ch == str.charAt(i)) { - count++; - } - } - return count; - } - - /** - *

- * Counts how many times the substring appears in the larger string. Note that - * the code only counts non-overlapping matches. - *

- * - *

- * A {@code null} or empty ("") String input returns {@code 0}. - *

- * - *
-	 * StringUtils.countMatches(null, *)       = 0
-	 * StringUtils.countMatches("", *)         = 0
-	 * StringUtils.countMatches("abba", null)  = 0
-	 * StringUtils.countMatches("abba", "")    = 0
-	 * StringUtils.countMatches("abba", "a")   = 2
-	 * StringUtils.countMatches("abba", "ab")  = 1
-	 * StringUtils.countMatches("abba", "xxx") = 0
-	 * StringUtils.countMatches("ababa", "aba") = 1
-	 * 
- * - * @param str the CharSequence to check, may be null - * @param sub the substring to count, may be null - * @return the number of occurrences, 0 if either CharSequence is {@code null} - * @since 3.0 Changed signature from countMatches(String, String) to - * countMatches(CharSequence, CharSequence) - */ - public static int countMatches(final CharSequence str, final CharSequence sub) { - if (isEmpty(str) || isEmpty(sub)) { - return 0; - } - int count = 0; - int idx = 0; - while ((idx = CharSequenceUtils.indexOf(str, sub, idx)) != INDEX_NOT_FOUND) { - count++; - idx += sub.length(); - } - return count; - } - - /** - *

- * Returns either the passed in CharSequence, or if the CharSequence is - * whitespace, empty ("") or {@code null}, the value of {@code defaultStr}. - *

- * - *

- * Whitespace is defined by {@link Character#isWhitespace(char)}. - *

- * - *
-	 * StringUtils.defaultIfBlank(null, "NULL")  = "NULL"
-	 * StringUtils.defaultIfBlank("", "NULL")    = "NULL"
-	 * StringUtils.defaultIfBlank(" ", "NULL")   = "NULL"
-	 * StringUtils.defaultIfBlank("bat", "NULL") = "bat"
-	 * StringUtils.defaultIfBlank("", null)      = null
-	 * 
- * - * @param the specific kind of CharSequence - * @param str the CharSequence to check, may be null - * @param defaultStr the default CharSequence to return if the input is - * whitespace, empty ("") or {@code null}, may be null - * @return the passed in CharSequence, or the default - * @see StringUtils#defaultString(String, String) - */ - public static T defaultIfBlank(final T str, final T defaultStr) { - return isBlank(str) ? defaultStr : str; - } - - /** - *

- * Returns either the passed in CharSequence, or if the CharSequence is empty or - * {@code null}, the value of {@code defaultStr}. - *

- * - *
-	 * StringUtils.defaultIfEmpty(null, "NULL")  = "NULL"
-	 * StringUtils.defaultIfEmpty("", "NULL")    = "NULL"
-	 * StringUtils.defaultIfEmpty(" ", "NULL")   = " "
-	 * StringUtils.defaultIfEmpty("bat", "NULL") = "bat"
-	 * StringUtils.defaultIfEmpty("", null)      = null
-	 * 
- * - * @param the specific kind of CharSequence - * @param str the CharSequence to check, may be null - * @param defaultStr the default CharSequence to return if the input is empty - * ("") or {@code null}, may be null - * @return the passed in CharSequence, or the default - * @see StringUtils#defaultString(String, String) - */ - public static T defaultIfEmpty(final T str, final T defaultStr) { - return isEmpty(str) ? defaultStr : str; - } - - /** - *

- * Returns either the passed in String, or if the String is {@code null}, an - * empty String (""). - *

- * - *
-	 * StringUtils.defaultString(null)  = ""
-	 * StringUtils.defaultString("")    = ""
-	 * StringUtils.defaultString("bat") = "bat"
-	 * 
- * - * @see ObjectUtils#toString(Object) - * @see String#valueOf(Object) - * @param str the String to check, may be null - * @return the passed in String, or the empty String if it was {@code null} - */ - public static String defaultString(final String str) { - return defaultString(str, EMPTY); - } - - /** - *

- * Returns either the passed in String, or if the String is {@code null}, the - * value of {@code defaultStr}. - *

- * - *
-	 * StringUtils.defaultString(null, "NULL")  = "NULL"
-	 * StringUtils.defaultString("", "NULL")    = ""
-	 * StringUtils.defaultString("bat", "NULL") = "bat"
-	 * 
- * - * @see ObjectUtils#toString(Object,String) - * @see String#valueOf(Object) - * @param str the String to check, may be null - * @param defaultStr the default String to return if the input is {@code null}, - * may be null - * @return the passed in String, or the default if it was {@code null} - */ - public static String defaultString(final String str, final String defaultStr) { - return str == null ? defaultStr : str; - } - - /** - *

- * Deletes all whitespaces from a String as defined by - * {@link Character#isWhitespace(char)}. - *

- * - *
-	 * StringUtils.deleteWhitespace(null)         = null
-	 * StringUtils.deleteWhitespace("")           = ""
-	 * StringUtils.deleteWhitespace("abc")        = "abc"
-	 * StringUtils.deleteWhitespace("   ab  c  ") = "abc"
-	 * 
- * - * @param str the String to delete whitespace from, may be null - * @return the String without whitespaces, {@code null} if null String input - */ - public static String deleteWhitespace(final String str) { - if (isEmpty(str)) { - return str; - } - final int sz = str.length(); - final char[] chs = new char[sz]; - int count = 0; - for (int i = 0; i < sz; i++) { - if (!Character.isWhitespace(str.charAt(i))) { - chs[count++] = str.charAt(i); - } - } - if (count == sz) { - return str; - } - if (count == 0) { - return EMPTY; - } - return new String(chs, 0, count); - } - - /** - *

- * Compares two Strings, and returns the portion where they differ. More - * precisely, return the remainder of the second String, starting from where - * it's different from the first. This means that the difference between "abc" - * and "ab" is the empty String and not "c". - *

- * - *

- * For example, {@code difference("i am a machine", "i am a robot") -> "robot"}. - *

- * - *
-	 * StringUtils.difference(null, null) = null
-	 * StringUtils.difference("", "") = ""
-	 * StringUtils.difference("", "abc") = "abc"
-	 * StringUtils.difference("abc", "") = ""
-	 * StringUtils.difference("abc", "abc") = ""
-	 * StringUtils.difference("abc", "ab") = ""
-	 * StringUtils.difference("ab", "abxyz") = "xyz"
-	 * StringUtils.difference("abcde", "abxyz") = "xyz"
-	 * StringUtils.difference("abcde", "xyz") = "xyz"
-	 * 
- * - * @param str1 the first String, may be null - * @param str2 the second String, may be null - * @return the portion of str2 where it differs from str1; returns the empty - * String if they are equal - * @see #indexOfDifference(CharSequence,CharSequence) - * @since 2.0 - */ - public static String difference(final String str1, final String str2) { - if (str1 == null) { - return str2; - } - if (str2 == null) { - return str1; - } - final int at = indexOfDifference(str1, str2); - if (at == INDEX_NOT_FOUND) { - return EMPTY; - } - return str2.substring(at); - } - - /** - *

- * Check if a CharSequence ends with a specified suffix. - *

- * - *

- * {@code null}s are handled without exceptions. Two {@code null} references are - * considered to be equal. The comparison is case sensitive. - *

- * - *
-	 * StringUtils.endsWith(null, null)      = true
-	 * StringUtils.endsWith(null, "def")     = false
-	 * StringUtils.endsWith("abcdef", null)  = false
-	 * StringUtils.endsWith("abcdef", "def") = true
-	 * StringUtils.endsWith("ABCDEF", "def") = false
-	 * StringUtils.endsWith("ABCDEF", "cde") = false
-	 * StringUtils.endsWith("ABCDEF", "")    = true
-	 * 
- * - * @see java.lang.String#endsWith(String) - * @param str the CharSequence to check, may be null - * @param suffix the suffix to find, may be null - * @return {@code true} if the CharSequence ends with the suffix, case - * sensitive, or both {@code null} - * @since 2.4 - * @since 3.0 Changed signature from endsWith(String, String) to - * endsWith(CharSequence, CharSequence) - */ - public static boolean endsWith(final CharSequence str, final CharSequence suffix) { - return endsWith(str, suffix, false); - } - - /** - *

- * Check if a CharSequence ends with a specified suffix (optionally case - * insensitive). - *

- * - * @see java.lang.String#endsWith(String) - * @param str the CharSequence to check, may be null - * @param suffix the suffix to find, may be null - * @param ignoreCase indicates whether the compare should ignore case (case - * insensitive) or not. - * @return {@code true} if the CharSequence starts with the prefix or both - * {@code null} - */ - private static boolean endsWith(final CharSequence str, final CharSequence suffix, final boolean ignoreCase) { - if (str == null || suffix == null) { - return str == suffix; - } - if (suffix.length() > str.length()) { - return false; - } - final int strOffset = str.length() - suffix.length(); - return CharSequenceUtils.regionMatches(str, ignoreCase, strOffset, suffix, 0, suffix.length()); - } - - /** - *

- * Check if a CharSequence ends with any of the provided case-sensitive - * suffixes. - *

- * - *
-	 * StringUtils.endsWithAny(null, null)      = false
-	 * StringUtils.endsWithAny(null, new String[] {"abc"})  = false
-	 * StringUtils.endsWithAny("abcxyz", null)     = false
-	 * StringUtils.endsWithAny("abcxyz", new String[] {""}) = true
-	 * StringUtils.endsWithAny("abcxyz", new String[] {"xyz"}) = true
-	 * StringUtils.endsWithAny("abcxyz", new String[] {null, "xyz", "abc"}) = true
-	 * StringUtils.endsWithAny("abcXYZ", "def", "XYZ") = true
-	 * StringUtils.endsWithAny("abcXYZ", "def", "xyz") = false
-	 * 
- * - * @param sequence the CharSequence to check, may be null - * @param searchStrings the case-sensitive CharSequences to find, may be empty - * or contain {@code null} - * @see StringUtils#endsWith(CharSequence, CharSequence) - * @return {@code true} if the input {@code sequence} is {@code null} AND no - * {@code searchStrings} are provided, or the input {@code sequence} - * ends in any of the provided case-sensitive {@code searchStrings}. - * @since 3.0 - */ - public static boolean endsWithAny(final CharSequence sequence, final CharSequence... searchStrings) { - if (isEmpty(sequence) || searchStrings.length == 0) { - return false; - } - for (final CharSequence searchString : searchStrings) { - if (endsWith(sequence, searchString)) { - return true; - } - } - return false; - } - - /** - *

- * Case insensitive check if a CharSequence ends with a specified suffix. - *

- * - *

- * {@code null}s are handled without exceptions. Two {@code null} references are - * considered to be equal. The comparison is case insensitive. - *

- * - *
-	 * StringUtils.endsWithIgnoreCase(null, null)      = true
-	 * StringUtils.endsWithIgnoreCase(null, "def")     = false
-	 * StringUtils.endsWithIgnoreCase("abcdef", null)  = false
-	 * StringUtils.endsWithIgnoreCase("abcdef", "def") = true
-	 * StringUtils.endsWithIgnoreCase("ABCDEF", "def") = true
-	 * StringUtils.endsWithIgnoreCase("ABCDEF", "cde") = false
-	 * 
- * - * @see java.lang.String#endsWith(String) - * @param str the CharSequence to check, may be null - * @param suffix the suffix to find, may be null - * @return {@code true} if the CharSequence ends with the suffix, case - * insensitive, or both {@code null} - * @since 2.4 - * @since 3.0 Changed signature from endsWithIgnoreCase(String, String) to - * endsWithIgnoreCase(CharSequence, CharSequence) - */ - public static boolean endsWithIgnoreCase(final CharSequence str, final CharSequence suffix) { - return endsWith(str, suffix, true); - } - - /** - *

- * Compares two CharSequences, returning {@code true} if they represent equal - * sequences of characters. - *

- * - *

- * {@code null}s are handled without exceptions. Two {@code null} references are - * considered to be equal. The comparison is case sensitive. - *

- * - *
-	 * StringUtils.equals(null, null)   = true
-	 * StringUtils.equals(null, "abc")  = false
-	 * StringUtils.equals("abc", null)  = false
-	 * StringUtils.equals("abc", "abc") = true
-	 * StringUtils.equals("abc", "ABC") = false
-	 * 
- * - * @param cs1 the first CharSequence, may be {@code null} - * @param cs2 the second CharSequence, may be {@code null} - * @return {@code true} if the CharSequences are equal (case-sensitive), or both - * {@code null} - * @since 3.0 Changed signature from equals(String, String) to - * equals(CharSequence, CharSequence) - * @see Object#equals(Object) - * @see #equalsIgnoreCase(CharSequence, CharSequence) - */ - public static boolean equals(final CharSequence cs1, final CharSequence cs2) { - if (cs1 == cs2) { - return true; - } - if (cs1 == null || cs2 == null) { - return false; - } - if (cs1.length() != cs2.length()) { - return false; - } - if (cs1 instanceof String && cs2 instanceof String) { - return cs1.equals(cs2); - } - // Step-wise comparison - final int length = cs1.length(); - for (int i = 0; i < length; i++) { - if (cs1.charAt(i) != cs2.charAt(i)) { - return false; - } - } - return true; - } - - /** - *

- * Compares given {@code string} to a CharSequences vararg of - * {@code searchStrings}, returning {@code true} if the {@code string} is equal - * to any of the {@code searchStrings}. - *

- * - *
-	 * StringUtils.equalsAny(null, (CharSequence[]) null) = false
-	 * StringUtils.equalsAny(null, null, null)    = true
-	 * StringUtils.equalsAny(null, "abc", "def")  = false
-	 * StringUtils.equalsAny("abc", null, "def")  = false
-	 * StringUtils.equalsAny("abc", "abc", "def") = true
-	 * StringUtils.equalsAny("abc", "ABC", "DEF") = false
-	 * 
- * - * @param string to compare, may be {@code null}. - * @param searchStrings a vararg of strings, may be {@code null}. - * @return {@code true} if the string is equal (case-sensitive) to any other - * element of {@code searchStrings}; {@code false} if - * {@code searchStrings} is null or contains no matches. - * @since 3.5 - */ - public static boolean equalsAny(final CharSequence string, final CharSequence... searchStrings) { - if (searchStrings.length > 0) { - for (final CharSequence next : searchStrings) { - if (equals(string, next)) { - return true; - } - } - } - return false; - } - - /** - *

- * Compares given {@code string} to a CharSequences vararg of - * {@code searchStrings}, returning {@code true} if the {@code string} is equal - * to any of the {@code searchStrings}, ignoring case. - *

- * - *
-	 * StringUtils.equalsAnyIgnoreCase(null, (CharSequence[]) null) = false
-	 * StringUtils.equalsAnyIgnoreCase(null, null, null)    = true
-	 * StringUtils.equalsAnyIgnoreCase(null, "abc", "def")  = false
-	 * StringUtils.equalsAnyIgnoreCase("abc", null, "def")  = false
-	 * StringUtils.equalsAnyIgnoreCase("abc", "abc", "def") = true
-	 * StringUtils.equalsAnyIgnoreCase("abc", "ABC", "DEF") = true
-	 * 
- * - * @param string to compare, may be {@code null}. - * @param searchStrings a vararg of strings, may be {@code null}. - * @return {@code true} if the string is equal (case-insensitive) to any other - * element of {@code searchStrings}; {@code false} if - * {@code searchStrings} is null or contains no matches. - * @since 3.5 - */ - public static boolean equalsAnyIgnoreCase(final CharSequence string, final CharSequence... searchStrings) { - if (searchStrings.length > 0) { - for (final CharSequence next : searchStrings) { - if (equalsIgnoreCase(string, next)) { - return true; - } - } - } - return false; - } - - /** - *

- * Compares two CharSequences, returning {@code true} if they represent equal - * sequences of characters, ignoring case. - *

- * - *

- * {@code null}s are handled without exceptions. Two {@code null} references are - * considered equal. The comparison is case insensitive. - *

- * - *
-	 * StringUtils.equalsIgnoreCase(null, null)   = true
-	 * StringUtils.equalsIgnoreCase(null, "abc")  = false
-	 * StringUtils.equalsIgnoreCase("abc", null)  = false
-	 * StringUtils.equalsIgnoreCase("abc", "abc") = true
-	 * StringUtils.equalsIgnoreCase("abc", "ABC") = true
-	 * 
- * - * @param cs1 the first CharSequence, may be {@code null} - * @param cs2 the second CharSequence, may be {@code null} - * @return {@code true} if the CharSequences are equal (case-insensitive), or - * both {@code null} - * @since 3.0 Changed signature from equalsIgnoreCase(String, String) to - * equalsIgnoreCase(CharSequence, CharSequence) - * @see #equals(CharSequence, CharSequence) - */ - public static boolean equalsIgnoreCase(final CharSequence cs1, final CharSequence cs2) { - if (cs1 == cs2) { - return true; - } - if (cs1 == null || cs2 == null) { - return false; - } - if (cs1.length() != cs2.length()) { - return false; - } - return CharSequenceUtils.regionMatches(cs1, true, 0, cs2, 0, cs1.length()); - } - - /** - *

- * Returns the first value in the array which is not empty (""), {@code null} or - * whitespace only. - *

- * - *

- * Whitespace is defined by {@link Character#isWhitespace(char)}. - *

- * - *

- * If all values are blank or the array is {@code null} or empty then - * {@code null} is returned. - *

- * - *
-	 * StringUtils.firstNonBlank(null, null, null)     = null
-	 * StringUtils.firstNonBlank(null, "", " ")        = null
-	 * StringUtils.firstNonBlank("abc")                = "abc"
-	 * StringUtils.firstNonBlank(null, "xyz")          = "xyz"
-	 * StringUtils.firstNonBlank(null, "", " ", "xyz") = "xyz"
-	 * StringUtils.firstNonBlank(null, "xyz", "abc")   = "xyz"
-	 * StringUtils.firstNonBlank()                     = null
-	 * 
- * - * @param the specific kind of CharSequence - * @param values the values to test, may be {@code null} or empty - * @return the first value from {@code values} which is not blank, or - * {@code null} if there are no non-blank values - * @since 3.8 - */ - @SafeVarargs - public static T firstNonBlank(final T... values) { - if (values != null) { - for (final T val : values) { - if (isNotBlank(val)) { - return val; - } - } - } - return null; - } - - /** - *

- * Returns the first value in the array which is not empty. - *

- * - *

- * If all values are empty or the array is {@code null} or empty then - * {@code null} is returned. - *

- * - *
-	 * StringUtils.firstNonEmpty(null, null, null)   = null
-	 * StringUtils.firstNonEmpty(null, null, "")     = null
-	 * StringUtils.firstNonEmpty(null, "", " ")      = " "
-	 * StringUtils.firstNonEmpty("abc")              = "abc"
-	 * StringUtils.firstNonEmpty(null, "xyz")        = "xyz"
-	 * StringUtils.firstNonEmpty("", "xyz")          = "xyz"
-	 * StringUtils.firstNonEmpty(null, "xyz", "abc") = "xyz"
-	 * StringUtils.firstNonEmpty()                   = null
-	 * 
- * - * @param the specific kind of CharSequence - * @param values the values to test, may be {@code null} or empty - * @return the first value from {@code values} which is not empty, or - * {@code null} if there are no non-empty values - * @since 3.8 - */ - @SafeVarargs - public static T firstNonEmpty(final T... values) { - if (values != null) { - for (final T val : values) { - if (isNotEmpty(val)) { - return val; - } - } - } - return null; - } - - /** - * Calls {@link String#getBytes(Charset)} in a null-safe manner. - * - * @param string input string - * @param charset The {@link Charset} to encode the {@code String}. If null, - * then use the default Charset. - * @return The empty byte[] if {@code string} is null, the result of - * {@link String#getBytes(Charset)} otherwise. - * @see String#getBytes(Charset) - * @since 3.10 - */ - public static byte[] getBytes(final String string, final Charset charset) { - return string == null ? new byte[0] : string.getBytes(Charsets.toCharset(charset)); - } - - /** - * Calls {@link String#getBytes(String)} in a null-safe manner. - * - * @param string input string - * @param charset The {@link Charset} name to encode the {@code String}. If - * null, then use the default Charset. - * @return The empty byte[] if {@code string} is null, the result of - * {@link String#getBytes(String)} otherwise. - * @throws UnsupportedEncodingException Thrown when the named charset is not - * supported. - * @see String#getBytes(String) - * @since 3.10 - */ - public static byte[] getBytes(final String string, final String charset) throws UnsupportedEncodingException { - return string == null ? new byte[0] : string.getBytes(Charsets.toCharsetName(charset)); - } - - /** - *

- * Compares all Strings in an array and returns the initial sequence of - * characters that is common to all of them. - *

- * - *

- * For example, {@code getCommonPrefix(new String[] {"i am a machine", "i am a - * robot"}) -> "i am a "} - *

- * - *
-	 * StringUtils.getCommonPrefix(null) = ""
-	 * StringUtils.getCommonPrefix(new String[] {}) = ""
-	 * StringUtils.getCommonPrefix(new String[] {"abc"}) = "abc"
-	 * StringUtils.getCommonPrefix(new String[] {null, null}) = ""
-	 * StringUtils.getCommonPrefix(new String[] {"", ""}) = ""
-	 * StringUtils.getCommonPrefix(new String[] {"", null}) = ""
-	 * StringUtils.getCommonPrefix(new String[] {"abc", null, null}) = ""
-	 * StringUtils.getCommonPrefix(new String[] {null, null, "abc"}) = ""
-	 * StringUtils.getCommonPrefix(new String[] {"", "abc"}) = ""
-	 * StringUtils.getCommonPrefix(new String[] {"abc", ""}) = ""
-	 * StringUtils.getCommonPrefix(new String[] {"abc", "abc"}) = "abc"
-	 * StringUtils.getCommonPrefix(new String[] {"abc", "a"}) = "a"
-	 * StringUtils.getCommonPrefix(new String[] {"ab", "abxyz"}) = "ab"
-	 * StringUtils.getCommonPrefix(new String[] {"abcde", "abxyz"}) = "ab"
-	 * StringUtils.getCommonPrefix(new String[] {"abcde", "xyz"}) = ""
-	 * StringUtils.getCommonPrefix(new String[] {"xyz", "abcde"}) = ""
-	 * StringUtils.getCommonPrefix(new String[] {"i am a machine", "i am a robot"}) = "i am a "
-	 * 
- * - * @param strs array of String objects, entries may be null - * @return the initial sequence of characters that are common to all Strings in - * the array; empty String if the array is null, the elements are all - * null or if there is no common prefix. - * @since 2.4 - */ - public static String getCommonPrefix(final String... strs) { - if (strs.length == 0) { - return EMPTY; - } - final int smallestIndexOfDiff = indexOfDifference(strs); - if (smallestIndexOfDiff == INDEX_NOT_FOUND) { - // all strings were identical - if (strs[0] == null) { - return EMPTY; - } - return strs[0]; - } else if (smallestIndexOfDiff == 0) { - // there were no common initial characters - return EMPTY; - } else { - // we found a common initial character sequence - return strs[0].substring(0, smallestIndexOfDiff); - } - } - - /** - *

- * Checks if a String {@code str} contains Unicode digits, if yes then - * concatenate all the digits in {@code str} and return it as a String. - *

- * - *

- * An empty ("") String will be returned if no digits found in {@code str}. - *

- * - *
-	 * StringUtils.getDigits(null)  = null
-	 * StringUtils.getDigits("")    = ""
-	 * StringUtils.getDigits("abc") = ""
-	 * StringUtils.getDigits("1000$") = "1000"
-	 * StringUtils.getDigits("1123~45") = "112345"
-	 * StringUtils.getDigits("(541) 754-3010") = "5417543010"
-	 * StringUtils.getDigits("\u0967\u0968\u0969") = "\u0967\u0968\u0969"
-	 * 
- * - * @param str the String to extract digits from, may be null - * @return String with only digits, or an empty ("") String if no digits found, - * or {@code null} String if {@code str} is null - * @since 3.6 - */ - public static String getDigits(final String str) { - if (isEmpty(str)) { - return str; - } - final int sz = str.length(); - final StringBuilder strDigits = new StringBuilder(sz); - for (int i = 0; i < sz; i++) { - final char tempChar = str.charAt(i); - if (Character.isDigit(tempChar)) { - strDigits.append(tempChar); - } - } - return strDigits.toString(); - } - - /** - *

- * Find the Fuzzy Distance which indicates the similarity score between two - * Strings. - *

- * - *

- * This string matching algorithm is similar to the algorithms of editors such - * as Sublime Text, TextMate, Atom and others. One point is given for every - * matched character. Subsequent matches yield two bonus points. A higher score - * indicates a higher similarity. - *

- * - *
-	 * StringUtils.getFuzzyDistance(null, null, null)                                    = IllegalArgumentException
-	 * StringUtils.getFuzzyDistance("", "", Locale.ENGLISH)                              = 0
-	 * StringUtils.getFuzzyDistance("Workshop", "b", Locale.ENGLISH)                     = 0
-	 * StringUtils.getFuzzyDistance("Room", "o", Locale.ENGLISH)                         = 1
-	 * StringUtils.getFuzzyDistance("Workshop", "w", Locale.ENGLISH)                     = 1
-	 * StringUtils.getFuzzyDistance("Workshop", "ws", Locale.ENGLISH)                    = 2
-	 * StringUtils.getFuzzyDistance("Workshop", "wo", Locale.ENGLISH)                    = 4
-	 * StringUtils.getFuzzyDistance("Apache Software Foundation", "asf", Locale.ENGLISH) = 3
-	 * 
- * - * @param term a full term that should be matched against, must not be null - * @param query the query that will be matched against a term, must not be null - * @param locale This string matching logic is case insensitive. A locale is - * necessary to normalize both Strings to lower case. - * @return result score - * @throws IllegalArgumentException if either String input {@code null} or - * Locale input {@code null} - * @since 3.4 - * @deprecated as of 3.6, use commons-text - * FuzzyScore instead - */ - @Deprecated - public static int getFuzzyDistance(final CharSequence term, final CharSequence query, final Locale locale) { - if (term == null || query == null) { - throw new IllegalArgumentException("Strings must not be null"); - } else if (locale == null) { - throw new IllegalArgumentException("Locale must not be null"); - } - - // fuzzy logic is case insensitive. We normalize the Strings to lower - // case right from the start. Turning characters to lower case - // via Character.toLowerCase(char) is unfortunately insufficient - // as it does not accept a locale. - final String termLowerCase = term.toString().toLowerCase(locale); - final String queryLowerCase = query.toString().toLowerCase(locale); - - // the resulting score - int score = 0; - - // the position in the term which will be scanned next for potential - // query character matches - int termIndex = 0; - - // index of the previously matched character in the term - int previousMatchingCharacterIndex = Integer.MIN_VALUE; - - for (int queryIndex = 0; queryIndex < queryLowerCase.length(); queryIndex++) { - final char queryChar = queryLowerCase.charAt(queryIndex); - - boolean termCharacterMatchFound = false; - for (; termIndex < termLowerCase.length() && !termCharacterMatchFound; termIndex++) { - final char termChar = termLowerCase.charAt(termIndex); - - if (queryChar == termChar) { - // simple character matches result in one point - score++; - - // subsequent character matches further improve - // the score. - if (previousMatchingCharacterIndex + 1 == termIndex) { - score += 2; - } - - previousMatchingCharacterIndex = termIndex; - - // we can leave the nested loop. Every character in the - // query can match at most one character in the term. - termCharacterMatchFound = true; - } - } - } - - return score; - } - - /** - *

- * Returns either the passed in CharSequence, or if the CharSequence is - * whitespace, empty ("") or {@code null}, the value supplied by - * {@code defaultStrSupplier}. - *

- * - *

- * Whitespace is defined by {@link Character#isWhitespace(char)}. - *

- * - *

- * Caller responsible for thread-safety and exception handling of default value - * supplier - *

- * - *
-	 * {@code
-	 * StringUtils.getIfBlank(null, () -> "NULL")   = "NULL"
-	 * StringUtils.getIfBlank("", () -> "NULL")     = "NULL"
-	 * StringUtils.getIfBlank(" ", () -> "NULL")    = "NULL"
-	 * StringUtils.getIfBlank("bat", () -> "NULL")  = "bat"
-	 * StringUtils.getIfBlank("", () -> null)       = null
-	 * StringUtils.getIfBlank("", null)             = null
-	 * }
-	 * 
- * - * @param the specific kind of CharSequence - * @param str the CharSequence to check, may be null - * @param defaultSupplier the supplier of default CharSequence to return if the - * input is whitespace, empty ("") or {@code null}, may - * be null - * @return the passed in CharSequence, or the default - * @see StringUtils#defaultString(String, String) - * @since 3.10 - */ - public static T getIfBlank(final T str, final Supplier defaultSupplier) { - return isBlank(str) ? defaultSupplier == null ? null : defaultSupplier.get() : str; - } - - /** - *

- * Returns either the passed in CharSequence, or if the CharSequence is empty or - * {@code null}, the value supplied by {@code defaultStrSupplier}. - *

- * - *

- * Caller responsible for thread-safety and exception handling of default value - * supplier - *

- * - *
-	 * {@code
-	 * StringUtils.getIfEmpty(null, () -> "NULL")    = "NULL"
-	 * StringUtils.getIfEmpty("", () -> "NULL")      = "NULL"
-	 * StringUtils.getIfEmpty(" ", () -> "NULL")     = " "
-	 * StringUtils.getIfEmpty("bat", () -> "NULL")   = "bat"
-	 * StringUtils.getIfEmpty("", () -> null)        = null
-	 * StringUtils.getIfEmpty("", null)              = null
-	 * }
-	 * 
- * - * @param the specific kind of CharSequence - * @param str the CharSequence to check, may be null - * @param defaultSupplier the supplier of default CharSequence to return if the - * input is empty ("") or {@code null}, may be null - * @return the passed in CharSequence, or the default - * @see StringUtils#defaultString(String, String) - * @since 3.10 - */ - public static T getIfEmpty(final T str, final Supplier defaultSupplier) { - return isEmpty(str) ? defaultSupplier == null ? null : defaultSupplier.get() : str; - } - - /** - *

- * Find the Jaro Winkler Distance which indicates the similarity score between - * two Strings. - *

- * - *

- * The Jaro measure is the weighted sum of percentage of matched characters from - * each file and transposed characters. Winkler increased this measure for - * matching initial characters. - *

- * - *

- * This implementation is based on the Jaro Winkler similarity algorithm from - * http://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance. - *

- * - *
-	 * StringUtils.getJaroWinklerDistance(null, null)          = IllegalArgumentException
-	 * StringUtils.getJaroWinklerDistance("", "")              = 0.0
-	 * StringUtils.getJaroWinklerDistance("", "a")             = 0.0
-	 * StringUtils.getJaroWinklerDistance("aaapppp", "")       = 0.0
-	 * StringUtils.getJaroWinklerDistance("frog", "fog")       = 0.93
-	 * StringUtils.getJaroWinklerDistance("fly", "ant")        = 0.0
-	 * StringUtils.getJaroWinklerDistance("elephant", "hippo") = 0.44
-	 * StringUtils.getJaroWinklerDistance("hippo", "elephant") = 0.44
-	 * StringUtils.getJaroWinklerDistance("hippo", "zzzzzzzz") = 0.0
-	 * StringUtils.getJaroWinklerDistance("hello", "hallo")    = 0.88
-	 * StringUtils.getJaroWinklerDistance("ABC Corporation", "ABC Corp") = 0.93
-	 * StringUtils.getJaroWinklerDistance("D N H Enterprises Inc", "D & H Enterprises, Inc.") = 0.95
-	 * StringUtils.getJaroWinklerDistance("My Gym Children's Fitness Center", "My Gym. Childrens Fitness") = 0.92
-	 * StringUtils.getJaroWinklerDistance("PENNSYLVANIA", "PENNCISYLVNIA") = 0.88
-	 * 
- * - * @param first the first String, must not be null - * @param second the second String, must not be null - * @return result distance - * @throws IllegalArgumentException if either String input {@code null} - * @since 3.3 - * @deprecated as of 3.6, use commons-text - * JaroWinklerDistance instead - */ - @Deprecated - public static double getJaroWinklerDistance(final CharSequence first, final CharSequence second) { - final double DEFAULT_SCALING_FACTOR = 0.1; - - if (first == null || second == null) { - throw new IllegalArgumentException("Strings must not be null"); - } - - final int[] mtp = matches(first, second); - final double m = mtp[0]; - if (m == 0) { - return 0D; - } - final double j = (m / first.length() + m / second.length() + (m - mtp[1]) / m) / 3; - final double jw = j < 0.7D ? j : j + Math.min(DEFAULT_SCALING_FACTOR, 1D / mtp[3]) * mtp[2] * (1D - j); - return Math.round(jw * 100.0D) / 100.0D; - } - - /** - *

- * Find the Levenshtein distance between two Strings. - *

- * - *

- * This is the number of changes needed to change one String into another, where - * each change is a single character modification (deletion, insertion or - * substitution). - *

- * - *

- * The implementation uses a single-dimensional array of length s.length() + 1. - * See - * http://blog.softwx.net/2014/12/optimizing-levenshtein-algorithm-in-c.html - * for details. - *

- * - *
-	 * StringUtils.getLevenshteinDistance(null, *)             = IllegalArgumentException
-	 * StringUtils.getLevenshteinDistance(*, null)             = IllegalArgumentException
-	 * StringUtils.getLevenshteinDistance("", "")              = 0
-	 * StringUtils.getLevenshteinDistance("", "a")             = 1
-	 * StringUtils.getLevenshteinDistance("aaapppp", "")       = 7
-	 * StringUtils.getLevenshteinDistance("frog", "fog")       = 1
-	 * StringUtils.getLevenshteinDistance("fly", "ant")        = 3
-	 * StringUtils.getLevenshteinDistance("elephant", "hippo") = 7
-	 * StringUtils.getLevenshteinDistance("hippo", "elephant") = 7
-	 * StringUtils.getLevenshteinDistance("hippo", "zzzzzzzz") = 8
-	 * StringUtils.getLevenshteinDistance("hello", "hallo")    = 1
-	 * 
- * - * @param s the first String, must not be null - * @param t the second String, must not be null - * @return result distance - * @throws IllegalArgumentException if either String input {@code null} - * @since 3.0 Changed signature from getLevenshteinDistance(String, String) to - * getLevenshteinDistance(CharSequence, CharSequence) - * @deprecated as of 3.6, use commons-text - * LevenshteinDistance instead - */ - @Deprecated - public static int getLevenshteinDistance(CharSequence s, CharSequence t) { - if (s == null || t == null) { - throw new IllegalArgumentException("Strings must not be null"); - } - - int n = s.length(); - int m = t.length(); - - if (n == 0) { - return m; - } else if (m == 0) { - return n; - } - - if (n > m) { - // swap the input strings to consume less memory - final CharSequence tmp = s; - s = t; - t = tmp; - n = m; - m = t.length(); - } - - final int[] p = new int[n + 1]; - // indexes into strings s and t - int i; // iterates through s - int j; // iterates through t - int upper_left; - int upper; - - char t_j; // jth character of t - int cost; - - for (i = 0; i <= n; i++) { - p[i] = i; - } - - for (j = 1; j <= m; j++) { - upper_left = p[0]; - t_j = t.charAt(j - 1); - p[0] = j; - - for (i = 1; i <= n; i++) { - upper = p[i]; - cost = s.charAt(i - 1) == t_j ? 0 : 1; - // minimum of cell to the left+1, to the top+1, diagonally left and up +cost - p[i] = Math.min(Math.min(p[i - 1] + 1, p[i] + 1), upper_left + cost); - upper_left = upper; - } - } - - return p[n]; - } - - /** - *

- * Find the Levenshtein distance between two Strings if it's less than or equal - * to a given threshold. - *

- * - *

- * This is the number of changes needed to change one String into another, where - * each change is a single character modification (deletion, insertion or - * substitution). - *

- * - *

- * This implementation follows from Algorithms on Strings, Trees and Sequences - * by Dan Gusfield and Chas Emerick's implementation of the Levenshtein distance - * algorithm from http://www.merriampark.com/ld.htm - *

- * - *
-	 * StringUtils.getLevenshteinDistance(null, *, *)             = IllegalArgumentException
-	 * StringUtils.getLevenshteinDistance(*, null, *)             = IllegalArgumentException
-	 * StringUtils.getLevenshteinDistance(*, *, -1)               = IllegalArgumentException
-	 * StringUtils.getLevenshteinDistance("", "", 0)              = 0
-	 * StringUtils.getLevenshteinDistance("aaapppp", "", 8)       = 7
-	 * StringUtils.getLevenshteinDistance("aaapppp", "", 7)       = 7
-	 * StringUtils.getLevenshteinDistance("aaapppp", "", 6))      = -1
-	 * StringUtils.getLevenshteinDistance("elephant", "hippo", 7) = 7
-	 * StringUtils.getLevenshteinDistance("elephant", "hippo", 6) = -1
-	 * StringUtils.getLevenshteinDistance("hippo", "elephant", 7) = 7
-	 * StringUtils.getLevenshteinDistance("hippo", "elephant", 6) = -1
-	 * 
- * - * @param s the first String, must not be null - * @param t the second String, must not be null - * @param threshold the target threshold, must not be negative - * @return result distance, or {@code -1} if the distance would be greater than - * the threshold - * @throws IllegalArgumentException if either String input {@code null} or - * negative threshold - * @deprecated as of 3.6, use commons-text - * LevenshteinDistance instead - */ - @Deprecated - public static int getLevenshteinDistance(CharSequence s, CharSequence t, final int threshold) { - if (s == null || t == null) { - throw new IllegalArgumentException("Strings must not be null"); - } - if (threshold < 0) { - throw new IllegalArgumentException("Threshold must not be negative"); - } - - /* - * This implementation only computes the distance if it's less than or equal to - * the threshold value, returning -1 if it's greater. The advantage is - * performance: unbounded distance is O(nm), but a bound of k allows us to - * reduce it to O(km) time by only computing a diagonal stripe of width 2k + 1 - * of the cost table. It is also possible to use this to compute the unbounded - * Levenshtein distance by starting the threshold at 1 and doubling each time - * until the distance is found; this is O(dm), where d is the distance. - * - * One subtlety comes from needing to ignore entries on the border of our stripe - * eg. p[] = |#|#|#|* d[] = *|#|#|#| We must ignore the entry to the left of the - * leftmost member We must ignore the entry above the rightmost member - * - * Another subtlety comes from our stripe running off the matrix if the strings - * aren't of the same size. Since string s is always swapped to be the shorter - * of the two, the stripe will always run off to the upper right instead of the - * lower left of the matrix. - * - * As a concrete example, suppose s is of length 5, t is of length 7, and our - * threshold is 1. In this case we're going to walk a stripe of length 3. The - * matrix would look like so: - * - * 1 2 3 4 5 1 |#|#| | | | 2 |#|#|#| | | 3 | |#|#|#| | 4 | | |#|#|#| 5 | | | - * |#|#| 6 | | | | |#| 7 | | | | | | - * - * Note how the stripe leads off the table as there is no possible way to turn a - * string of length 5 into one of length 7 in edit distance of 1. - * - * Additionally, this implementation decreases memory usage by using two - * single-dimensional arrays and swapping them back and forth instead of - * allocating an entire n by m matrix. This requires a few minor changes, such - * as immediately returning when it's detected that the stripe has run off the - * matrix and initially filling the arrays with large values so that entries we - * don't compute are ignored. - * - * See Algorithms on Strings, Trees and Sequences by Dan Gusfield for some - * discussion. - */ - - int n = s.length(); // length of s - int m = t.length(); // length of t - - // if one string is empty, the edit distance is necessarily the length of the - // other - if (n == 0) { - return m <= threshold ? m : -1; - } else if (m == 0) { - return n <= threshold ? n : -1; - } else if (Math.abs(n - m) > threshold) { - // no need to calculate the distance if the length difference is greater than - // the threshold - return -1; - } - - if (n > m) { - // swap the two strings to consume less memory - final CharSequence tmp = s; - s = t; - t = tmp; - n = m; - m = t.length(); - } - - int[] p = new int[n + 1]; // 'previous' cost array, horizontally - int[] d = new int[n + 1]; // cost array, horizontally - int[] _d; // placeholder to assist in swapping p and d - - // fill in starting table values - final int boundary = Math.min(n, threshold) + 1; - for (int i = 0; i < boundary; i++) { - p[i] = i; - } - // these fills ensure that the value above the rightmost entry of our - // stripe will be ignored in following loop iterations - Arrays.fill(p, boundary, p.length, Integer.MAX_VALUE); - Arrays.fill(d, Integer.MAX_VALUE); - - // iterates through t - for (int j = 1; j <= m; j++) { - final char t_j = t.charAt(j - 1); // jth character of t - d[0] = j; - - // compute stripe indices, constrain to array size - final int min = Math.max(1, j - threshold); - final int max = j > Integer.MAX_VALUE - threshold ? n : Math.min(n, j + threshold); - - // the stripe may lead off of the table if s and t are of different sizes - if (min > max) { - return -1; - } - - // ignore entry left of leftmost - if (min > 1) { - d[min - 1] = Integer.MAX_VALUE; - } - - // iterates through [min, max] in s - for (int i = min; i <= max; i++) { - if (s.charAt(i - 1) == t_j) { - // diagonally left and up - d[i] = p[i - 1]; - } else { - // 1 + minimum of cell to the left, to the top, diagonally left and up - d[i] = 1 + Math.min(Math.min(d[i - 1], p[i]), p[i - 1]); - } - } - - // copy current distance counts to 'previous row' distance counts - _d = p; - p = d; - d = _d; - } - - // if p[n] is greater than the threshold, there's no guarantee on it being the - // correct - // distance - if (p[n] <= threshold) { - return p[n]; - } - return -1; - } - - /** - *

- * Finds the first index within a CharSequence, handling {@code null}. This - * method uses {@link String#indexOf(String, int)} if possible. - *

- * - *

- * A {@code null} CharSequence will return {@code -1}. - *

- * - *
-	 * StringUtils.indexOf(null, *)          = -1
-	 * StringUtils.indexOf(*, null)          = -1
-	 * StringUtils.indexOf("", "")           = 0
-	 * StringUtils.indexOf("", *)            = -1 (except when * = "")
-	 * StringUtils.indexOf("aabaabaa", "a")  = 0
-	 * StringUtils.indexOf("aabaabaa", "b")  = 2
-	 * StringUtils.indexOf("aabaabaa", "ab") = 1
-	 * StringUtils.indexOf("aabaabaa", "")   = 0
-	 * 
- * - * @param seq the CharSequence to check, may be null - * @param searchSeq the CharSequence to find, may be null - * @return the first index of the search CharSequence, -1 if no match or - * {@code null} string input - * @since 2.0 - * @since 3.0 Changed signature from indexOf(String, String) to - * indexOf(CharSequence, CharSequence) - */ - public static int indexOf(final CharSequence seq, final CharSequence searchSeq) { - if (seq == null || searchSeq == null) { - return INDEX_NOT_FOUND; - } - return CharSequenceUtils.indexOf(seq, searchSeq, 0); - } - - /** - *

- * Finds the first index within a CharSequence, handling {@code null}. This - * method uses {@link String#indexOf(String, int)} if possible. - *

- * - *

- * A {@code null} CharSequence will return {@code -1}. A negative start position - * is treated as zero. An empty ("") search CharSequence always matches. A start - * position greater than the string length only matches an empty search - * CharSequence. - *

- * - *
-	 * StringUtils.indexOf(null, *, *)          = -1
-	 * StringUtils.indexOf(*, null, *)          = -1
-	 * StringUtils.indexOf("", "", 0)           = 0
-	 * StringUtils.indexOf("", *, 0)            = -1 (except when * = "")
-	 * StringUtils.indexOf("aabaabaa", "a", 0)  = 0
-	 * StringUtils.indexOf("aabaabaa", "b", 0)  = 2
-	 * StringUtils.indexOf("aabaabaa", "ab", 0) = 1
-	 * StringUtils.indexOf("aabaabaa", "b", 3)  = 5
-	 * StringUtils.indexOf("aabaabaa", "b", 9)  = -1
-	 * StringUtils.indexOf("aabaabaa", "b", -1) = 2
-	 * StringUtils.indexOf("aabaabaa", "", 2)   = 2
-	 * StringUtils.indexOf("abc", "", 9)        = 3
-	 * 
- * - * @param seq the CharSequence to check, may be null - * @param searchSeq the CharSequence to find, may be null - * @param startPos the start position, negative treated as zero - * @return the first index of the search CharSequence (always ≥ startPos), -1 - * if no match or {@code null} string input - * @since 2.0 - * @since 3.0 Changed signature from indexOf(String, String, int) to - * indexOf(CharSequence, CharSequence, int) - */ - public static int indexOf(final CharSequence seq, final CharSequence searchSeq, final int startPos) { - if (seq == null || searchSeq == null) { - return INDEX_NOT_FOUND; - } - return CharSequenceUtils.indexOf(seq, searchSeq, startPos); - } - - /** - * Returns the index within {@code seq} of the first occurrence of the specified - * character. If a character with value {@code searchChar} occurs in the - * character sequence represented by {@code seq} {@code CharSequence} object, - * then the index (in Unicode code units) of the first such occurrence is - * returned. For values of {@code searchChar} in the range from 0 to 0xFFFF - * (inclusive), this is the smallest value k such that:
- * - *
-	 * this.charAt(k) == searchChar
-	 * 
- * - *
is true. For other values of {@code searchChar}, it is the - * smallest value k such that:
- * - *
-	 * this.codePointAt(k) == searchChar
-	 * 
- * - *
is true. In either case, if no such character occurs in - * {@code seq}, then {@code INDEX_NOT_FOUND (-1)} is returned. - * - *

- * Furthermore, a {@code null} or empty ("") CharSequence will return - * {@code INDEX_NOT_FOUND (-1)}. - *

- * - *
-	 * StringUtils.indexOf(null, *)         = -1
-	 * StringUtils.indexOf("", *)           = -1
-	 * StringUtils.indexOf("aabaabaa", 'a') = 0
-	 * StringUtils.indexOf("aabaabaa", 'b') = 2
-	 * 
- * - * @param seq the CharSequence to check, may be null - * @param searchChar the character to find - * @return the first index of the search character, -1 if no match or - * {@code null} string input - * @since 2.0 - * @since 3.0 Changed signature from indexOf(String, int) to - * indexOf(CharSequence, int) - * @since 3.6 Updated {@link CharSequenceUtils} call to behave more like - * {@code String} - */ - public static int indexOf(final CharSequence seq, final int searchChar) { - if (isEmpty(seq)) { - return INDEX_NOT_FOUND; - } - return CharSequenceUtils.indexOf(seq, searchChar, 0); - } - - /** - * - * Returns the index within {@code seq} of the first occurrence of the specified - * character, starting the search at the specified index. - *

- * If a character with value {@code searchChar} occurs in the character sequence - * represented by the {@code seq} {@code CharSequence} object at an index no - * smaller than {@code startPos}, then the index of the first such occurrence is - * returned. For values of {@code searchChar} in the range from 0 to 0xFFFF - * (inclusive), this is the smallest value k such that:

- * - *
-	 * (this.charAt(k) == searchChar) && (k >= startPos)
-	 * 
- * - *
is true. For other values of {@code searchChar}, it is the - * smallest value k such that:
- * - *
-	 * (this.codePointAt(k) == searchChar) && (k >= startPos)
-	 * 
- * - *
is true. In either case, if no such character occurs in - * {@code seq} at or after position {@code startPos}, then {@code -1} is - * returned. - * - *

- * There is no restriction on the value of {@code startPos}. If it is negative, - * it has the same effect as if it were zero: this entire string may be - * searched. If it is greater than the length of this string, it has the same - * effect as if it were equal to the length of this string: - * {@code (INDEX_NOT_FOUND) -1} is returned. Furthermore, a {@code null} or - * empty ("") CharSequence will return {@code (INDEX_NOT_FOUND) -1}. - * - *

- * All indices are specified in {@code char} values (Unicode code units). - * - *

-	 * StringUtils.indexOf(null, *, *)          = -1
-	 * StringUtils.indexOf("", *, *)            = -1
-	 * StringUtils.indexOf("aabaabaa", 'b', 0)  = 2
-	 * StringUtils.indexOf("aabaabaa", 'b', 3)  = 5
-	 * StringUtils.indexOf("aabaabaa", 'b', 9)  = -1
-	 * StringUtils.indexOf("aabaabaa", 'b', -1) = 2
-	 * 
- * - * @param seq the CharSequence to check, may be null - * @param searchChar the character to find - * @param startPos the start position, negative treated as zero - * @return the first index of the search character (always ≥ startPos), -1 if - * no match or {@code null} string input - * @since 2.0 - * @since 3.0 Changed signature from indexOf(String, int, int) to - * indexOf(CharSequence, int, int) - * @since 3.6 Updated {@link CharSequenceUtils} call to behave more like - * {@code String} - */ - public static int indexOf(final CharSequence seq, final int searchChar, final int startPos) { - if (isEmpty(seq)) { - return INDEX_NOT_FOUND; - } - return CharSequenceUtils.indexOf(seq, searchChar, startPos); - } - - /** - *

- * Search a CharSequence to find the first index of any character in the given - * set of characters. - *

- * - *

- * A {@code null} String will return {@code -1}. A {@code null} or zero length - * search array will return {@code -1}. - *

- * - *
-	 * StringUtils.indexOfAny(null, *)                  = -1
-	 * StringUtils.indexOfAny("", *)                    = -1
-	 * StringUtils.indexOfAny(*, null)                  = -1
-	 * StringUtils.indexOfAny(*, [])                    = -1
-	 * StringUtils.indexOfAny("zzabyycdxx", ['z', 'a']) = 0
-	 * StringUtils.indexOfAny("zzabyycdxx", ['b', 'y']) = 3
-	 * StringUtils.indexOfAny("aba", ['z'])             = -1
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @param searchChars the chars to search for, may be null - * @return the index of any of the chars, -1 if no match or null input - * @since 2.0 - * @since 3.0 Changed signature from indexOfAny(String, char[]) to - * indexOfAny(CharSequence, char...) - */ - public static int indexOfAny(final CharSequence cs, final char... searchChars) { - if (isEmpty(cs) || searchChars.length == 0) { - return INDEX_NOT_FOUND; - } - final int csLen = cs.length(); - final int csLast = csLen - 1; - final int searchLen = searchChars.length; - final int searchLast = searchLen - 1; - for (int i = 0; i < csLen; i++) { - final char ch = cs.charAt(i); - for (int j = 0; j < searchLen; j++) { - if (searchChars[j] == ch) { - if (i < csLast && j < searchLast && Character.isHighSurrogate(ch)) { - // ch is a supplementary character - if (searchChars[j + 1] == cs.charAt(i + 1)) { - return i; - } - } else { - return i; - } - } - } - } - return INDEX_NOT_FOUND; - } - - /** - *

- * Find the first index of any of a set of potential substrings. - *

- * - *

- * A {@code null} CharSequence will return {@code -1}. A {@code null} or zero - * length search array will return {@code -1}. A {@code null} search array entry - * will be ignored, but a search array containing "" will return {@code 0} if - * {@code str} is not null. This method uses {@link String#indexOf(String)} if - * possible. - *

- * - *
-	 * StringUtils.indexOfAny(null, *)                      = -1
-	 * StringUtils.indexOfAny(*, null)                      = -1
-	 * StringUtils.indexOfAny(*, [])                        = -1
-	 * StringUtils.indexOfAny("zzabyycdxx", ["ab", "cd"])   = 2
-	 * StringUtils.indexOfAny("zzabyycdxx", ["cd", "ab"])   = 2
-	 * StringUtils.indexOfAny("zzabyycdxx", ["mn", "op"])   = -1
-	 * StringUtils.indexOfAny("zzabyycdxx", ["zab", "aby"]) = 1
-	 * StringUtils.indexOfAny("zzabyycdxx", [""])           = 0
-	 * StringUtils.indexOfAny("", [""])                     = 0
-	 * StringUtils.indexOfAny("", ["a"])                    = -1
-	 * 
- * - * @param str the CharSequence to check, may be null - * @param searchStrs the CharSequences to search for, may be null - * @return the first index of any of the searchStrs in str, -1 if no match - * @since 3.0 Changed signature from indexOfAny(String, String[]) to - * indexOfAny(CharSequence, CharSequence...) - */ - public static int indexOfAny(final CharSequence str, final CharSequence... searchStrs) { - if (str == null || searchStrs == null) { - return INDEX_NOT_FOUND; - } - - // String's can't have a MAX_VALUEth index. - int ret = Integer.MAX_VALUE; - - int tmp = 0; - for (final CharSequence search : searchStrs) { - if (search == null) { - continue; - } - tmp = CharSequenceUtils.indexOf(str, search, 0); - if (tmp == INDEX_NOT_FOUND) { - continue; - } - - if (tmp < ret) { - ret = tmp; - } - } - - return ret == Integer.MAX_VALUE ? INDEX_NOT_FOUND : ret; - } - - /** - *

- * Search a CharSequence to find the first index of any character in the given - * set of characters. - *

- * - *

- * A {@code null} String will return {@code -1}. A {@code null} search string - * will return {@code -1}. - *

- * - *
-	 * StringUtils.indexOfAny(null, *)            = -1
-	 * StringUtils.indexOfAny("", *)              = -1
-	 * StringUtils.indexOfAny(*, null)            = -1
-	 * StringUtils.indexOfAny(*, "")              = -1
-	 * StringUtils.indexOfAny("zzabyycdxx", "za") = 0
-	 * StringUtils.indexOfAny("zzabyycdxx", "by") = 3
-	 * StringUtils.indexOfAny("aba", "z")         = -1
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @param searchChars the chars to search for, may be null - * @return the index of any of the chars, -1 if no match or null input - * @since 2.0 - * @since 3.0 Changed signature from indexOfAny(String, String) to - * indexOfAny(CharSequence, String) - */ - public static int indexOfAny(final CharSequence cs, final String searchChars) { - if (isEmpty(cs) || isEmpty(searchChars)) { - return INDEX_NOT_FOUND; - } - return indexOfAny(cs, searchChars.toCharArray()); - } - - /** - *

- * Searches a CharSequence to find the first index of any character not in the - * given set of characters. - *

- * - *

- * A {@code null} CharSequence will return {@code -1}. A {@code null} or zero - * length search array will return {@code -1}. - *

- * - *
-	 * StringUtils.indexOfAnyBut(null, *)                              = -1
-	 * StringUtils.indexOfAnyBut("", *)                                = -1
-	 * StringUtils.indexOfAnyBut(*, null)                              = -1
-	 * StringUtils.indexOfAnyBut(*, [])                                = -1
-	 * StringUtils.indexOfAnyBut("zzabyycdxx", new char[] {'z', 'a'} ) = 3
-	 * StringUtils.indexOfAnyBut("aba", new char[] {'z'} )             = 0
-	 * StringUtils.indexOfAnyBut("aba", new char[] {'a', 'b'} )        = -1
-	 * 
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @param searchChars the chars to search for, may be null - * @return the index of any of the chars, -1 if no match or null input - * @since 2.0 - * @since 3.0 Changed signature from indexOfAnyBut(String, char[]) to - * indexOfAnyBut(CharSequence, char...) - */ - public static int indexOfAnyBut(final CharSequence cs, final char... searchChars) { - if (isEmpty(cs) || searchChars.length == 0) { - return INDEX_NOT_FOUND; - } - final int csLen = cs.length(); - final int csLast = csLen - 1; - final int searchLen = searchChars.length; - final int searchLast = searchLen - 1; - outer: for (int i = 0; i < csLen; i++) { - final char ch = cs.charAt(i); - for (int j = 0; j < searchLen; j++) { - if (searchChars[j] == ch) { - if (i < csLast && j < searchLast && Character.isHighSurrogate(ch)) { - if (searchChars[j + 1] == cs.charAt(i + 1)) { - continue outer; - } - } else { - continue outer; - } - } - } - return i; - } - return INDEX_NOT_FOUND; - } - - /** - *

- * Search a CharSequence to find the first index of any character not in the - * given set of characters. - *

- * - *

- * A {@code null} CharSequence will return {@code -1}. A {@code null} or empty - * search string will return {@code -1}. - *

- * - *
-	 * StringUtils.indexOfAnyBut(null, *)            = -1
-	 * StringUtils.indexOfAnyBut("", *)              = -1
-	 * StringUtils.indexOfAnyBut(*, null)            = -1
-	 * StringUtils.indexOfAnyBut(*, "")              = -1
-	 * StringUtils.indexOfAnyBut("zzabyycdxx", "za") = 3
-	 * StringUtils.indexOfAnyBut("zzabyycdxx", "")   = -1
-	 * StringUtils.indexOfAnyBut("aba", "ab")        = -1
-	 * 
- * - * @param seq the CharSequence to check, may be null - * @param searchChars the chars to search for, may be null - * @return the index of any of the chars, -1 if no match or null input - * @since 2.0 - * @since 3.0 Changed signature from indexOfAnyBut(String, String) to - * indexOfAnyBut(CharSequence, CharSequence) - */ - public static int indexOfAnyBut(final CharSequence seq, final CharSequence searchChars) { - if (isEmpty(seq) || isEmpty(searchChars)) { - return INDEX_NOT_FOUND; - } - final int strLen = seq.length(); - for (int i = 0; i < strLen; i++) { - final char ch = seq.charAt(i); - final boolean chFound = CharSequenceUtils.indexOf(searchChars, ch, 0) >= 0; - if (i + 1 < strLen && Character.isHighSurrogate(ch)) { - final char ch2 = seq.charAt(i + 1); - if (chFound && CharSequenceUtils.indexOf(searchChars, ch2, 0) < 0) { - return i; - } - } else if (!chFound) { - return i; - } - } - return INDEX_NOT_FOUND; - } - - /** - *

- * Compares all CharSequences in an array and returns the index at which the - * CharSequences begin to differ. - *

- * - *

- * For example, {@code indexOfDifference(new String[] {"i am a machine", "i am a - * robot"}) -> 7} - *

- * - *
-	 * StringUtils.indexOfDifference(null) = -1
-	 * StringUtils.indexOfDifference(new String[] {}) = -1
-	 * StringUtils.indexOfDifference(new String[] {"abc"}) = -1
-	 * StringUtils.indexOfDifference(new String[] {null, null}) = -1
-	 * StringUtils.indexOfDifference(new String[] {"", ""}) = -1
-	 * StringUtils.indexOfDifference(new String[] {"", null}) = 0
-	 * StringUtils.indexOfDifference(new String[] {"abc", null, null}) = 0
-	 * StringUtils.indexOfDifference(new String[] {null, null, "abc"}) = 0
-	 * StringUtils.indexOfDifference(new String[] {"", "abc"}) = 0
-	 * StringUtils.indexOfDifference(new String[] {"abc", ""}) = 0
-	 * StringUtils.indexOfDifference(new String[] {"abc", "abc"}) = -1
-	 * StringUtils.indexOfDifference(new String[] {"abc", "a"}) = 1
-	 * StringUtils.indexOfDifference(new String[] {"ab", "abxyz"}) = 2
-	 * StringUtils.indexOfDifference(new String[] {"abcde", "abxyz"}) = 2
-	 * StringUtils.indexOfDifference(new String[] {"abcde", "xyz"}) = 0
-	 * StringUtils.indexOfDifference(new String[] {"xyz", "abcde"}) = 0
-	 * StringUtils.indexOfDifference(new String[] {"i am a machine", "i am a robot"}) = 7
-	 * 
- * - * @param css array of CharSequences, entries may be null - * @return the index where the strings begin to differ; -1 if they are all equal - * @since 2.4 - * @since 3.0 Changed signature from indexOfDifference(String...) to - * indexOfDifference(CharSequence...) - */ - public static int indexOfDifference(final CharSequence... css) { - if (css.length <= 1) { - return INDEX_NOT_FOUND; - } - boolean anyStringNull = false; - boolean allStringsNull = true; - final int arrayLen = css.length; - int shortestStrLen = Integer.MAX_VALUE; - int longestStrLen = 0; - - // find the min and max string lengths; this avoids checking to make - // sure we are not exceeding the length of the string each time through - // the bottom loop. - for (final CharSequence cs : css) { - if (cs == null) { - anyStringNull = true; - shortestStrLen = 0; - } else { - allStringsNull = false; - shortestStrLen = Math.min(cs.length(), shortestStrLen); - longestStrLen = Math.max(cs.length(), longestStrLen); - } - } - - // handle lists containing all nulls or all empty strings - if (allStringsNull || longestStrLen == 0 && !anyStringNull) { - return INDEX_NOT_FOUND; - } - - // handle lists containing some nulls or some empty strings - if (shortestStrLen == 0) { - return 0; - } - - // find the position with the first difference across all strings - int firstDiff = -1; - for (int stringPos = 0; stringPos < shortestStrLen; stringPos++) { - final char comparisonChar = css[0].charAt(stringPos); - for (int arrayPos = 1; arrayPos < arrayLen; arrayPos++) { - if (css[arrayPos].charAt(stringPos) != comparisonChar) { - firstDiff = stringPos; - break; - } - } - if (firstDiff != -1) { - break; - } - } - - if (firstDiff == -1 && shortestStrLen != longestStrLen) { - // we compared all of the characters up to the length of the - // shortest string and didn't find a match, but the string lengths - // vary, so return the length of the shortest string. - return shortestStrLen; - } - return firstDiff; - } - - /** - *

- * Compares two CharSequences, and returns the index at which the CharSequences - * begin to differ. - *

- * - *

- * For example, {@code indexOfDifference("i am a machine", "i am a robot") -> 7} - *

- * - *
-	 * StringUtils.indexOfDifference(null, null) = -1
-	 * StringUtils.indexOfDifference("", "") = -1
-	 * StringUtils.indexOfDifference("", "abc") = 0
-	 * StringUtils.indexOfDifference("abc", "") = 0
-	 * StringUtils.indexOfDifference("abc", "abc") = -1
-	 * StringUtils.indexOfDifference("ab", "abxyz") = 2
-	 * StringUtils.indexOfDifference("abcde", "abxyz") = 2
-	 * StringUtils.indexOfDifference("abcde", "xyz") = 0
-	 * 
- * - * @param cs1 the first CharSequence, may be null - * @param cs2 the second CharSequence, may be null - * @return the index where cs1 and cs2 begin to differ; -1 if they are equal - * @since 2.0 - * @since 3.0 Changed signature from indexOfDifference(String, String) to - * indexOfDifference(CharSequence, CharSequence) - */ - public static int indexOfDifference(final CharSequence cs1, final CharSequence cs2) { - if (cs1 == cs2) { - return INDEX_NOT_FOUND; - } - if (cs1 == null || cs2 == null) { - return 0; - } - int i; - for (i = 0; i < cs1.length() && i < cs2.length(); ++i) { - if (cs1.charAt(i) != cs2.charAt(i)) { - break; - } - } - if (i < cs2.length() || i < cs1.length()) { - return i; - } - return INDEX_NOT_FOUND; - } - - /** - *

- * Case in-sensitive find of the first index within a CharSequence. - *

- * - *

- * A {@code null} CharSequence will return {@code -1}. A negative start position - * is treated as zero. An empty ("") search CharSequence always matches. A start - * position greater than the string length only matches an empty search - * CharSequence. - *

- * - *
-	 * StringUtils.indexOfIgnoreCase(null, *)          = -1
-	 * StringUtils.indexOfIgnoreCase(*, null)          = -1
-	 * StringUtils.indexOfIgnoreCase("", "")           = 0
-	 * StringUtils.indexOfIgnoreCase("aabaabaa", "a")  = 0
-	 * StringUtils.indexOfIgnoreCase("aabaabaa", "b")  = 2
-	 * StringUtils.indexOfIgnoreCase("aabaabaa", "ab") = 1
-	 * 
- * - * @param str the CharSequence to check, may be null - * @param searchStr the CharSequence to find, may be null - * @return the first index of the search CharSequence, -1 if no match or - * {@code null} string input - * @since 2.5 - * @since 3.0 Changed signature from indexOfIgnoreCase(String, String) to - * indexOfIgnoreCase(CharSequence, CharSequence) - */ - public static int indexOfIgnoreCase(final CharSequence str, final CharSequence searchStr) { - return indexOfIgnoreCase(str, searchStr, 0); - } - - /** - *

- * Case in-sensitive find of the first index within a CharSequence from the - * specified position. - *

- * - *

- * A {@code null} CharSequence will return {@code -1}. A negative start position - * is treated as zero. An empty ("") search CharSequence always matches. A start - * position greater than the string length only matches an empty search - * CharSequence. - *

- * - *
-	 * StringUtils.indexOfIgnoreCase(null, *, *)          = -1
-	 * StringUtils.indexOfIgnoreCase(*, null, *)          = -1
-	 * StringUtils.indexOfIgnoreCase("", "", 0)           = 0
-	 * StringUtils.indexOfIgnoreCase("aabaabaa", "A", 0)  = 0
-	 * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 0)  = 2
-	 * StringUtils.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
-	 * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 3)  = 5
-	 * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 9)  = -1
-	 * StringUtils.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
-	 * StringUtils.indexOfIgnoreCase("aabaabaa", "", 2)   = 2
-	 * StringUtils.indexOfIgnoreCase("abc", "", 9)        = -1
-	 * 
- * - * @param str the CharSequence to check, may be null - * @param searchStr the CharSequence to find, may be null - * @param startPos the start position, negative treated as zero - * @return the first index of the search CharSequence (always ≥ startPos), -1 - * if no match or {@code null} string input - * @since 2.5 - * @since 3.0 Changed signature from indexOfIgnoreCase(String, String, int) to - * indexOfIgnoreCase(CharSequence, CharSequence, int) - */ - public static int indexOfIgnoreCase(final CharSequence str, final CharSequence searchStr, int startPos) { - if (str == null || searchStr == null) { - return INDEX_NOT_FOUND; - } - if (startPos < 0) { - startPos = 0; - } - final int endLimit = str.length() - searchStr.length() + 1; - if (startPos > endLimit) { - return INDEX_NOT_FOUND; - } - if (searchStr.length() == 0) { - return startPos; - } - for (int i = startPos; i < endLimit; i++) { - if (CharSequenceUtils.regionMatches(str, true, i, searchStr, 0, searchStr.length())) { - return i; - } - } - return INDEX_NOT_FOUND; - } - - /** - *

- * Checks if all of the CharSequences are empty (""), null or whitespace only. - *

- * - *

- * Whitespace is defined by {@link Character#isWhitespace(char)}. - *

- * - *
-	 * StringUtils.isAllBlank(null)             = true
-	 * StringUtils.isAllBlank(null, "foo")      = false
-	 * StringUtils.isAllBlank(null, null)       = true
-	 * StringUtils.isAllBlank("", "bar")        = false
-	 * StringUtils.isAllBlank("bob", "")        = false
-	 * StringUtils.isAllBlank("  bob  ", null)  = false
-	 * StringUtils.isAllBlank(" ", "bar")       = false
-	 * StringUtils.isAllBlank("foo", "bar")     = false
-	 * StringUtils.isAllBlank(new String[] {})  = true
-	 * 
- * - * @param css the CharSequences to check, may be null or empty - * @return {@code true} if all of the CharSequences are empty or null or - * whitespace only - * @since 3.6 - */ - public static boolean isAllBlank(final CharSequence... css) { - if (css.length == 0) { - return true; - } - for (final CharSequence cs : css) { - if (isNotBlank(cs)) { - return false; - } - } - return true; - } - - /** - *

- * Checks if all of the CharSequences are empty ("") or null. - *

- * - *
-	 * StringUtils.isAllEmpty(null)             = true
-	 * StringUtils.isAllEmpty(null, "")         = true
-	 * StringUtils.isAllEmpty(new String[] {})  = true
-	 * StringUtils.isAllEmpty(null, "foo")      = false
-	 * StringUtils.isAllEmpty("", "bar")        = false
-	 * StringUtils.isAllEmpty("bob", "")        = false
-	 * StringUtils.isAllEmpty("  bob  ", null)  = false
-	 * StringUtils.isAllEmpty(" ", "bar")       = false
-	 * StringUtils.isAllEmpty("foo", "bar")     = false
-	 * 
- * - * @param css the CharSequences to check, may be null or empty - * @return {@code true} if all of the CharSequences are empty or null - * @since 3.6 - */ - public static boolean isAllEmpty(final CharSequence... css) { - if (css.length == 0) { - return true; - } - for (final CharSequence cs : css) { - if (isNotEmpty(cs)) { - return false; - } - } - return true; - } - - /** - *

- * Checks if the CharSequence contains only lowercase characters. - *

- * - *

- * {@code null} will return {@code false}. An empty CharSequence (length()=0) - * will return {@code false}. - *

- * - *
-	 * StringUtils.isAllLowerCase(null)   = false
-	 * StringUtils.isAllLowerCase("")     = false
-	 * StringUtils.isAllLowerCase("  ")   = false
-	 * StringUtils.isAllLowerCase("abc")  = true
-	 * StringUtils.isAllLowerCase("abC")  = false
-	 * StringUtils.isAllLowerCase("ab c") = false
-	 * StringUtils.isAllLowerCase("ab1c") = false
-	 * StringUtils.isAllLowerCase("ab/c") = false
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @return {@code true} if only contains lowercase characters, and is non-null - * @since 2.5 - * @since 3.0 Changed signature from isAllLowerCase(String) to - * isAllLowerCase(CharSequence) - */ - public static boolean isAllLowerCase(final CharSequence cs) { - if (isEmpty(cs)) { - return false; - } - final int sz = cs.length(); - for (int i = 0; i < sz; i++) { - if (!Character.isLowerCase(cs.charAt(i))) { - return false; - } - } - return true; - } - - /** - *

- * Checks if the CharSequence contains only uppercase characters. - *

- * - *

- * {@code null} will return {@code false}. An empty String (length()=0) will - * return {@code false}. - *

- * - *
-	 * StringUtils.isAllUpperCase(null)   = false
-	 * StringUtils.isAllUpperCase("")     = false
-	 * StringUtils.isAllUpperCase("  ")   = false
-	 * StringUtils.isAllUpperCase("ABC")  = true
-	 * StringUtils.isAllUpperCase("aBC")  = false
-	 * StringUtils.isAllUpperCase("A C")  = false
-	 * StringUtils.isAllUpperCase("A1C")  = false
-	 * StringUtils.isAllUpperCase("A/C")  = false
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @return {@code true} if only contains uppercase characters, and is non-null - * @since 2.5 - * @since 3.0 Changed signature from isAllUpperCase(String) to - * isAllUpperCase(CharSequence) - */ - public static boolean isAllUpperCase(final CharSequence cs) { - if (isEmpty(cs)) { - return false; - } - final int sz = cs.length(); - for (int i = 0; i < sz; i++) { - if (!Character.isUpperCase(cs.charAt(i))) { - return false; - } - } - return true; - } - - /** - *

- * Checks if the CharSequence contains only Unicode letters. - *

- * - *

- * {@code null} will return {@code false}. An empty CharSequence (length()=0) - * will return {@code false}. - *

- * - *
-	 * StringUtils.isAlpha(null)   = false
-	 * StringUtils.isAlpha("")     = false
-	 * StringUtils.isAlpha("  ")   = false
-	 * StringUtils.isAlpha("abc")  = true
-	 * StringUtils.isAlpha("ab2c") = false
-	 * StringUtils.isAlpha("ab-c") = false
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @return {@code true} if only contains letters, and is non-null - * @since 3.0 Changed signature from isAlpha(String) to isAlpha(CharSequence) - * @since 3.0 Changed "" to return false and not true - */ - public static boolean isAlpha(final CharSequence cs) { - if (isEmpty(cs)) { - return false; - } - final int sz = cs.length(); - for (int i = 0; i < sz; i++) { - if (!Character.isLetter(cs.charAt(i))) { - return false; - } - } - return true; - } - - /** - *

- * Checks if the CharSequence contains only Unicode letters or digits. - *

- * - *

- * {@code null} will return {@code false}. An empty CharSequence (length()=0) - * will return {@code false}. - *

- * - *
-	 * StringUtils.isAlphanumeric(null)   = false
-	 * StringUtils.isAlphanumeric("")     = false
-	 * StringUtils.isAlphanumeric("  ")   = false
-	 * StringUtils.isAlphanumeric("abc")  = true
-	 * StringUtils.isAlphanumeric("ab c") = false
-	 * StringUtils.isAlphanumeric("ab2c") = true
-	 * StringUtils.isAlphanumeric("ab-c") = false
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @return {@code true} if only contains letters or digits, and is non-null - * @since 3.0 Changed signature from isAlphanumeric(String) to - * isAlphanumeric(CharSequence) - * @since 3.0 Changed "" to return false and not true - */ - public static boolean isAlphanumeric(final CharSequence cs) { - if (isEmpty(cs)) { - return false; - } - final int sz = cs.length(); - for (int i = 0; i < sz; i++) { - if (!Character.isLetterOrDigit(cs.charAt(i))) { - return false; - } - } - return true; - } - - /** - *

- * Checks if the CharSequence contains only Unicode letters, digits or space - * ({@code ' '}). - *

- * - *

- * {@code null} will return {@code false}. An empty CharSequence (length()=0) - * will return {@code true}. - *

- * - *
-	 * StringUtils.isAlphanumericSpace(null)   = false
-	 * StringUtils.isAlphanumericSpace("")     = true
-	 * StringUtils.isAlphanumericSpace("  ")   = true
-	 * StringUtils.isAlphanumericSpace("abc")  = true
-	 * StringUtils.isAlphanumericSpace("ab c") = true
-	 * StringUtils.isAlphanumericSpace("ab2c") = true
-	 * StringUtils.isAlphanumericSpace("ab-c") = false
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @return {@code true} if only contains letters, digits or space, and is - * non-null - * @since 3.0 Changed signature from isAlphanumericSpace(String) to - * isAlphanumericSpace(CharSequence) - */ - public static boolean isAlphanumericSpace(final CharSequence cs) { - if (cs == null) { - return false; - } - final int sz = cs.length(); - for (int i = 0; i < sz; i++) { - final char nowChar = cs.charAt(i); - if (nowChar != ' ' && !Character.isLetterOrDigit(nowChar)) { - return false; - } - } - return true; - } - - /** - *

- * Checks if the CharSequence contains only Unicode letters and space (' '). - *

- * - *

- * {@code null} will return {@code false} An empty CharSequence (length()=0) - * will return {@code true}. - *

- * - *
-	 * StringUtils.isAlphaSpace(null)   = false
-	 * StringUtils.isAlphaSpace("")     = true
-	 * StringUtils.isAlphaSpace("  ")   = true
-	 * StringUtils.isAlphaSpace("abc")  = true
-	 * StringUtils.isAlphaSpace("ab c") = true
-	 * StringUtils.isAlphaSpace("ab2c") = false
-	 * StringUtils.isAlphaSpace("ab-c") = false
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @return {@code true} if only contains letters and space, and is non-null - * @since 3.0 Changed signature from isAlphaSpace(String) to - * isAlphaSpace(CharSequence) - */ - public static boolean isAlphaSpace(final CharSequence cs) { - if (cs == null) { - return false; - } - final int sz = cs.length(); - for (int i = 0; i < sz; i++) { - final char nowChar = cs.charAt(i); - if (nowChar != ' ' && !Character.isLetter(nowChar)) { - return false; - } - } - return true; - } - - /** - *

- * Checks if any of the CharSequences are empty ("") or null or whitespace only. - *

- * - *

- * Whitespace is defined by {@link Character#isWhitespace(char)}. - *

- * - *
-	 * StringUtils.isAnyBlank((String) null)    = true
-	 * StringUtils.isAnyBlank((String[]) null)  = false
-	 * StringUtils.isAnyBlank(null, "foo")      = true
-	 * StringUtils.isAnyBlank(null, null)       = true
-	 * StringUtils.isAnyBlank("", "bar")        = true
-	 * StringUtils.isAnyBlank("bob", "")        = true
-	 * StringUtils.isAnyBlank("  bob  ", null)  = true
-	 * StringUtils.isAnyBlank(" ", "bar")       = true
-	 * StringUtils.isAnyBlank(new String[] {})  = false
-	 * StringUtils.isAnyBlank(new String[]{""}) = true
-	 * StringUtils.isAnyBlank("foo", "bar")     = false
-	 * 
- * - * @param css the CharSequences to check, may be null or empty - * @return {@code true} if any of the CharSequences are empty or null or - * whitespace only - * @since 3.2 - */ - public static boolean isAnyBlank(final CharSequence... css) { - if (css.length == 0) { - return false; - } - for (final CharSequence cs : css) { - if (isBlank(cs)) { - return true; - } - } - return false; - } - - /** - *

- * Checks if any of the CharSequences are empty ("") or null. - *

- * - *
-	 * StringUtils.isAnyEmpty((String) null)    = true
-	 * StringUtils.isAnyEmpty((String[]) null)  = false
-	 * StringUtils.isAnyEmpty(null, "foo")      = true
-	 * StringUtils.isAnyEmpty("", "bar")        = true
-	 * StringUtils.isAnyEmpty("bob", "")        = true
-	 * StringUtils.isAnyEmpty("  bob  ", null)  = true
-	 * StringUtils.isAnyEmpty(" ", "bar")       = false
-	 * StringUtils.isAnyEmpty("foo", "bar")     = false
-	 * StringUtils.isAnyEmpty(new String[]{})   = false
-	 * StringUtils.isAnyEmpty(new String[]{""}) = true
-	 * 
- * - * @param css the CharSequences to check, may be null or empty - * @return {@code true} if any of the CharSequences are empty or null - * @since 3.2 - */ - public static boolean isAnyEmpty(final CharSequence... css) { - if (css.length == 0) { - return false; - } - for (final CharSequence cs : css) { - if (isEmpty(cs)) { - return true; - } - } - return false; - } - - /** - *

- * Checks if the CharSequence contains only ASCII printable characters. - *

- * - *

- * {@code null} will return {@code false}. An empty CharSequence (length()=0) - * will return {@code true}. - *

- * - *
-	 * StringUtils.isAsciiPrintable(null)     = false
-	 * StringUtils.isAsciiPrintable("")       = true
-	 * StringUtils.isAsciiPrintable(" ")      = true
-	 * StringUtils.isAsciiPrintable("Ceki")   = true
-	 * StringUtils.isAsciiPrintable("ab2c")   = true
-	 * StringUtils.isAsciiPrintable("!ab-c~") = true
-	 * StringUtils.isAsciiPrintable("\u0020") = true
-	 * StringUtils.isAsciiPrintable("\u0021") = true
-	 * StringUtils.isAsciiPrintable("\u007e") = true
-	 * StringUtils.isAsciiPrintable("\u007f") = false
-	 * StringUtils.isAsciiPrintable("Ceki G\u00fclc\u00fc") = false
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @return {@code true} if every character is in the range 32 thru 126 - * @since 2.1 - * @since 3.0 Changed signature from isAsciiPrintable(String) to - * isAsciiPrintable(CharSequence) - */ - public static boolean isAsciiPrintable(final CharSequence cs) { - if (cs == null) { - return false; - } - final int sz = cs.length(); - for (int i = 0; i < sz; i++) { - if (!CharUtils.isAsciiPrintable(cs.charAt(i))) { - return false; - } - } - return true; - } - - /** - *

- * Checks if a CharSequence is empty (""), null or whitespace only. - *

- * - *

- * Whitespace is defined by {@link Character#isWhitespace(char)}. - *

- * - *
-	 * StringUtils.isBlank(null)      = true
-	 * StringUtils.isBlank("")        = true
-	 * StringUtils.isBlank(" ")       = true
-	 * StringUtils.isBlank("bob")     = false
-	 * StringUtils.isBlank("  bob  ") = false
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @return {@code true} if the CharSequence is null, empty or whitespace only - * @since 2.0 - * @since 3.0 Changed signature from isBlank(String) to isBlank(CharSequence) - */ - public static boolean isBlank(final CharSequence cs) { - final int strLen = length(cs); - if (strLen == 0) { - return true; - } - for (int i = 0; i < strLen; i++) { - if (!Character.isWhitespace(cs.charAt(i))) { - return false; - } - } - return true; - } - - /** - *

- * Checks if a CharSequence is empty ("") or null. - *

- * - *
-	 * StringUtils.isEmpty(null)      = true
-	 * StringUtils.isEmpty("")        = true
-	 * StringUtils.isEmpty(" ")       = false
-	 * StringUtils.isEmpty("bob")     = false
-	 * StringUtils.isEmpty("  bob  ") = false
-	 * 
- * - *

- * NOTE: This method changed in Lang version 2.0. It no longer trims the - * CharSequence. That functionality is available in isBlank(). - *

- * - * @param cs the CharSequence to check, may be null - * @return {@code true} if the CharSequence is empty or null - * @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence) - */ - public static boolean isEmpty(final CharSequence cs) { - return cs == null || cs.length() == 0; - } - - /** - *

- * Checks if the CharSequence contains mixed casing of both uppercase and - * lowercase characters. - *

- * - *

- * {@code null} will return {@code false}. An empty CharSequence - * ({@code length()=0}) will return {@code false}. - *

- * - *
-	 * StringUtils.isMixedCase(null)    = false
-	 * StringUtils.isMixedCase("")      = false
-	 * StringUtils.isMixedCase("ABC")   = false
-	 * StringUtils.isMixedCase("abc")   = false
-	 * StringUtils.isMixedCase("aBc")   = true
-	 * StringUtils.isMixedCase("A c")   = true
-	 * StringUtils.isMixedCase("A1c")   = true
-	 * StringUtils.isMixedCase("a/C")   = true
-	 * StringUtils.isMixedCase("aC\t")  = true
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @return {@code true} if the CharSequence contains both uppercase and - * lowercase characters - * @since 3.5 - */ - public static boolean isMixedCase(final CharSequence cs) { - if (isEmpty(cs) || cs.length() == 1) { - return false; - } - boolean containsUppercase = false; - boolean containsLowercase = false; - final int sz = cs.length(); - for (int i = 0; i < sz; i++) { - if (containsUppercase && containsLowercase) { - return true; - } else if (Character.isUpperCase(cs.charAt(i))) { - containsUppercase = true; - } else if (Character.isLowerCase(cs.charAt(i))) { - containsLowercase = true; - } - } - return containsUppercase && containsLowercase; - } - - /** - *

- * Checks if none of the CharSequences are empty (""), null or whitespace only. - *

- * - *

- * Whitespace is defined by {@link Character#isWhitespace(char)}. - *

- * - *
-	 * StringUtils.isNoneBlank((String) null)    = false
-	 * StringUtils.isNoneBlank((String[]) null)  = true
-	 * StringUtils.isNoneBlank(null, "foo")      = false
-	 * StringUtils.isNoneBlank(null, null)       = false
-	 * StringUtils.isNoneBlank("", "bar")        = false
-	 * StringUtils.isNoneBlank("bob", "")        = false
-	 * StringUtils.isNoneBlank("  bob  ", null)  = false
-	 * StringUtils.isNoneBlank(" ", "bar")       = false
-	 * StringUtils.isNoneBlank(new String[] {})  = true
-	 * StringUtils.isNoneBlank(new String[]{""}) = false
-	 * StringUtils.isNoneBlank("foo", "bar")     = true
-	 * 
- * - * @param css the CharSequences to check, may be null or empty - * @return {@code true} if none of the CharSequences are empty or null or - * whitespace only - * @since 3.2 - */ - public static boolean isNoneBlank(final CharSequence... css) { - return !isAnyBlank(css); - } - - /** - *

- * Checks if none of the CharSequences are empty ("") or null. - *

- * - *
-	 * StringUtils.isNoneEmpty((String) null)    = false
-	 * StringUtils.isNoneEmpty((String[]) null)  = true
-	 * StringUtils.isNoneEmpty(null, "foo")      = false
-	 * StringUtils.isNoneEmpty("", "bar")        = false
-	 * StringUtils.isNoneEmpty("bob", "")        = false
-	 * StringUtils.isNoneEmpty("  bob  ", null)  = false
-	 * StringUtils.isNoneEmpty(new String[] {})  = true
-	 * StringUtils.isNoneEmpty(new String[]{""}) = false
-	 * StringUtils.isNoneEmpty(" ", "bar")       = true
-	 * StringUtils.isNoneEmpty("foo", "bar")     = true
-	 * 
- * - * @param css the CharSequences to check, may be null or empty - * @return {@code true} if none of the CharSequences are empty or null - * @since 3.2 - */ - public static boolean isNoneEmpty(final CharSequence... css) { - return !isAnyEmpty(css); - } - - /** - *

- * Checks if a CharSequence is not empty (""), not null and not whitespace only. - *

- * - *

- * Whitespace is defined by {@link Character#isWhitespace(char)}. - *

- * - *
-	 * StringUtils.isNotBlank(null)      = false
-	 * StringUtils.isNotBlank("")        = false
-	 * StringUtils.isNotBlank(" ")       = false
-	 * StringUtils.isNotBlank("bob")     = true
-	 * StringUtils.isNotBlank("  bob  ") = true
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @return {@code true} if the CharSequence is not empty and not null and not - * whitespace only - * @since 2.0 - * @since 3.0 Changed signature from isNotBlank(String) to - * isNotBlank(CharSequence) - */ - public static boolean isNotBlank(final CharSequence cs) { - return !isBlank(cs); - } - - /** - *

- * Checks if a CharSequence is not empty ("") and not null. - *

- * - *
-	 * StringUtils.isNotEmpty(null)      = false
-	 * StringUtils.isNotEmpty("")        = false
-	 * StringUtils.isNotEmpty(" ")       = true
-	 * StringUtils.isNotEmpty("bob")     = true
-	 * StringUtils.isNotEmpty("  bob  ") = true
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @return {@code true} if the CharSequence is not empty and not null - * @since 3.0 Changed signature from isNotEmpty(String) to - * isNotEmpty(CharSequence) - */ - public static boolean isNotEmpty(final CharSequence cs) { - return !isEmpty(cs); - } - - /** - *

- * Checks if the CharSequence contains only Unicode digits. A decimal point is - * not a Unicode digit and returns false. - *

- * - *

- * {@code null} will return {@code false}. An empty CharSequence (length()=0) - * will return {@code false}. - *

- * - *

- * Note that the method does not allow for a leading sign, either positive or - * negative. Also, if a String passes the numeric test, it may still generate a - * NumberFormatException when parsed by Integer.parseInt or Long.parseLong, e.g. - * if the value is outside the range for int or long respectively. - *

- * - *
-	 * StringUtils.isNumeric(null)   = false
-	 * StringUtils.isNumeric("")     = false
-	 * StringUtils.isNumeric("  ")   = false
-	 * StringUtils.isNumeric("123")  = true
-	 * StringUtils.isNumeric("\u0967\u0968\u0969")  = true
-	 * StringUtils.isNumeric("12 3") = false
-	 * StringUtils.isNumeric("ab2c") = false
-	 * StringUtils.isNumeric("12-3") = false
-	 * StringUtils.isNumeric("12.3") = false
-	 * StringUtils.isNumeric("-123") = false
-	 * StringUtils.isNumeric("+123") = false
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @return {@code true} if only contains digits, and is non-null - * @since 3.0 Changed signature from isNumeric(String) to - * isNumeric(CharSequence) - * @since 3.0 Changed "" to return false and not true - */ - public static boolean isNumeric(final CharSequence cs) { - if (isEmpty(cs)) { - return false; - } - final int sz = cs.length(); - for (int i = 0; i < sz; i++) { - if (!Character.isDigit(cs.charAt(i))) { - return false; - } - } - return true; - } - - /** - *

- * Checks if the CharSequence contains only Unicode digits or space - * ({@code ' '}). A decimal point is not a Unicode digit and returns false. - *

- * - *

- * {@code null} will return {@code false}. An empty CharSequence (length()=0) - * will return {@code true}. - *

- * - *
-	 * StringUtils.isNumericSpace(null)   = false
-	 * StringUtils.isNumericSpace("")     = true
-	 * StringUtils.isNumericSpace("  ")   = true
-	 * StringUtils.isNumericSpace("123")  = true
-	 * StringUtils.isNumericSpace("12 3") = true
-	 * StringUtils.isNumericSpace("\u0967\u0968\u0969")  = true
-	 * StringUtils.isNumericSpace("\u0967\u0968 \u0969")  = true
-	 * StringUtils.isNumericSpace("ab2c") = false
-	 * StringUtils.isNumericSpace("12-3") = false
-	 * StringUtils.isNumericSpace("12.3") = false
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @return {@code true} if only contains digits or space, and is non-null - * @since 3.0 Changed signature from isNumericSpace(String) to - * isNumericSpace(CharSequence) - */ - public static boolean isNumericSpace(final CharSequence cs) { - if (cs == null) { - return false; - } - final int sz = cs.length(); - for (int i = 0; i < sz; i++) { - final char nowChar = cs.charAt(i); - if (nowChar != ' ' && !Character.isDigit(nowChar)) { - return false; - } - } - return true; - } - - /** - *

- * Checks if the CharSequence contains only whitespace. - *

- * - *

- * Whitespace is defined by {@link Character#isWhitespace(char)}. - *

- * - *

- * {@code null} will return {@code false}. An empty CharSequence (length()=0) - * will return {@code true}. - *

- * - *
-	 * StringUtils.isWhitespace(null)   = false
-	 * StringUtils.isWhitespace("")     = true
-	 * StringUtils.isWhitespace("  ")   = true
-	 * StringUtils.isWhitespace("abc")  = false
-	 * StringUtils.isWhitespace("ab2c") = false
-	 * StringUtils.isWhitespace("ab-c") = false
-	 * 
- * - * @param cs the CharSequence to check, may be null - * @return {@code true} if only contains whitespace, and is non-null - * @since 2.0 - * @since 3.0 Changed signature from isWhitespace(String) to - * isWhitespace(CharSequence) - */ - public static boolean isWhitespace(final CharSequence cs) { - if (cs == null) { - return false; - } - final int sz = cs.length(); - for (int i = 0; i < sz; i++) { - if (!Character.isWhitespace(cs.charAt(i))) { - return false; - } - } - return true; - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)              = null
-	 * StringUtils.join([], *)                = ""
-	 * StringUtils.join([null], *)            = ""
-	 * StringUtils.join([false, false], ';')  = "false;false"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param delimiter the separator character to use - * @return the joined String, {@code null} if null array input - * @since 3.12.0 - */ - public static String join(final boolean[] array, final char delimiter) { - if (array == null) { - return null; - } - return join(array, delimiter, 0, array.length); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)                   = null
-	 * StringUtils.join([], *)                     = ""
-	 * StringUtils.join([null], *)                 = ""
-	 * StringUtils.join([true, false, true], ';')  = "true;false;true"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param delimiter the separator character to use - * @param startIndex the first index to start joining from. It is an error to - * pass in a start index past the end of the array - * @param endIndex the index to stop joining from (exclusive). It is an error - * to pass in an end index past the end of the array - * @return the joined String, {@code null} if null array input - * @since 3.12.0 - */ - public static String join(final boolean[] array, final char delimiter, final int startIndex, final int endIndex) { - if (array == null) { - return null; - } - if (endIndex - startIndex <= 0) { - return EMPTY; - } - final StringJoiner joiner = newStringJoiner(delimiter); - for (int i = startIndex; i < endIndex; i++) { - joiner.add(String.valueOf(array[i])); - } - return joiner.toString(); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)               = null
-	 * StringUtils.join([], *)                 = ""
-	 * StringUtils.join([null], *)             = ""
-	 * StringUtils.join([1, 2, 3], ';')  = "1;2;3"
-	 * StringUtils.join([1, 2, 3], null) = "123"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param delimiter the separator character to use - * @return the joined String, {@code null} if null array input - * @since 3.2 - */ - public static String join(final byte[] array, final char delimiter) { - if (array == null) { - return null; - } - return join(array, delimiter, 0, array.length); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)               = null
-	 * StringUtils.join([], *)                 = ""
-	 * StringUtils.join([null], *)             = ""
-	 * StringUtils.join([1, 2, 3], ';')  = "1;2;3"
-	 * StringUtils.join([1, 2, 3], null) = "123"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param delimiter the separator character to use - * @param startIndex the first index to start joining from. It is an error to - * pass in a start index past the end of the array - * @param endIndex the index to stop joining from (exclusive). It is an error - * to pass in an end index past the end of the array - * @return the joined String, {@code null} if null array input - * @since 3.2 - */ - public static String join(final byte[] array, final char delimiter, final int startIndex, final int endIndex) { - if (array == null) { - return null; - } - if (endIndex - startIndex <= 0) { - return EMPTY; - } - final StringJoiner joiner = newStringJoiner(delimiter); - for (int i = startIndex; i < endIndex; i++) { - joiner.add(String.valueOf(array[i])); - } - return joiner.toString(); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)               = null
-	 * StringUtils.join([], *)                 = ""
-	 * StringUtils.join([null], *)             = ""
-	 * StringUtils.join([1, 2, 3], ';')  = "1;2;3"
-	 * StringUtils.join([1, 2, 3], null) = "123"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param delimiter the separator character to use - * @return the joined String, {@code null} if null array input - * @since 3.2 - */ - public static String join(final char[] array, final char delimiter) { - if (array == null) { - return null; - } - return join(array, delimiter, 0, array.length); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)               = null
-	 * StringUtils.join([], *)                 = ""
-	 * StringUtils.join([null], *)             = ""
-	 * StringUtils.join([1, 2, 3], ';')  = "1;2;3"
-	 * StringUtils.join([1, 2, 3], null) = "123"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param delimiter the separator character to use - * @param startIndex the first index to start joining from. It is an error to - * pass in a start index past the end of the array - * @param endIndex the index to stop joining from (exclusive). It is an error - * to pass in an end index past the end of the array - * @return the joined String, {@code null} if null array input - * @since 3.2 - */ - public static String join(final char[] array, final char delimiter, final int startIndex, final int endIndex) { - if (array == null) { - return null; - } - if (endIndex - startIndex <= 0) { - return EMPTY; - } - final StringJoiner joiner = newStringJoiner(delimiter); - for (int i = startIndex; i < endIndex; i++) { - joiner.add(String.valueOf(array[i])); - } - return joiner.toString(); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)               = null
-	 * StringUtils.join([], *)                 = ""
-	 * StringUtils.join([null], *)             = ""
-	 * StringUtils.join([1, 2, 3], ';')  = "1;2;3"
-	 * StringUtils.join([1, 2, 3], null) = "123"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param delimiter the separator character to use - * @return the joined String, {@code null} if null array input - * @since 3.2 - */ - public static String join(final double[] array, final char delimiter) { - if (array == null) { - return null; - } - return join(array, delimiter, 0, array.length); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)               = null
-	 * StringUtils.join([], *)                 = ""
-	 * StringUtils.join([null], *)             = ""
-	 * StringUtils.join([1, 2, 3], ';')  = "1;2;3"
-	 * StringUtils.join([1, 2, 3], null) = "123"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param delimiter the separator character to use - * @param startIndex the first index to start joining from. It is an error to - * pass in a start index past the end of the array - * @param endIndex the index to stop joining from (exclusive). It is an error - * to pass in an end index past the end of the array - * @return the joined String, {@code null} if null array input - * @since 3.2 - */ - public static String join(final double[] array, final char delimiter, final int startIndex, final int endIndex) { - if (array == null) { - return null; - } - if (endIndex - startIndex <= 0) { - return EMPTY; - } - final StringJoiner joiner = newStringJoiner(delimiter); - for (int i = startIndex; i < endIndex; i++) { - joiner.add(String.valueOf(array[i])); - } - return joiner.toString(); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)               = null
-	 * StringUtils.join([], *)                 = ""
-	 * StringUtils.join([null], *)             = ""
-	 * StringUtils.join([1, 2, 3], ';')  = "1;2;3"
-	 * StringUtils.join([1, 2, 3], null) = "123"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param delimiter the separator character to use - * @return the joined String, {@code null} if null array input - * @since 3.2 - */ - public static String join(final float[] array, final char delimiter) { - if (array == null) { - return null; - } - return join(array, delimiter, 0, array.length); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)               = null
-	 * StringUtils.join([], *)                 = ""
-	 * StringUtils.join([null], *)             = ""
-	 * StringUtils.join([1, 2, 3], ';')  = "1;2;3"
-	 * StringUtils.join([1, 2, 3], null) = "123"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param delimiter the separator character to use - * @param startIndex the first index to start joining from. It is an error to - * pass in a start index past the end of the array - * @param endIndex the index to stop joining from (exclusive). It is an error - * to pass in an end index past the end of the array - * @return the joined String, {@code null} if null array input - * @since 3.2 - */ - public static String join(final float[] array, final char delimiter, final int startIndex, final int endIndex) { - if (array == null) { - return null; - } - if (endIndex - startIndex <= 0) { - return EMPTY; - } - final StringJoiner joiner = newStringJoiner(delimiter); - for (int i = startIndex; i < endIndex; i++) { - joiner.add(String.valueOf(array[i])); - } - return joiner.toString(); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)               = null
-	 * StringUtils.join([], *)                 = ""
-	 * StringUtils.join([null], *)             = ""
-	 * StringUtils.join([1, 2, 3], ';')  = "1;2;3"
-	 * StringUtils.join([1, 2, 3], null) = "123"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param separator the separator character to use - * @return the joined String, {@code null} if null array input - * @since 3.2 - */ - public static String join(final int[] array, final char separator) { - if (array == null) { - return null; - } - return join(array, separator, 0, array.length); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)               = null
-	 * StringUtils.join([], *)                 = ""
-	 * StringUtils.join([null], *)             = ""
-	 * StringUtils.join([1, 2, 3], ';')  = "1;2;3"
-	 * StringUtils.join([1, 2, 3], null) = "123"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param delimiter the separator character to use - * @param startIndex the first index to start joining from. It is an error to - * pass in a start index past the end of the array - * @param endIndex the index to stop joining from (exclusive). It is an error - * to pass in an end index past the end of the array - * @return the joined String, {@code null} if null array input - * @since 3.2 - */ - public static String join(final int[] array, final char delimiter, final int startIndex, final int endIndex) { - if (array == null) { - return null; - } - if (endIndex - startIndex <= 0) { - return EMPTY; - } - final StringJoiner joiner = newStringJoiner(delimiter); - for (int i = startIndex; i < endIndex; i++) { - joiner.add(String.valueOf(array[i])); - } - return joiner.toString(); - } - - /** - *

- * Joins the elements of the provided {@code Iterable} into a single String - * containing the provided elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the iteration are represented by empty strings. - *

- * - *

- * See the examples here: {@link #join(Object[],char)}. - *

- * - * @param iterable the {@code Iterable} providing the values to join together, - * may be null - * @param separator the separator character to use - * @return the joined String, {@code null} if null iterator input - * @since 2.3 - */ - public static String join(final Iterable iterable, final char separator) { - if (iterable == null) { - return null; - } - return join(iterable.iterator(), separator); - } - - /** - *

- * Joins the elements of the provided {@code Iterable} into a single String - * containing the provided elements. - *

- * - *

- * No delimiter is added before or after the list. A {@code null} separator is - * the same as an empty String (""). - *

- * - *

- * See the examples here: {@link #join(Object[],String)}. - *

- * - * @param iterable the {@code Iterable} providing the values to join together, - * may be null - * @param separator the separator character to use, null treated as "" - * @return the joined String, {@code null} if null iterator input - * @since 2.3 - */ - public static String join(final Iterable iterable, final String separator) { - if (iterable == null) { - return null; - } - return join(iterable.iterator(), separator); - } - - /** - *

- * Joins the elements of the provided {@code Iterator} into a single String - * containing the provided elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the iteration are represented by empty strings. - *

- * - *

- * See the examples here: {@link #join(Object[],char)}. - *

- * - * @param iterator the {@code Iterator} of values to join together, may be null - * @param separator the separator character to use - * @return the joined String, {@code null} if null iterator input - * @since 2.0 - */ - public static String join(final Iterator iterator, final char separator) { - - // handle null, zero and one elements before building a buffer - if (iterator == null) { - return null; - } - if (!iterator.hasNext()) { - return EMPTY; - } - final Object first = iterator.next(); - if (!iterator.hasNext()) { - return toStringOrEmpty(first); - } - - // two or more elements - final StringBuilder buf = new StringBuilder(STRING_BUILDER_SIZE); // Java default is 16, probably too small - if (first != null) { - buf.append(first); - } - - while (iterator.hasNext()) { - buf.append(separator); - final Object obj = iterator.next(); - if (obj != null) { - buf.append(obj); - } - } - - return buf.toString(); - } - - /** - *

- * Joins the elements of the provided {@code Iterator} into a single String - * containing the provided elements. - *

- * - *

- * No delimiter is added before or after the list. A {@code null} separator is - * the same as an empty String (""). - *

- * - *

- * See the examples here: {@link #join(Object[],String)}. - *

- * - * @param iterator the {@code Iterator} of values to join together, may be null - * @param separator the separator character to use, null treated as "" - * @return the joined String, {@code null} if null iterator input - */ - public static String join(final Iterator iterator, final String separator) { - - // handle null, zero and one elements before building a buffer - if (iterator == null) { - return null; - } - if (!iterator.hasNext()) { - return EMPTY; - } - final Object first = iterator.next(); - if (!iterator.hasNext()) { - return Objects.toString(first, ""); - } - - // two or more elements - final StringBuilder buf = new StringBuilder(STRING_BUILDER_SIZE); // Java default is 16, probably too small - if (first != null) { - buf.append(first); - } - - while (iterator.hasNext()) { - if (separator != null) { - buf.append(separator); - } - final Object obj = iterator.next(); - if (obj != null) { - buf.append(obj); - } - } - return buf.toString(); - } - - /** - *

- * Joins the elements of the provided {@code List} into a single String - * containing the provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)               = null
-	 * StringUtils.join([], *)                 = ""
-	 * StringUtils.join([null], *)             = ""
-	 * StringUtils.join(["a", "b", "c"], ';')  = "a;b;c"
-	 * StringUtils.join(["a", "b", "c"], null) = "abc"
-	 * StringUtils.join([null, "", "a"], ';')  = ";;a"
-	 * 
- * - * @param list the {@code List} of values to join together, may be null - * @param separator the separator character to use - * @param startIndex the first index to start joining from. It is an error to - * pass in a start index past the end of the list - * @param endIndex the index to stop joining from (exclusive). It is an error - * to pass in an end index past the end of the list - * @return the joined String, {@code null} if null list input - * @since 3.8 - */ - public static String join(final List list, final char separator, final int startIndex, final int endIndex) { - if (list == null) { - return null; - } - final int noOfItems = endIndex - startIndex; - if (noOfItems <= 0) { - return EMPTY; - } - final List subList = list.subList(startIndex, endIndex); - return join(subList.iterator(), separator); - } - - /** - *

- * Joins the elements of the provided {@code List} into a single String - * containing the provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)               = null
-	 * StringUtils.join([], *)                 = ""
-	 * StringUtils.join([null], *)             = ""
-	 * StringUtils.join(["a", "b", "c"], ';')  = "a;b;c"
-	 * StringUtils.join(["a", "b", "c"], null) = "abc"
-	 * StringUtils.join([null, "", "a"], ';')  = ";;a"
-	 * 
- * - * @param list the {@code List} of values to join together, may be null - * @param separator the separator character to use - * @param startIndex the first index to start joining from. It is an error to - * pass in a start index past the end of the list - * @param endIndex the index to stop joining from (exclusive). It is an error - * to pass in an end index past the end of the list - * @return the joined String, {@code null} if null list input - * @since 3.8 - */ - public static String join(final List list, final String separator, final int startIndex, final int endIndex) { - if (list == null) { - return null; - } - final int noOfItems = endIndex - startIndex; - if (noOfItems <= 0) { - return EMPTY; - } - final List subList = list.subList(startIndex, endIndex); - return join(subList.iterator(), separator); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)               = null
-	 * StringUtils.join([], *)                 = ""
-	 * StringUtils.join([null], *)             = ""
-	 * StringUtils.join([1, 2, 3], ';')  = "1;2;3"
-	 * StringUtils.join([1, 2, 3], null) = "123"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param separator the separator character to use - * @return the joined String, {@code null} if null array input - * @since 3.2 - */ - public static String join(final long[] array, final char separator) { - if (array == null) { - return null; - } - return join(array, separator, 0, array.length); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)               = null
-	 * StringUtils.join([], *)                 = ""
-	 * StringUtils.join([null], *)             = ""
-	 * StringUtils.join([1, 2, 3], ';')  = "1;2;3"
-	 * StringUtils.join([1, 2, 3], null) = "123"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param delimiter the separator character to use - * @param startIndex the first index to start joining from. It is an error to - * pass in a start index past the end of the array - * @param endIndex the index to stop joining from (exclusive). It is an error - * to pass in an end index past the end of the array - * @return the joined String, {@code null} if null array input - * @since 3.2 - */ - public static String join(final long[] array, final char delimiter, final int startIndex, final int endIndex) { - if (array == null) { - return null; - } - if (endIndex - startIndex <= 0) { - return EMPTY; - } - final StringJoiner joiner = newStringJoiner(delimiter); - for (int i = startIndex; i < endIndex; i++) { - joiner.add(String.valueOf(array[i])); - } - return joiner.toString(); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)               = null
-	 * StringUtils.join([], *)                 = ""
-	 * StringUtils.join([null], *)             = ""
-	 * StringUtils.join(["a", "b", "c"], ';')  = "a;b;c"
-	 * StringUtils.join(["a", "b", "c"], null) = "abc"
-	 * StringUtils.join([null, "", "a"], ';')  = ";;a"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param delimiter the separator character to use - * @return the joined String, {@code null} if null array input - * @since 2.0 - */ - public static String join(final Object[] array, final char delimiter) { - if (array == null) { - return null; - } - return join(array, delimiter, 0, array.length); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)               = null
-	 * StringUtils.join([], *)                 = ""
-	 * StringUtils.join([null], *)             = ""
-	 * StringUtils.join(["a", "b", "c"], ';')  = "a;b;c"
-	 * StringUtils.join(["a", "b", "c"], null) = "abc"
-	 * StringUtils.join([null, "", "a"], ';')  = ";;a"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param delimiter the separator character to use - * @param startIndex the first index to start joining from. It is an error to - * pass in a start index past the end of the array - * @param endIndex the index to stop joining from (exclusive). It is an error - * to pass in an end index past the end of the array - * @return the joined String, {@code null} if null array input - * @since 2.0 - */ - public static String join(final Object[] array, final char delimiter, final int startIndex, final int endIndex) { - if (array == null) { - return null; - } - if (endIndex - startIndex <= 0) { - return EMPTY; - } - final StringJoiner joiner = newStringJoiner(delimiter); - for (int i = startIndex; i < endIndex; i++) { - joiner.add(toStringOrEmpty(array[i])); - } - return joiner.toString(); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. A {@code null} separator is - * the same as an empty String (""). Null objects or empty strings within the - * array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)                = null
-	 * StringUtils.join([], *)                  = ""
-	 * StringUtils.join([null], *)              = ""
-	 * StringUtils.join(["a", "b", "c"], "--")  = "a--b--c"
-	 * StringUtils.join(["a", "b", "c"], null)  = "abc"
-	 * StringUtils.join(["a", "b", "c"], "")    = "abc"
-	 * StringUtils.join([null, "", "a"], ',')   = ",,a"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param delimiter the separator character to use, null treated as "" - * @return the joined String, {@code null} if null array input - */ - public static String join(final Object[] array, final String delimiter) { - if (array == null) { - return null; - } - return join(array, delimiter, 0, array.length); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. A {@code null} separator is - * the same as an empty String (""). Null objects or empty strings within the - * array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *, *, *)                = null
-	 * StringUtils.join([], *, *, *)                  = ""
-	 * StringUtils.join([null], *, *, *)              = ""
-	 * StringUtils.join(["a", "b", "c"], "--", 0, 3)  = "a--b--c"
-	 * StringUtils.join(["a", "b", "c"], "--", 1, 3)  = "b--c"
-	 * StringUtils.join(["a", "b", "c"], "--", 2, 3)  = "c"
-	 * StringUtils.join(["a", "b", "c"], "--", 2, 2)  = ""
-	 * StringUtils.join(["a", "b", "c"], null, 0, 3)  = "abc"
-	 * StringUtils.join(["a", "b", "c"], "", 0, 3)    = "abc"
-	 * StringUtils.join([null, "", "a"], ',', 0, 3)   = ",,a"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param delimiter the separator character to use, null treated as "" - * @param startIndex the first index to start joining from. - * @param endIndex the index to stop joining from (exclusive). - * @return the joined String, {@code null} if null array input; or the empty - * string if {@code endIndex - startIndex <= 0}. The number of joined - * entries is given by {@code endIndex - startIndex} - * @throws ArrayIndexOutOfBoundsException ife
- * {@code startIndex < 0} or
- * {@code startIndex >= array.length()} - * or
- * {@code endIndex < 0} or
- * {@code endIndex > array.length()} - */ - public static String join(final Object[] array, final String delimiter, final int startIndex, final int endIndex) { - if (array == null) { - return null; - } - if (endIndex - startIndex <= 0) { - return EMPTY; - } - final StringJoiner joiner = new StringJoiner(toStringOrEmpty(delimiter)); - for (int i = startIndex; i < endIndex; i++) { - joiner.add(toStringOrEmpty(array[i])); - } - return joiner.toString(); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)               = null
-	 * StringUtils.join([], *)                 = ""
-	 * StringUtils.join([null], *)             = ""
-	 * StringUtils.join([1, 2, 3], ';')  = "1;2;3"
-	 * StringUtils.join([1, 2, 3], null) = "123"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param delimiter the separator character to use - * @return the joined String, {@code null} if null array input - * @since 3.2 - */ - public static String join(final short[] array, final char delimiter) { - if (array == null) { - return null; - } - return join(array, delimiter, 0, array.length); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No delimiter is added before or after the list. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null, *)               = null
-	 * StringUtils.join([], *)                 = ""
-	 * StringUtils.join([null], *)             = ""
-	 * StringUtils.join([1, 2, 3], ';')  = "1;2;3"
-	 * StringUtils.join([1, 2, 3], null) = "123"
-	 * 
- * - * @param array the array of values to join together, may be null - * @param delimiter the separator character to use - * @param startIndex the first index to start joining from. It is an error to - * pass in a start index past the end of the array - * @param endIndex the index to stop joining from (exclusive). It is an error - * to pass in an end index past the end of the array - * @return the joined String, {@code null} if null array input - * @since 3.2 - */ - public static String join(final short[] array, final char delimiter, final int startIndex, final int endIndex) { - if (array == null) { - return null; - } - if (endIndex - startIndex <= 0) { - return EMPTY; - } - final StringJoiner joiner = newStringJoiner(delimiter); - for (int i = startIndex; i < endIndex; i++) { - joiner.add(String.valueOf(array[i])); - } - return joiner.toString(); - } - - /** - *

- * Joins the elements of the provided array into a single String containing the - * provided list of elements. - *

- * - *

- * No separator is added to the joined String. Null objects or empty strings - * within the array are represented by empty strings. - *

- * - *
-	 * StringUtils.join(null)            = null
-	 * StringUtils.join([])              = ""
-	 * StringUtils.join([null])          = ""
-	 * StringUtils.join(["a", "b", "c"]) = "abc"
-	 * StringUtils.join([null, "", "a"]) = "a"
-	 * 
- * - * @param the specific type of values to join together - * @param elements the values to join together, may be null - * @return the joined String, {@code null} if null array input - * @since 2.0 - * @since 3.0 Changed signature to use varargs - */ - @SafeVarargs - public static String join(final T... elements) { - return join(elements, null); - } - - /** - *

- * Joins the elements of the provided varargs into a single String containing - * the provided elements. - *

- * - *

- * No delimiter is added before or after the list. {@code null} elements and - * separator are treated as empty Strings (""). - *

- * - *
-	 * StringUtils.joinWith(",", {"a", "b"})        = "a,b"
-	 * StringUtils.joinWith(",", {"a", "b",""})     = "a,b,"
-	 * StringUtils.joinWith(",", {"a", null, "b"})  = "a,,b"
-	 * StringUtils.joinWith(null, {"a", "b"})       = "ab"
-	 * 
- * - * @param delimiter the separator character to use, null treated as "" - * @param array the varargs providing the values to join together. - * {@code null} elements are treated as "" - * @return the joined String. - * @throws java.lang.IllegalArgumentException if a null varargs is provided - * @since 3.5 - */ - public static String joinWith(final String delimiter, final Object... array) { - if (array == null) { - throw new IllegalArgumentException("Object varargs must not be null"); - } - return join(array, delimiter); - } - - /** - *

- * Finds the last index within a CharSequence, handling {@code null}. This - * method uses {@link String#lastIndexOf(String)} if possible. - *

- * - *

- * A {@code null} CharSequence will return {@code -1}. - *

- * - *
-	 * StringUtils.lastIndexOf(null, *)          = -1
-	 * StringUtils.lastIndexOf(*, null)          = -1
-	 * StringUtils.lastIndexOf("", "")           = 0
-	 * StringUtils.lastIndexOf("aabaabaa", "a")  = 7
-	 * StringUtils.lastIndexOf("aabaabaa", "b")  = 5
-	 * StringUtils.lastIndexOf("aabaabaa", "ab") = 4
-	 * StringUtils.lastIndexOf("aabaabaa", "")   = 8
-	 * 
- * - * @param seq the CharSequence to check, may be null - * @param searchSeq the CharSequence to find, may be null - * @return the last index of the search String, -1 if no match or {@code null} - * string input - * @since 2.0 - * @since 3.0 Changed signature from lastIndexOf(String, String) to - * lastIndexOf(CharSequence, CharSequence) - */ - public static int lastIndexOf(final CharSequence seq, final CharSequence searchSeq) { - if (seq == null) { - return INDEX_NOT_FOUND; - } - return CharSequenceUtils.lastIndexOf(seq, searchSeq, seq.length()); - } - - /** - *

- * Finds the last index within a CharSequence, handling {@code null}. This - * method uses {@link String#lastIndexOf(String, int)} if possible. - *

- * - *

- * A {@code null} CharSequence will return {@code -1}. A negative start position - * returns {@code -1}. An empty ("") search CharSequence always matches unless - * the start position is negative. A start position greater than the string - * length searches the whole string. The search starts at the startPos and works - * backwards; matches starting after the start position are ignored. - *

- * - *
-	 * StringUtils.lastIndexOf(null, *, *)          = -1
-	 * StringUtils.lastIndexOf(*, null, *)          = -1
-	 * StringUtils.lastIndexOf("aabaabaa", "a", 8)  = 7
-	 * StringUtils.lastIndexOf("aabaabaa", "b", 8)  = 5
-	 * StringUtils.lastIndexOf("aabaabaa", "ab", 8) = 4
-	 * StringUtils.lastIndexOf("aabaabaa", "b", 9)  = 5
-	 * StringUtils.lastIndexOf("aabaabaa", "b", -1) = -1
-	 * StringUtils.lastIndexOf("aabaabaa", "a", 0)  = 0
-	 * StringUtils.lastIndexOf("aabaabaa", "b", 0)  = -1
-	 * StringUtils.lastIndexOf("aabaabaa", "b", 1)  = -1
-	 * StringUtils.lastIndexOf("aabaabaa", "b", 2)  = 2
-	 * StringUtils.lastIndexOf("aabaabaa", "ba", 2)  = 2
-	 * 
- * - * @param seq the CharSequence to check, may be null - * @param searchSeq the CharSequence to find, may be null - * @param startPos the start position, negative treated as zero - * @return the last index of the search CharSequence (always ≤ startPos), -1 - * if no match or {@code null} string input - * @since 2.0 - * @since 3.0 Changed signature from lastIndexOf(String, String, int) to - * lastIndexOf(CharSequence, CharSequence, int) - */ - public static int lastIndexOf(final CharSequence seq, final CharSequence searchSeq, final int startPos) { - return CharSequenceUtils.lastIndexOf(seq, searchSeq, startPos); - } - - /** - * Returns the index within {@code seq} of the last occurrence of the specified - * character. For values of {@code searchChar} in the range from 0 to 0xFFFF - * (inclusive), the index (in Unicode code units) returned is the largest value - * k such that:
- * - *
-	 * this.charAt(k) == searchChar
-	 * 
- * - *
is true. For other values of {@code searchChar}, it is the - * largest value k such that:
- * - *
-	 * this.codePointAt(k) == searchChar
-	 * 
- * - *
is true. In either case, if no such character occurs in this - * string, then {@code -1} is returned. Furthermore, a {@code null} or empty - * ("") {@code CharSequence} will return {@code -1}. The {@code seq} - * {@code CharSequence} object is searched backwards starting at the last - * character. - * - *
-	 * StringUtils.lastIndexOf(null, *)         = -1
-	 * StringUtils.lastIndexOf("", *)           = -1
-	 * StringUtils.lastIndexOf("aabaabaa", 'a') = 7
-	 * StringUtils.lastIndexOf("aabaabaa", 'b') = 5
-	 * 
- * - * @param seq the {@code CharSequence} to check, may be null - * @param searchChar the character to find - * @return the last index of the search character, -1 if no match or - * {@code null} string input - * @since 2.0 - * @since 3.0 Changed signature from lastIndexOf(String, int) to - * lastIndexOf(CharSequence, int) - * @since 3.6 Updated {@link CharSequenceUtils} call to behave more like - * {@code String} - */ - public static int lastIndexOf(final CharSequence seq, final int searchChar) { - if (isEmpty(seq)) { - return INDEX_NOT_FOUND; - } - return CharSequenceUtils.lastIndexOf(seq, searchChar, seq.length()); - } - - /** - * Returns the index within {@code seq} of the last occurrence of the specified - * character, searching backward starting at the specified index. For values of - * {@code searchChar} in the range from 0 to 0xFFFF (inclusive), the index - * returned is the largest value k such that:
- * - *
-	 * (this.charAt(k) == searchChar) && (k <= startPos)
-	 * 
- * - *
is true. For other values of {@code searchChar}, it is the - * largest value k such that:
- * - *
-	 * (this.codePointAt(k) == searchChar) && (k <= startPos)
-	 * 
- * - *
is true. In either case, if no such character occurs in - * {@code seq} at or before position {@code startPos}, then {@code -1} is - * returned. Furthermore, a {@code null} or empty ("") {@code CharSequence} will - * return {@code -1}. A start position greater than the string length searches - * the whole string. The search starts at the {@code startPos} and works - * backwards; matches starting after the start position are ignored. - * - *

- * All indices are specified in {@code char} values (Unicode code units). - * - *

-	 * StringUtils.lastIndexOf(null, *, *)          = -1
-	 * StringUtils.lastIndexOf("", *,  *)           = -1
-	 * StringUtils.lastIndexOf("aabaabaa", 'b', 8)  = 5
-	 * StringUtils.lastIndexOf("aabaabaa", 'b', 4)  = 2
-	 * StringUtils.lastIndexOf("aabaabaa", 'b', 0)  = -1
-	 * StringUtils.lastIndexOf("aabaabaa", 'b', 9)  = 5
-	 * StringUtils.lastIndexOf("aabaabaa", 'b', -1) = -1
-	 * StringUtils.lastIndexOf("aabaabaa", 'a', 0)  = 0
-	 * 
- * - * @param seq the CharSequence to check, may be null - * @param searchChar the character to find - * @param startPos the start position - * @return the last index of the search character (always ≤ startPos), -1 if - * no match or {@code null} string input - * @since 2.0 - * @since 3.0 Changed signature from lastIndexOf(String, int, int) to - * lastIndexOf(CharSequence, int, int) - */ - public static int lastIndexOf(final CharSequence seq, final int searchChar, final int startPos) { - if (isEmpty(seq)) { - return INDEX_NOT_FOUND; - } - return CharSequenceUtils.lastIndexOf(seq, searchChar, startPos); - } - - /** - *

- * Find the latest index of any substring in a set of potential substrings. - *

- * - *

- * A {@code null} CharSequence will return {@code -1}. A {@code null} search - * array will return {@code -1}. A {@code null} or zero length search array - * entry will be ignored, but a search array containing "" will return the - * length of {@code str} if {@code str} is not null. This method uses - * {@link String#indexOf(String)} if possible - *

- * - *
-	 * StringUtils.lastIndexOfAny(null, *)                    = -1
-	 * StringUtils.lastIndexOfAny(*, null)                    = -1
-	 * StringUtils.lastIndexOfAny(*, [])                      = -1
-	 * StringUtils.lastIndexOfAny(*, [null])                  = -1
-	 * StringUtils.lastIndexOfAny("zzabyycdxx", ["ab", "cd"]) = 6
-	 * StringUtils.lastIndexOfAny("zzabyycdxx", ["cd", "ab"]) = 6
-	 * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn", "op"]) = -1
-	 * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn", "op"]) = -1
-	 * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn", ""])   = 10
-	 * 
- * - * @param str the CharSequence to check, may be null - * @param searchStrs the CharSequences to search for, may be null - * @return the last index of any of the CharSequences, -1 if no match - * @since 3.0 Changed signature from lastIndexOfAny(String, String[]) to - * lastIndexOfAny(CharSequence, CharSequence) - */ - public static int lastIndexOfAny(final CharSequence str, final CharSequence... searchStrs) { - if (str == null || searchStrs == null) { - return INDEX_NOT_FOUND; - } - int ret = INDEX_NOT_FOUND; - int tmp = 0; - for (final CharSequence search : searchStrs) { - if (search == null) { - continue; - } - tmp = CharSequenceUtils.lastIndexOf(str, search, str.length()); - if (tmp > ret) { - ret = tmp; - } - } - return ret; - } - - /** - *

- * Case in-sensitive find of the last index within a CharSequence. - *

- * - *

- * A {@code null} CharSequence will return {@code -1}. A negative start position - * returns {@code -1}. An empty ("") search CharSequence always matches unless - * the start position is negative. A start position greater than the string - * length searches the whole string. - *

- * - *
-	 * StringUtils.lastIndexOfIgnoreCase(null, *)          = -1
-	 * StringUtils.lastIndexOfIgnoreCase(*, null)          = -1
-	 * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A")  = 7
-	 * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B")  = 5
-	 * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "AB") = 4
-	 * 
- * - * @param str the CharSequence to check, may be null - * @param searchStr the CharSequence to find, may be null - * @return the first index of the search CharSequence, -1 if no match or - * {@code null} string input - * @since 2.5 - * @since 3.0 Changed signature from lastIndexOfIgnoreCase(String, String) to - * lastIndexOfIgnoreCase(CharSequence, CharSequence) - */ - public static int lastIndexOfIgnoreCase(final CharSequence str, final CharSequence searchStr) { - if (str == null || searchStr == null) { - return INDEX_NOT_FOUND; - } - return lastIndexOfIgnoreCase(str, searchStr, str.length()); - } - - /** - *

- * Case in-sensitive find of the last index within a CharSequence from the - * specified position. - *

- * - *

- * A {@code null} CharSequence will return {@code -1}. A negative start position - * returns {@code -1}. An empty ("") search CharSequence always matches unless - * the start position is negative. A start position greater than the string - * length searches the whole string. The search starts at the startPos and works - * backwards; matches starting after the start position are ignored. - *

- * - *
-	 * StringUtils.lastIndexOfIgnoreCase(null, *, *)          = -1
-	 * StringUtils.lastIndexOfIgnoreCase(*, null, *)          = -1
-	 * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A", 8)  = 7
-	 * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 8)  = 5
-	 * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "AB", 8) = 4
-	 * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 9)  = 5
-	 * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", -1) = -1
-	 * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A", 0)  = 0
-	 * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 0)  = -1
-	 * 
- * - * @param str the CharSequence to check, may be null - * @param searchStr the CharSequence to find, may be null - * @param startPos the start position - * @return the last index of the search CharSequence (always ≤ startPos), -1 - * if no match or {@code null} input - * @since 2.5 - * @since 3.0 Changed signature from lastIndexOfIgnoreCase(String, String, int) - * to lastIndexOfIgnoreCase(CharSequence, CharSequence, int) - */ - public static int lastIndexOfIgnoreCase(final CharSequence str, final CharSequence searchStr, int startPos) { - if (str == null || searchStr == null) { - return INDEX_NOT_FOUND; - } - final int searchStrLength = searchStr.length(); - final int strLength = str.length(); - if (startPos > strLength - searchStrLength) { - startPos = strLength - searchStrLength; - } - if (startPos < 0) { - return INDEX_NOT_FOUND; - } - if (searchStrLength == 0) { - return startPos; - } - - for (int i = startPos; i >= 0; i--) { - if (CharSequenceUtils.regionMatches(str, true, i, searchStr, 0, searchStrLength)) { - return i; - } - } - return INDEX_NOT_FOUND; - } - - /** - *

- * Finds the n-th last index within a String, handling {@code null}. This method - * uses {@link String#lastIndexOf(String)}. - *

- * - *

- * A {@code null} String will return {@code -1}. - *

- * - *
-	 * StringUtils.lastOrdinalIndexOf(null, *, *)          = -1
-	 * StringUtils.lastOrdinalIndexOf(*, null, *)          = -1
-	 * StringUtils.lastOrdinalIndexOf("", "", *)           = 0
-	 * StringUtils.lastOrdinalIndexOf("aabaabaa", "a", 1)  = 7
-	 * StringUtils.lastOrdinalIndexOf("aabaabaa", "a", 2)  = 6
-	 * StringUtils.lastOrdinalIndexOf("aabaabaa", "b", 1)  = 5
-	 * StringUtils.lastOrdinalIndexOf("aabaabaa", "b", 2)  = 2
-	 * StringUtils.lastOrdinalIndexOf("aabaabaa", "ab", 1) = 4
-	 * StringUtils.lastOrdinalIndexOf("aabaabaa", "ab", 2) = 1
-	 * StringUtils.lastOrdinalIndexOf("aabaabaa", "", 1)   = 8
-	 * StringUtils.lastOrdinalIndexOf("aabaabaa", "", 2)   = 8
-	 * 
- * - *

- * Note that 'tail(CharSequence str, int n)' may be implemented as: - *

- * - *
-	 * str.substring(lastOrdinalIndexOf(str, "\n", n) + 1)
-	 * 
- * - * @param str the CharSequence to check, may be null - * @param searchStr the CharSequence to find, may be null - * @param ordinal the n-th last {@code searchStr} to find - * @return the n-th last index of the search CharSequence, {@code -1} - * ({@code INDEX_NOT_FOUND}) if no match or {@code null} string input - * @since 2.5 - * @since 3.0 Changed signature from lastOrdinalIndexOf(String, String, int) to - * lastOrdinalIndexOf(CharSequence, CharSequence, int) - */ - public static int lastOrdinalIndexOf(final CharSequence str, final CharSequence searchStr, final int ordinal) { - return ordinalIndexOf(str, searchStr, ordinal, true); - } - - /** - *

- * Gets the leftmost {@code len} characters of a String. - *

- * - *

- * If {@code len} characters are not available, or the String is {@code null}, - * the String will be returned without an exception. An empty String is returned - * if len is negative. - *

- * - *
-	 * StringUtils.left(null, *)    = null
-	 * StringUtils.left(*, -ve)     = ""
-	 * StringUtils.left("", *)      = ""
-	 * StringUtils.left("abc", 0)   = ""
-	 * StringUtils.left("abc", 2)   = "ab"
-	 * StringUtils.left("abc", 4)   = "abc"
-	 * 
- * - * @param str the String to get the leftmost characters from, may be null - * @param len the length of the required String - * @return the leftmost characters, {@code null} if null String input - */ - public static String left(final String str, final int len) { - if (str == null) { - return null; - } - if (len < 0) { - return EMPTY; - } - if (str.length() <= len) { - return str; - } - return str.substring(0, len); - } - - /** - *

- * Left pad a String with spaces (' '). - *

- * - *

- * The String is padded to the size of {@code size}. - *

- * - *
-	 * StringUtils.leftPad(null, *)   = null
-	 * StringUtils.leftPad("", 3)     = "   "
-	 * StringUtils.leftPad("bat", 3)  = "bat"
-	 * StringUtils.leftPad("bat", 5)  = "  bat"
-	 * StringUtils.leftPad("bat", 1)  = "bat"
-	 * StringUtils.leftPad("bat", -1) = "bat"
-	 * 
- * - * @param str the String to pad out, may be null - * @param size the size to pad to - * @return left padded String or original String if no padding is necessary, - * {@code null} if null String input - */ - public static String leftPad(final String str, final int size) { - return leftPad(str, size, ' '); - } - - /** - *

- * Left pad a String with a specified character. - *

- * - *

- * Pad to a size of {@code size}. - *

- * - *
-	 * StringUtils.leftPad(null, *, *)     = null
-	 * StringUtils.leftPad("", 3, 'z')     = "zzz"
-	 * StringUtils.leftPad("bat", 3, 'z')  = "bat"
-	 * StringUtils.leftPad("bat", 5, 'z')  = "zzbat"
-	 * StringUtils.leftPad("bat", 1, 'z')  = "bat"
-	 * StringUtils.leftPad("bat", -1, 'z') = "bat"
-	 * 
- * - * @param str the String to pad out, may be null - * @param size the size to pad to - * @param padChar the character to pad with - * @return left padded String or original String if no padding is necessary, - * {@code null} if null String input - * @since 2.0 - */ - public static String leftPad(final String str, final int size, final char padChar) { - if (str == null) { - return null; - } - final int pads = size - str.length(); - if (pads <= 0) { - return str; // returns original String when possible - } - if (pads > PAD_LIMIT) { - return leftPad(str, size, String.valueOf(padChar)); - } - return repeat(padChar, pads).concat(str); - } - - /** - *

- * Left pad a String with a specified String. - *

- * - *

- * Pad to a size of {@code size}. - *

- * - *
-	 * StringUtils.leftPad(null, *, *)      = null
-	 * StringUtils.leftPad("", 3, "z")      = "zzz"
-	 * StringUtils.leftPad("bat", 3, "yz")  = "bat"
-	 * StringUtils.leftPad("bat", 5, "yz")  = "yzbat"
-	 * StringUtils.leftPad("bat", 8, "yz")  = "yzyzybat"
-	 * StringUtils.leftPad("bat", 1, "yz")  = "bat"
-	 * StringUtils.leftPad("bat", -1, "yz") = "bat"
-	 * StringUtils.leftPad("bat", 5, null)  = "  bat"
-	 * StringUtils.leftPad("bat", 5, "")    = "  bat"
-	 * 
- * - * @param str the String to pad out, may be null - * @param size the size to pad to - * @param padStr the String to pad with, null or empty treated as single space - * @return left padded String or original String if no padding is necessary, - * {@code null} if null String input - */ - public static String leftPad(final String str, final int size, String padStr) { - if (str == null) { - return null; - } - if (isEmpty(padStr)) { - padStr = SPACE; - } - final int padLen = padStr.length(); - final int strLen = str.length(); - final int pads = size - strLen; - if (pads <= 0) { - return str; // returns original String when possible - } - if (padLen == 1 && pads <= PAD_LIMIT) { - return leftPad(str, size, padStr.charAt(0)); - } - - if (pads == padLen) { - return padStr.concat(str); - } else if (pads < padLen) { - return padStr.substring(0, pads).concat(str); - } else { - final char[] padding = new char[pads]; - final char[] padChars = padStr.toCharArray(); - for (int i = 0; i < pads; i++) { - padding[i] = padChars[i % padLen]; - } - return new String(padding).concat(str); - } - } - - /** - * Gets a CharSequence length or {@code 0} if the CharSequence is {@code null}. - * - * @param cs a CharSequence or {@code null} - * @return CharSequence length or {@code 0} if the CharSequence is {@code null}. - * @since 2.4 - * @since 3.0 Changed signature from length(String) to length(CharSequence) - */ - public static int length(final CharSequence cs) { - return cs == null ? 0 : cs.length(); - } - - /** - *

- * Converts a String to lower case as per {@link String#toLowerCase()}. - *

- * - *

- * A {@code null} input String returns {@code null}. - *

- * - *
-	 * StringUtils.lowerCase(null)  = null
-	 * StringUtils.lowerCase("")    = ""
-	 * StringUtils.lowerCase("aBc") = "abc"
-	 * 
- * - *

- * Note: As described in the documentation for - * {@link String#toLowerCase()}, the result of this method is affected by the - * current locale. For platform-independent case transformations, the method - * {@link #lowerCase(String, Locale)} should be used with a specific locale - * (e.g. {@link Locale#ENGLISH}). - *

- * - * @param str the String to lower case, may be null - * @return the lower cased String, {@code null} if null String input - */ - public static String lowerCase(final String str) { - if (str == null) { - return null; - } - return str.toLowerCase(); - } - - /** - *

- * Converts a String to lower case as per {@link String#toLowerCase(Locale)}. - *

- * - *

- * A {@code null} input String returns {@code null}. - *

- * - *
-	 * StringUtils.lowerCase(null, Locale.ENGLISH)  = null
-	 * StringUtils.lowerCase("", Locale.ENGLISH)    = ""
-	 * StringUtils.lowerCase("aBc", Locale.ENGLISH) = "abc"
-	 * 
- * - * @param str the String to lower case, may be null - * @param locale the locale that defines the case transformation rules, must not - * be null - * @return the lower cased String, {@code null} if null String input - * @since 2.5 - */ - public static String lowerCase(final String str, final Locale locale) { - if (str == null) { - return null; - } - return str.toLowerCase(LocaleUtils.toLocale(locale)); - } - - private static int[] matches(final CharSequence first, final CharSequence second) { - final CharSequence max; - final CharSequence min; - if (first.length() > second.length()) { - max = first; - min = second; - } else { - max = second; - min = first; - } - final int range = Math.max(max.length() / 2 - 1, 0); - final int[] matchIndexes = new int[min.length()]; - Arrays.fill(matchIndexes, -1); - final boolean[] matchFlags = new boolean[max.length()]; - int matches = 0; - for (int mi = 0; mi < min.length(); mi++) { - final char c1 = min.charAt(mi); - for (int xi = Math.max(mi - range, 0), xn = Math.min(mi + range + 1, max.length()); xi < xn; xi++) { - if (!matchFlags[xi] && c1 == max.charAt(xi)) { - matchIndexes[mi] = xi; - matchFlags[xi] = true; - matches++; - break; - } - } - } - final char[] ms1 = new char[matches]; - final char[] ms2 = new char[matches]; - for (int i = 0, si = 0; i < min.length(); i++) { - if (matchIndexes[i] != -1) { - ms1[si] = min.charAt(i); - si++; - } - } - for (int i = 0, si = 0; i < max.length(); i++) { - if (matchFlags[i]) { - ms2[si] = max.charAt(i); - si++; - } - } - int transpositions = 0; - for (int mi = 0; mi < ms1.length; mi++) { - if (ms1[mi] != ms2[mi]) { - transpositions++; - } - } - int prefix = 0; - for (int mi = 0; mi < min.length(); mi++) { - if (first.charAt(mi) == second.charAt(mi)) { - prefix++; - } else { - break; - } - } - return new int[] { matches, transpositions / 2, prefix, max.length() }; - } - - /** - *

- * Gets {@code len} characters from the middle of a String. - *

- * - *

- * If {@code len} characters are not available, the remainder of the String will - * be returned without an exception. If the String is {@code null}, {@code null} - * will be returned. An empty String is returned if len is negative or exceeds - * the length of {@code str}. - *

- * - *
-	 * StringUtils.mid(null, *, *)    = null
-	 * StringUtils.mid(*, *, -ve)     = ""
-	 * StringUtils.mid("", 0, *)      = ""
-	 * StringUtils.mid("abc", 0, 2)   = "ab"
-	 * StringUtils.mid("abc", 0, 4)   = "abc"
-	 * StringUtils.mid("abc", 2, 4)   = "c"
-	 * StringUtils.mid("abc", 4, 2)   = ""
-	 * StringUtils.mid("abc", -2, 2)  = "ab"
-	 * 
- * - * @param str the String to get the characters from, may be null - * @param pos the position to start from, negative treated as zero - * @param len the length of the required String - * @return the middle characters, {@code null} if null String input - */ - public static String mid(final String str, int pos, final int len) { - if (str == null) { - return null; - } - if (len < 0 || pos > str.length()) { - return EMPTY; - } - if (pos < 0) { - pos = 0; - } - if (str.length() <= pos + len) { - return str.substring(pos); - } - return str.substring(pos, pos + len); - } - - private static StringJoiner newStringJoiner(final char delimiter) { - return new StringJoiner(String.valueOf(delimiter)); - } - - /** - *

- * Similar to http://www.w3.org/TR/xpath/#function-normalize - * -space - *

- *

- * The function returns the argument string with whitespace normalized by using - * {@code {@link #trim(String)}} to remove leading and trailing whitespace and - * then replacing sequences of whitespace characters by a single space. - *

- * In XML Whitespace characters are the same as those allowed by the - * S production, which is S ::= - * (#x20 | #x9 | #xD | #xA)+ - *

- * Java's regexp pattern \s defines whitespace as [ \t\n\x0B\f\r] - * - *

- * For reference: - *

- *
    - *
  • \x0B = vertical tab
  • - *
  • \f = #xC = form feed
  • - *
  • #x20 = space
  • - *
  • #x9 = \t
  • - *
  • #xA = \n
  • - *
  • #xD = \r
  • - *
- * - *

- * The difference is that Java's whitespace includes vertical tab and form feed, - * which this functional will also normalize. Additionally {@code {@link - * #trim(String)}} removes control characters (char <= 32) from both ends of - * this String. - *

- * - * @see Pattern - * @see #trim(String) - * @see http://www.w3.org/TR/xpath/#function-normalize-space - * @param str the source String to normalize whitespaces from, may be null - * @return the modified string with whitespace normalized, {@code null} if null - * String input - * - * @since 3.0 - */ - public static String normalizeSpace(final String str) { - // LANG-1020: Improved performance significantly by normalizing manually instead - // of using regex - // See https://github.com/librucha/commons-lang-normalizespaces-benchmark for - // performance test - if (isEmpty(str)) { - return str; - } - final int size = str.length(); - final char[] newChars = new char[size]; - int count = 0; - int whitespacesCount = 0; - boolean startWhitespaces = true; - for (int i = 0; i < size; i++) { - final char actualChar = str.charAt(i); - final boolean isWhitespace = Character.isWhitespace(actualChar); - if (isWhitespace) { - if (whitespacesCount == 0 && !startWhitespaces) { - newChars[count++] = SPACE.charAt(0); - } - whitespacesCount++; - } else { - startWhitespaces = false; - newChars[count++] = (actualChar == 160 ? 32 : actualChar); - whitespacesCount = 0; - } - } - if (startWhitespaces) { - return EMPTY; - } - return new String(newChars, 0, count - (whitespacesCount > 0 ? 1 : 0)).trim(); - } - - /** - *

- * Finds the n-th index within a CharSequence, handling {@code null}. This - * method uses {@link String#indexOf(String)} if possible. - *

- *

- * Note: The code starts looking for a match at the start of the target, - * incrementing the starting index by one after each successful match (unless - * {@code searchStr} is an empty string in which case the position is never - * incremented and {@code 0} is returned immediately). This means that matches - * may overlap. - *

- *

- * A {@code null} CharSequence will return {@code -1}. - *

- * - *
-	 * StringUtils.ordinalIndexOf(null, *, *)          = -1
-	 * StringUtils.ordinalIndexOf(*, null, *)          = -1
-	 * StringUtils.ordinalIndexOf("", "", *)           = 0
-	 * StringUtils.ordinalIndexOf("aabaabaa", "a", 1)  = 0
-	 * StringUtils.ordinalIndexOf("aabaabaa", "a", 2)  = 1
-	 * StringUtils.ordinalIndexOf("aabaabaa", "b", 1)  = 2
-	 * StringUtils.ordinalIndexOf("aabaabaa", "b", 2)  = 5
-	 * StringUtils.ordinalIndexOf("aabaabaa", "ab", 1) = 1
-	 * StringUtils.ordinalIndexOf("aabaabaa", "ab", 2) = 4
-	 * StringUtils.ordinalIndexOf("aabaabaa", "", 1)   = 0
-	 * StringUtils.ordinalIndexOf("aabaabaa", "", 2)   = 0
-	 * 
- * - *

- * Matches may overlap: - *

- * - *
-	 * StringUtils.ordinalIndexOf("ababab", "aba", 1)   = 0
-	 * StringUtils.ordinalIndexOf("ababab", "aba", 2)   = 2
-	 * StringUtils.ordinalIndexOf("ababab", "aba", 3)   = -1
-	 *
-	 * StringUtils.ordinalIndexOf("abababab", "abab", 1) = 0
-	 * StringUtils.ordinalIndexOf("abababab", "abab", 2) = 2
-	 * StringUtils.ordinalIndexOf("abababab", "abab", 3) = 4
-	 * StringUtils.ordinalIndexOf("abababab", "abab", 4) = -1
-	 * 
- * - *

- * Note that 'head(CharSequence str, int n)' may be implemented as: - *

- * - *
-	 * str.substring(0, lastOrdinalIndexOf(str, "\n", n))
-	 * 
- * - * @param str the CharSequence to check, may be null - * @param searchStr the CharSequence to find, may be null - * @param ordinal the n-th {@code searchStr} to find - * @return the n-th index of the search CharSequence, {@code -1} - * ({@code INDEX_NOT_FOUND}) if no match or {@code null} string input - * @since 2.1 - * @since 3.0 Changed signature from ordinalIndexOf(String, String, int) to - * ordinalIndexOf(CharSequence, CharSequence, int) - */ - public static int ordinalIndexOf(final CharSequence str, final CharSequence searchStr, final int ordinal) { - return ordinalIndexOf(str, searchStr, ordinal, false); - } - - /** - *

- * Finds the n-th index within a String, handling {@code null}. This method uses - * {@link String#indexOf(String)} if possible. - *

- *

- * Note that matches may overlap - *

- * - *

- * A {@code null} CharSequence will return {@code -1}. - *

- * - * @param str the CharSequence to check, may be null - * @param searchStr the CharSequence to find, may be null - * @param ordinal the n-th {@code searchStr} to find, overlapping matches are - * allowed. - * @param lastIndex true if lastOrdinalIndexOf() otherwise false if - * ordinalIndexOf() - * @return the n-th index of the search CharSequence, {@code -1} - * ({@code INDEX_NOT_FOUND}) if no match or {@code null} string input - */ - // Shared code between ordinalIndexOf(String, String, int) and - // lastOrdinalIndexOf(String, String, int) - private static int ordinalIndexOf(final CharSequence str, final CharSequence searchStr, final int ordinal, - final boolean lastIndex) { - if (str == null || searchStr == null || ordinal <= 0) { - return INDEX_NOT_FOUND; - } - if (searchStr.length() == 0) { - return lastIndex ? str.length() : 0; - } - int found = 0; - // set the initial index beyond the end of the string - // this is to allow for the initial index decrement/increment - int index = lastIndex ? str.length() : INDEX_NOT_FOUND; - do { - if (lastIndex) { - index = CharSequenceUtils.lastIndexOf(str, searchStr, index - 1); // step backwards thru string - } else { - index = CharSequenceUtils.indexOf(str, searchStr, index + 1); // step forwards through string - } - if (index < 0) { - return index; - } - found++; - } while (found < ordinal); - return index; - } - - /** - *

- * Overlays part of a String with another String. - *

- * - *

- * A {@code null} string input returns {@code null}. A negative index is treated - * as zero. An index greater than the string length is treated as the string - * length. The start index is always the smaller of the two indices. - *

- * - *
-	 * StringUtils.overlay(null, *, *, *)            = null
-	 * StringUtils.overlay("", "abc", 0, 0)          = "abc"
-	 * StringUtils.overlay("abcdef", null, 2, 4)     = "abef"
-	 * StringUtils.overlay("abcdef", "", 2, 4)       = "abef"
-	 * StringUtils.overlay("abcdef", "", 4, 2)       = "abef"
-	 * StringUtils.overlay("abcdef", "zzzz", 2, 4)   = "abzzzzef"
-	 * StringUtils.overlay("abcdef", "zzzz", 4, 2)   = "abzzzzef"
-	 * StringUtils.overlay("abcdef", "zzzz", -1, 4)  = "zzzzef"
-	 * StringUtils.overlay("abcdef", "zzzz", 2, 8)   = "abzzzz"
-	 * StringUtils.overlay("abcdef", "zzzz", -2, -3) = "zzzzabcdef"
-	 * StringUtils.overlay("abcdef", "zzzz", 8, 10)  = "abcdefzzzz"
-	 * 
- * - * @param str the String to do overlaying in, may be null - * @param overlay the String to overlay, may be null - * @param start the position to start overlaying at - * @param end the position to stop overlaying before - * @return overlayed String, {@code null} if null String input - * @since 2.0 - */ - public static String overlay(final String str, String overlay, int start, int end) { - if (str == null) { - return null; - } - if (overlay == null) { - overlay = EMPTY; - } - final int len = str.length(); - if (start < 0) { - start = 0; - } - if (start > len) { - start = len; - } - if (end < 0) { - end = 0; - } - if (end > len) { - end = len; - } - if (start > end) { - final int temp = start; - start = end; - end = temp; - } - return str.substring(0, start) + overlay + str.substring(end); - } - - /** - * Prepends the prefix to the start of the string if the string does not already - * start with any of the prefixes. - * - * @param str The string. - * @param prefix The prefix to prepend to the start of the string. - * @param ignoreCase Indicates whether the compare should ignore case. - * @param prefixes Additional prefixes that are valid (optional). - * - * @return A new String if prefix was prepended, the same string otherwise. - */ - private static String prependIfMissing(final String str, final CharSequence prefix, final boolean ignoreCase, - final CharSequence... prefixes) { - if (str == null || isEmpty(prefix) || startsWith(str, prefix, ignoreCase)) { - return str; - } - if (prefixes.length > 0) { - for (final CharSequence p : prefixes) { - if (startsWith(str, p, ignoreCase)) { - return str; - } - } - } - return prefix.toString() + str; - } - - /** - * Prepends the prefix to the start of the string if the string does not already - * start with any of the prefixes. - * - *
-	 * StringUtils.prependIfMissing(null, null) = null
-	 * StringUtils.prependIfMissing("abc", null) = "abc"
-	 * StringUtils.prependIfMissing("", "xyz") = "xyz"
-	 * StringUtils.prependIfMissing("abc", "xyz") = "xyzabc"
-	 * StringUtils.prependIfMissing("xyzabc", "xyz") = "xyzabc"
-	 * StringUtils.prependIfMissing("XYZabc", "xyz") = "xyzXYZabc"
-	 * 
- *

- * With additional prefixes, - *

- * - *
-	 * StringUtils.prependIfMissing(null, null, null) = null
-	 * StringUtils.prependIfMissing("abc", null, null) = "abc"
-	 * StringUtils.prependIfMissing("", "xyz", null) = "xyz"
-	 * StringUtils.prependIfMissing("abc", "xyz", new CharSequence[]{null}) = "xyzabc"
-	 * StringUtils.prependIfMissing("abc", "xyz", "") = "abc"
-	 * StringUtils.prependIfMissing("abc", "xyz", "mno") = "xyzabc"
-	 * StringUtils.prependIfMissing("xyzabc", "xyz", "mno") = "xyzabc"
-	 * StringUtils.prependIfMissing("mnoabc", "xyz", "mno") = "mnoabc"
-	 * StringUtils.prependIfMissing("XYZabc", "xyz", "mno") = "xyzXYZabc"
-	 * StringUtils.prependIfMissing("MNOabc", "xyz", "mno") = "xyzMNOabc"
-	 * 
- * - * @param str The string. - * @param prefix The prefix to prepend to the start of the string. - * @param prefixes Additional prefixes that are valid. - * - * @return A new String if prefix was prepended, the same string otherwise. - * - * @since 3.2 - */ - public static String prependIfMissing(final String str, final CharSequence prefix, final CharSequence... prefixes) { - return prependIfMissing(str, prefix, false, prefixes); - } - - /** - * Prepends the prefix to the start of the string if the string does not already - * start, case insensitive, with any of the prefixes. - * - *
-	 * StringUtils.prependIfMissingIgnoreCase(null, null) = null
-	 * StringUtils.prependIfMissingIgnoreCase("abc", null) = "abc"
-	 * StringUtils.prependIfMissingIgnoreCase("", "xyz") = "xyz"
-	 * StringUtils.prependIfMissingIgnoreCase("abc", "xyz") = "xyzabc"
-	 * StringUtils.prependIfMissingIgnoreCase("xyzabc", "xyz") = "xyzabc"
-	 * StringUtils.prependIfMissingIgnoreCase("XYZabc", "xyz") = "XYZabc"
-	 * 
- *

- * With additional prefixes, - *

- * - *
-	 * StringUtils.prependIfMissingIgnoreCase(null, null, null) = null
-	 * StringUtils.prependIfMissingIgnoreCase("abc", null, null) = "abc"
-	 * StringUtils.prependIfMissingIgnoreCase("", "xyz", null) = "xyz"
-	 * StringUtils.prependIfMissingIgnoreCase("abc", "xyz", new CharSequence[]{null}) = "xyzabc"
-	 * StringUtils.prependIfMissingIgnoreCase("abc", "xyz", "") = "abc"
-	 * StringUtils.prependIfMissingIgnoreCase("abc", "xyz", "mno") = "xyzabc"
-	 * StringUtils.prependIfMissingIgnoreCase("xyzabc", "xyz", "mno") = "xyzabc"
-	 * StringUtils.prependIfMissingIgnoreCase("mnoabc", "xyz", "mno") = "mnoabc"
-	 * StringUtils.prependIfMissingIgnoreCase("XYZabc", "xyz", "mno") = "XYZabc"
-	 * StringUtils.prependIfMissingIgnoreCase("MNOabc", "xyz", "mno") = "MNOabc"
-	 * 
- * - * @param str The string. - * @param prefix The prefix to prepend to the start of the string. - * @param prefixes Additional prefixes that are valid (optional). - * - * @return A new String if prefix was prepended, the same string otherwise. - * - * @since 3.2 - */ - public static String prependIfMissingIgnoreCase(final String str, final CharSequence prefix, - final CharSequence... prefixes) { - return prependIfMissing(str, prefix, true, prefixes); - } - - /** - *

- * Removes all occurrences of a character from within the source string. - *

- * - *

- * A {@code null} source string will return {@code null}. An empty ("") source - * string will return the empty string. - *

- * - *
-	 * StringUtils.remove(null, *)       = null
-	 * StringUtils.remove("", *)         = ""
-	 * StringUtils.remove("queued", 'u') = "qeed"
-	 * StringUtils.remove("queued", 'z') = "queued"
-	 * 
- * - * @param str the source String to search, may be null - * @param remove the char to search for and remove, may be null - * @return the substring with the char removed if found, {@code null} if null - * String input - * @since 2.1 - */ - public static String remove(final String str, final char remove) { - if (isEmpty(str) || str.indexOf(remove) == INDEX_NOT_FOUND) { - return str; - } - final char[] chars = str.toCharArray(); - int pos = 0; - for (int i = 0; i < chars.length; i++) { - if (chars[i] != remove) { - chars[pos++] = chars[i]; - } - } - return new String(chars, 0, pos); - } - - /** - *

- * Removes all occurrences of a substring from within the source string. - *

- * - *

- * A {@code null} source string will return {@code null}. An empty ("") source - * string will return the empty string. A {@code null} remove string will return - * the source string. An empty ("") remove string will return the source string. - *

- * - *
-	 * StringUtils.remove(null, *)        = null
-	 * StringUtils.remove("", *)          = ""
-	 * StringUtils.remove(*, null)        = *
-	 * StringUtils.remove(*, "")          = *
-	 * StringUtils.remove("queued", "ue") = "qd"
-	 * StringUtils.remove("queued", "zz") = "queued"
-	 * 
- * - * @param str the source String to search, may be null - * @param remove the String to search for and remove, may be null - * @return the substring with the string removed if found, {@code null} if null - * String input - * @since 2.1 - */ - public static String remove(final String str, final String remove) { - if (isEmpty(str) || isEmpty(remove)) { - return str; - } - return replace(str, remove, EMPTY, -1); - } - - /** - *

- * Removes each substring of the text String that matches the given regular - * expression. - *

- * - * This method is a {@code null} safe equivalent to: - *
    - *
  • {@code text.replaceAll(regex, StringUtils.EMPTY)}
  • - *
  • {@code Pattern.compile(regex).matcher(text).replaceAll(StringUtils.EMPTY)}
  • - *
- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *

- * Unlike in the {@link #removePattern(String, String)} method, the - * {@link Pattern#DOTALL} option is NOT automatically added. To use the DOTALL - * option prepend {@code "(?s)"} to the regex. DOTALL is also known as - * single-line mode in Perl. - *

- * - *
-	 * StringUtils.removeAll(null, *)      = null
-	 * StringUtils.removeAll("any", (String) null)  = "any"
-	 * StringUtils.removeAll("any", "")    = "any"
-	 * StringUtils.removeAll("any", ".*")  = ""
-	 * StringUtils.removeAll("any", ".+")  = ""
-	 * StringUtils.removeAll("abc", ".?")  = ""
-	 * StringUtils.removeAll("A<__>\n<__>B", "<.*>")      = "A\nB"
-	 * StringUtils.removeAll("A<__>\n<__>B", "(?s)<.*>")  = "AB"
-	 * StringUtils.removeAll("ABCabc123abc", "[a-z]")     = "ABC123"
-	 * 
- * - * @param text text to remove from, may be null - * @param regex the regular expression to which this string is to be matched - * @return the text with any removes processed, {@code null} if null String - * input - * - * @throws java.util.regex.PatternSyntaxException if the regular expression's - * syntax is invalid - * - * @see #replaceAll(String, String, String) - * @see #removePattern(String, String) - * @see String#replaceAll(String, String) - * @see java.util.regex.Pattern - * @see java.util.regex.Pattern#DOTALL - * @since 3.5 - * - * @deprecated Moved to RegExUtils. - */ - @Deprecated - public static String removeAll(final String text, final String regex) { - return RegExUtils.removeAll(text, regex); - } - - /** - *

- * Removes a substring only if it is at the end of a source string, otherwise - * returns the source string. - *

- * - *

- * A {@code null} source string will return {@code null}. An empty ("") source - * string will return the empty string. A {@code null} search string will return - * the source string. - *

- * - *
-	 * StringUtils.removeEnd(null, *)      = null
-	 * StringUtils.removeEnd("", *)        = ""
-	 * StringUtils.removeEnd(*, null)      = *
-	 * StringUtils.removeEnd("www.domain.com", ".com.")  = "www.domain.com"
-	 * StringUtils.removeEnd("www.domain.com", ".com")   = "www.domain"
-	 * StringUtils.removeEnd("www.domain.com", "domain") = "www.domain.com"
-	 * StringUtils.removeEnd("abc", "")    = "abc"
-	 * 
- * - * @param str the source String to search, may be null - * @param remove the String to search for and remove, may be null - * @return the substring with the string removed if found, {@code null} if null - * String input - * @since 2.1 - */ - public static String removeEnd(final String str, final String remove) { - if (isEmpty(str) || isEmpty(remove)) { - return str; - } - if (str.endsWith(remove)) { - return str.substring(0, str.length() - remove.length()); - } - return str; - } - - /** - *

- * Case insensitive removal of a substring if it is at the end of a source - * string, otherwise returns the source string. - *

- * - *

- * A {@code null} source string will return {@code null}. An empty ("") source - * string will return the empty string. A {@code null} search string will return - * the source string. - *

- * - *
-	 * StringUtils.removeEndIgnoreCase(null, *)      = null
-	 * StringUtils.removeEndIgnoreCase("", *)        = ""
-	 * StringUtils.removeEndIgnoreCase(*, null)      = *
-	 * StringUtils.removeEndIgnoreCase("www.domain.com", ".com.")  = "www.domain.com"
-	 * StringUtils.removeEndIgnoreCase("www.domain.com", ".com")   = "www.domain"
-	 * StringUtils.removeEndIgnoreCase("www.domain.com", "domain") = "www.domain.com"
-	 * StringUtils.removeEndIgnoreCase("abc", "")    = "abc"
-	 * StringUtils.removeEndIgnoreCase("www.domain.com", ".COM") = "www.domain")
-	 * StringUtils.removeEndIgnoreCase("www.domain.COM", ".com") = "www.domain")
-	 * 
- * - * @param str the source String to search, may be null - * @param remove the String to search for (case insensitive) and remove, may be - * null - * @return the substring with the string removed if found, {@code null} if null - * String input - * @since 2.4 - */ - public static String removeEndIgnoreCase(final String str, final String remove) { - if (isEmpty(str) || isEmpty(remove)) { - return str; - } - if (endsWithIgnoreCase(str, remove)) { - return str.substring(0, str.length() - remove.length()); - } - return str; - } - - /** - *

- * Removes the first substring of the text string that matches the given regular - * expression. - *

- * - * This method is a {@code null} safe equivalent to: - *
    - *
  • {@code text.replaceFirst(regex, StringUtils.EMPTY)}
  • - *
  • {@code Pattern.compile(regex).matcher(text).replaceFirst(StringUtils.EMPTY)}
  • - *
- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *

- * The {@link Pattern#DOTALL} option is NOT automatically added. To use the - * DOTALL option prepend {@code "(?s)"} to the regex. DOTALL is also known as - * single-line mode in Perl. - *

- * - *
-	 * StringUtils.removeFirst(null, *)      = null
-	 * StringUtils.removeFirst("any", (String) null)  = "any"
-	 * StringUtils.removeFirst("any", "")    = "any"
-	 * StringUtils.removeFirst("any", ".*")  = ""
-	 * StringUtils.removeFirst("any", ".+")  = ""
-	 * StringUtils.removeFirst("abc", ".?")  = "bc"
-	 * StringUtils.removeFirst("A<__>\n<__>B", "<.*>")      = "A\n<__>B"
-	 * StringUtils.removeFirst("A<__>\n<__>B", "(?s)<.*>")  = "AB"
-	 * StringUtils.removeFirst("ABCabc123", "[a-z]")          = "ABCbc123"
-	 * StringUtils.removeFirst("ABCabc123abc", "[a-z]+")      = "ABC123abc"
-	 * 
- * - * @param text text to remove from, may be null - * @param regex the regular expression to which this string is to be matched - * @return the text with the first replacement processed, {@code null} if null - * String input - * - * @throws java.util.regex.PatternSyntaxException if the regular expression's - * syntax is invalid - * - * @see #replaceFirst(String, String, String) - * @see String#replaceFirst(String, String) - * @see java.util.regex.Pattern - * @see java.util.regex.Pattern#DOTALL - * @since 3.5 - * - * @deprecated Moved to RegExUtils. - */ - @Deprecated - public static String removeFirst(final String text, final String regex) { - return replaceFirst(text, regex, EMPTY); - } - - /** - *

- * Case insensitive removal of all occurrences of a substring from within the - * source string. - *

- * - *

- * A {@code null} source string will return {@code null}. An empty ("") source - * string will return the empty string. A {@code null} remove string will return - * the source string. An empty ("") remove string will return the source string. - *

- * - *
-	 * StringUtils.removeIgnoreCase(null, *)        = null
-	 * StringUtils.removeIgnoreCase("", *)          = ""
-	 * StringUtils.removeIgnoreCase(*, null)        = *
-	 * StringUtils.removeIgnoreCase(*, "")          = *
-	 * StringUtils.removeIgnoreCase("queued", "ue") = "qd"
-	 * StringUtils.removeIgnoreCase("queued", "zz") = "queued"
-	 * StringUtils.removeIgnoreCase("quEUed", "UE") = "qd"
-	 * StringUtils.removeIgnoreCase("queued", "zZ") = "queued"
-	 * 
- * - * @param str the source String to search, may be null - * @param remove the String to search for (case insensitive) and remove, may be - * null - * @return the substring with the string removed if found, {@code null} if null - * String input - * @since 3.5 - */ - public static String removeIgnoreCase(final String str, final String remove) { - return replaceIgnoreCase(str, remove, EMPTY, -1); - } - - /** - *

- * Removes each substring of the source String that matches the given regular - * expression using the DOTALL option. - *

- * - * This call is a {@code null} safe equivalent to: - *
    - *
  • {@code source.replaceAll("(?s)" + regex, StringUtils.EMPTY)}
  • - *
  • {@code Pattern.compile(regex, Pattern.DOTALL).matcher(source).replaceAll(StringUtils.EMPTY)}
  • - *
- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *
-	 * StringUtils.removePattern(null, *)       = null
-	 * StringUtils.removePattern("any", (String) null)   = "any"
-	 * StringUtils.removePattern("A<__>\n<__>B", "<.*>")  = "AB"
-	 * StringUtils.removePattern("ABCabc123", "[a-z]")    = "ABC123"
-	 * 
- * - * @param source the source string - * @param regex the regular expression to which this string is to be matched - * @return The resulting {@code String} - * @see #replacePattern(String, String, String) - * @see String#replaceAll(String, String) - * @see Pattern#DOTALL - * @since 3.2 - * @since 3.5 Changed {@code null} reference passed to this method is a no-op. - * - * @deprecated Moved to RegExUtils. - */ - @Deprecated - public static String removePattern(final String source, final String regex) { - return RegExUtils.removePattern(source, regex); - } - - /** - *

- * Removes a substring only if it is at the beginning of a source string, - * otherwise returns the source string. - *

- * - *

- * A {@code null} source string will return {@code null}. An empty ("") source - * string will return the empty string. A {@code null} search string will return - * the source string. - *

- * - *
-	 * StringUtils.removeStart(null, *)      = null
-	 * StringUtils.removeStart("", *)        = ""
-	 * StringUtils.removeStart(*, null)      = *
-	 * StringUtils.removeStart("www.domain.com", "www.")   = "domain.com"
-	 * StringUtils.removeStart("domain.com", "www.")       = "domain.com"
-	 * StringUtils.removeStart("www.domain.com", "domain") = "www.domain.com"
-	 * StringUtils.removeStart("abc", "")    = "abc"
-	 * 
- * - * @param str the source String to search, may be null - * @param remove the String to search for and remove, may be null - * @return the substring with the string removed if found, {@code null} if null - * String input - * @since 2.1 - */ - public static String removeStart(final String str, final String remove) { - if (isEmpty(str) || isEmpty(remove)) { - return str; - } - if (str.startsWith(remove)) { - return str.substring(remove.length()); - } - return str; - } - - /** - *

- * Case insensitive removal of a substring if it is at the beginning of a source - * string, otherwise returns the source string. - *

- * - *

- * A {@code null} source string will return {@code null}. An empty ("") source - * string will return the empty string. A {@code null} search string will return - * the source string. - *

- * - *
-	 * StringUtils.removeStartIgnoreCase(null, *)      = null
-	 * StringUtils.removeStartIgnoreCase("", *)        = ""
-	 * StringUtils.removeStartIgnoreCase(*, null)      = *
-	 * StringUtils.removeStartIgnoreCase("www.domain.com", "www.")   = "domain.com"
-	 * StringUtils.removeStartIgnoreCase("www.domain.com", "WWW.")   = "domain.com"
-	 * StringUtils.removeStartIgnoreCase("domain.com", "www.")       = "domain.com"
-	 * StringUtils.removeStartIgnoreCase("www.domain.com", "domain") = "www.domain.com"
-	 * StringUtils.removeStartIgnoreCase("abc", "")    = "abc"
-	 * 
- * - * @param str the source String to search, may be null - * @param remove the String to search for (case insensitive) and remove, may be - * null - * @return the substring with the string removed if found, {@code null} if null - * String input - * @since 2.4 - */ - public static String removeStartIgnoreCase(final String str, final String remove) { - if (str != null && startsWithIgnoreCase(str, remove)) { - return str.substring(length(remove)); - } - return str; - } - - /** - *

- * Returns padding using the specified delimiter repeated to a given length. - *

- * - *
-	 * StringUtils.repeat('e', 0)  = ""
-	 * StringUtils.repeat('e', 3)  = "eee"
-	 * StringUtils.repeat('e', -2) = ""
-	 * 
- * - *

- * Note: this method does not support padding with - * Unicode - * Supplementary Characters as they require a pair of {@code char}s to be - * represented. If you are needing to support full I18N of your applications - * consider using {@link #repeat(String, int)} instead. - *

- * - * @param ch character to repeat - * @param repeat number of times to repeat char, negative treated as zero - * @return String with repeated character - * @see #repeat(String, int) - */ - public static String repeat(final char ch, final int repeat) { - if (repeat <= 0) { - return EMPTY; - } - final char[] buf = new char[repeat]; - Arrays.fill(buf, ch); - return new String(buf); - } - - /** - *

- * Repeat a String {@code repeat} times to form a new String. - *

- * - *
-	 * StringUtils.repeat(null, 2) = null
-	 * StringUtils.repeat("", 0)   = ""
-	 * StringUtils.repeat("", 2)   = ""
-	 * StringUtils.repeat("a", 3)  = "aaa"
-	 * StringUtils.repeat("ab", 2) = "abab"
-	 * StringUtils.repeat("a", -2) = ""
-	 * 
- * - * @param str the String to repeat, may be null - * @param repeat number of times to repeat str, negative treated as zero - * @return a new String consisting of the original String repeated, {@code null} - * if null String input - */ - public static String repeat(final String str, final int repeat) { - // Performance tuned for 2.0 (JDK1.4) - if (str == null) { - return null; - } - if (repeat <= 0) { - return EMPTY; - } - final int inputLength = str.length(); - if (repeat == 1 || inputLength == 0) { - return str; - } - if (inputLength == 1 && repeat <= PAD_LIMIT) { - return repeat(str.charAt(0), repeat); - } - - final int outputLength = inputLength * repeat; - switch (inputLength) { - case 1: - return repeat(str.charAt(0), repeat); - case 2: - final char ch0 = str.charAt(0); - final char ch1 = str.charAt(1); - final char[] output2 = new char[outputLength]; - for (int i = repeat * 2 - 2; i >= 0; i--, i--) { - output2[i] = ch0; - output2[i + 1] = ch1; - } - return new String(output2); - default: - final StringBuilder buf = new StringBuilder(outputLength); - for (int i = 0; i < repeat; i++) { - buf.append(str); - } - return buf.toString(); - } - } - - /** - *

- * Repeat a String {@code repeat} times to form a new String, with a String - * separator injected each time. - *

- * - *
-	 * StringUtils.repeat(null, null, 2) = null
-	 * StringUtils.repeat(null, "x", 2)  = null
-	 * StringUtils.repeat("", null, 0)   = ""
-	 * StringUtils.repeat("", "", 2)     = ""
-	 * StringUtils.repeat("", "x", 3)    = "xxx"
-	 * StringUtils.repeat("?", ", ", 3)  = "?, ?, ?"
-	 * 
- * - * @param str the String to repeat, may be null - * @param separator the String to inject, may be null - * @param repeat number of times to repeat str, negative treated as zero - * @return a new String consisting of the original String repeated, {@code null} - * if null String input - * @since 2.5 - */ - public static String repeat(final String str, final String separator, final int repeat) { - if (str == null || separator == null) { - return repeat(str, repeat); - } - // given that repeat(String, int) is quite optimized, better to rely on it than - // try and splice this into it - final String result = repeat(str + separator, repeat); - return removeEnd(result, separator); - } - - /** - *

- * Replaces all occurrences of a String within another String. - *

- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *
-	 * StringUtils.replace(null, *, *)        = null
-	 * StringUtils.replace("", *, *)          = ""
-	 * StringUtils.replace("any", null, *)    = "any"
-	 * StringUtils.replace("any", *, null)    = "any"
-	 * StringUtils.replace("any", "", *)      = "any"
-	 * StringUtils.replace("aba", "a", null)  = "aba"
-	 * StringUtils.replace("aba", "a", "")    = "b"
-	 * StringUtils.replace("aba", "a", "z")   = "zbz"
-	 * 
- * - * @see #replace(String text, String searchString, String replacement, int max) - * @param text text to search and replace in, may be null - * @param searchString the String to search for, may be null - * @param replacement the String to replace it with, may be null - * @return the text with any replacements processed, {@code null} if null String - * input - */ - public static String replace(final String text, final String searchString, final String replacement) { - return replace(text, searchString, replacement, -1); - } - - /** - *

- * Replaces a String with another String inside a larger String, for the first - * {@code max} values of the search String. - *

- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *
-	 * StringUtils.replace(null, *, *, *)         = null
-	 * StringUtils.replace("", *, *, *)           = ""
-	 * StringUtils.replace("any", null, *, *)     = "any"
-	 * StringUtils.replace("any", *, null, *)     = "any"
-	 * StringUtils.replace("any", "", *, *)       = "any"
-	 * StringUtils.replace("any", *, *, 0)        = "any"
-	 * StringUtils.replace("abaa", "a", null, -1) = "abaa"
-	 * StringUtils.replace("abaa", "a", "", -1)   = "b"
-	 * StringUtils.replace("abaa", "a", "z", 0)   = "abaa"
-	 * StringUtils.replace("abaa", "a", "z", 1)   = "zbaa"
-	 * StringUtils.replace("abaa", "a", "z", 2)   = "zbza"
-	 * StringUtils.replace("abaa", "a", "z", -1)  = "zbzz"
-	 * 
- * - * @param text text to search and replace in, may be null - * @param searchString the String to search for, may be null - * @param replacement the String to replace it with, may be null - * @param max maximum number of values to replace, or {@code -1} if no - * maximum - * @return the text with any replacements processed, {@code null} if null String - * input - */ - public static String replace(final String text, final String searchString, final String replacement, - final int max) { - return replace(text, searchString, replacement, max, false); - } - - /** - *

- * Replaces a String with another String inside a larger String, for the first - * {@code max} values of the search String, case sensitively/insensitively based - * on {@code ignoreCase} value. - *

- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *
-	 * StringUtils.replace(null, *, *, *, false)         = null
-	 * StringUtils.replace("", *, *, *, false)           = ""
-	 * StringUtils.replace("any", null, *, *, false)     = "any"
-	 * StringUtils.replace("any", *, null, *, false)     = "any"
-	 * StringUtils.replace("any", "", *, *, false)       = "any"
-	 * StringUtils.replace("any", *, *, 0, false)        = "any"
-	 * StringUtils.replace("abaa", "a", null, -1, false) = "abaa"
-	 * StringUtils.replace("abaa", "a", "", -1, false)   = "b"
-	 * StringUtils.replace("abaa", "a", "z", 0, false)   = "abaa"
-	 * StringUtils.replace("abaa", "A", "z", 1, false)   = "abaa"
-	 * StringUtils.replace("abaa", "A", "z", 1, true)   = "zbaa"
-	 * StringUtils.replace("abAa", "a", "z", 2, true)   = "zbza"
-	 * StringUtils.replace("abAa", "a", "z", -1, true)  = "zbzz"
-	 * 
- * - * @param text text to search and replace in, may be null - * @param searchString the String to search for (case insensitive), may be null - * @param replacement the String to replace it with, may be null - * @param max maximum number of values to replace, or {@code -1} if no - * maximum - * @param ignoreCase if true replace is case insensitive, otherwise case - * sensitive - * @return the text with any replacements processed, {@code null} if null String - * input - */ - private static String replace(final String text, String searchString, final String replacement, int max, - final boolean ignoreCase) { - if (isEmpty(text) || isEmpty(searchString) || replacement == null || max == 0) { - return text; - } - if (ignoreCase) { - searchString = searchString.toLowerCase(); - } - int start = 0; - int end = ignoreCase ? indexOfIgnoreCase(text, searchString, start) : indexOf(text, searchString, start); - if (end == INDEX_NOT_FOUND) { - return text; - } - final int replLength = searchString.length(); - int increase = Math.max(replacement.length() - replLength, 0); - increase *= max < 0 ? 16 : Math.min(max, 64); - final StringBuilder buf = new StringBuilder(text.length() + increase); - while (end != INDEX_NOT_FOUND) { - buf.append(text, start, end).append(replacement); - start = end + replLength; - if (--max == 0) { - break; - } - end = ignoreCase ? indexOfIgnoreCase(text, searchString, start) : indexOf(text, searchString, start); - } - buf.append(text, start, text.length()); - return buf.toString(); - } - - /** - *

- * Replaces each substring of the text String that matches the given regular - * expression with the given replacement. - *

- * - * This method is a {@code null} safe equivalent to: - *
    - *
  • {@code text.replaceAll(regex, replacement)}
  • - *
  • {@code Pattern.compile(regex).matcher(text).replaceAll(replacement)}
  • - *
- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *

- * Unlike in the {@link #replacePattern(String, String, String)} method, the - * {@link Pattern#DOTALL} option is NOT automatically added. To use the DOTALL - * option prepend {@code "(?s)"} to the regex. DOTALL is also known as - * single-line mode in Perl. - *

- * - *
-	 * StringUtils.replaceAll(null, *, *)       = null
-	 * StringUtils.replaceAll("any", (String) null, *)   = "any"
-	 * StringUtils.replaceAll("any", *, null)   = "any"
-	 * StringUtils.replaceAll("", "", "zzz")    = "zzz"
-	 * StringUtils.replaceAll("", ".*", "zzz")  = "zzz"
-	 * StringUtils.replaceAll("", ".+", "zzz")  = ""
-	 * StringUtils.replaceAll("abc", "", "ZZ")  = "ZZaZZbZZcZZ"
-	 * StringUtils.replaceAll("<__>\n<__>", "<.*>", "z")      = "z\nz"
-	 * StringUtils.replaceAll("<__>\n<__>", "(?s)<.*>", "z")  = "z"
-	 * StringUtils.replaceAll("ABCabc123", "[a-z]", "_")       = "ABC___123"
-	 * StringUtils.replaceAll("ABCabc123", "[^A-Z0-9]+", "_")  = "ABC_123"
-	 * StringUtils.replaceAll("ABCabc123", "[^A-Z0-9]+", "")   = "ABC123"
-	 * StringUtils.replaceAll("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum_dolor_sit"
-	 * 
- * - * @param text text to search and replace in, may be null - * @param regex the regular expression to which this string is to be - * matched - * @param replacement the string to be substituted for each match - * @return the text with any replacements processed, {@code null} if null String - * input - * - * @throws java.util.regex.PatternSyntaxException if the regular expression's - * syntax is invalid - * - * @see #replacePattern(String, String, String) - * @see String#replaceAll(String, String) - * @see java.util.regex.Pattern - * @see java.util.regex.Pattern#DOTALL - * @since 3.5 - * - * @deprecated Moved to RegExUtils. - */ - @Deprecated - public static String replaceAll(final String text, final String regex, final String replacement) { - return RegExUtils.replaceAll(text, regex, replacement); - } - - /** - *

- * Replaces all occurrences of a character in a String with another. This is a - * null-safe version of {@link String#replace(char, char)}. - *

- * - *

- * A {@code null} string input returns {@code null}. An empty ("") string input - * returns an empty string. - *

- * - *
-	 * StringUtils.replaceChars(null, *, *)        = null
-	 * StringUtils.replaceChars("", *, *)          = ""
-	 * StringUtils.replaceChars("abcba", 'b', 'y') = "aycya"
-	 * StringUtils.replaceChars("abcba", 'z', 'y') = "abcba"
-	 * 
- * - * @param str String to replace characters in, may be null - * @param searchChar the character to search for, may be null - * @param replaceChar the character to replace, may be null - * @return modified String, {@code null} if null string input - * @since 2.0 - */ - public static String replaceChars(final String str, final char searchChar, final char replaceChar) { - if (str == null) { - return null; - } - return str.replace(searchChar, replaceChar); - } - - /** - *

- * Replaces multiple characters in a String in one go. This method can also be - * used to delete characters. - *

- * - *

- * For example:
- * {@code replaceChars("hello", "ho", "jy") = jelly}. - *

- * - *

- * A {@code null} string input returns {@code null}. An empty ("") string input - * returns an empty string. A null or empty set of search characters returns the - * input string. - *

- * - *

- * The length of the search characters should normally equal the length of the - * replace characters. If the search characters is longer, then the extra search - * characters are deleted. If the search characters is shorter, then the extra - * replace characters are ignored. - *

- * - *
-	 * StringUtils.replaceChars(null, *, *)           = null
-	 * StringUtils.replaceChars("", *, *)             = ""
-	 * StringUtils.replaceChars("abc", null, *)       = "abc"
-	 * StringUtils.replaceChars("abc", "", *)         = "abc"
-	 * StringUtils.replaceChars("abc", "b", null)     = "ac"
-	 * StringUtils.replaceChars("abc", "b", "")       = "ac"
-	 * StringUtils.replaceChars("abcba", "bc", "yz")  = "ayzya"
-	 * StringUtils.replaceChars("abcba", "bc", "y")   = "ayya"
-	 * StringUtils.replaceChars("abcba", "bc", "yzx") = "ayzya"
-	 * 
- * - * @param str String to replace characters in, may be null - * @param searchChars a set of characters to search for, may be null - * @param replaceChars a set of characters to replace, may be null - * @return modified String, {@code null} if null string input - * @since 2.0 - */ - public static String replaceChars(final String str, final String searchChars, String replaceChars) { - if (isEmpty(str) || isEmpty(searchChars)) { - return str; - } - if (replaceChars == null) { - replaceChars = EMPTY; - } - boolean modified = false; - final int replaceCharsLength = replaceChars.length(); - final int strLength = str.length(); - final StringBuilder buf = new StringBuilder(strLength); - for (int i = 0; i < strLength; i++) { - final char ch = str.charAt(i); - final int index = searchChars.indexOf(ch); - if (index >= 0) { - modified = true; - if (index < replaceCharsLength) { - buf.append(replaceChars.charAt(index)); - } - } else { - buf.append(ch); - } - } - if (modified) { - return buf.toString(); - } - return str; - } - - /** - *

- * Replaces all occurrences of Strings within another String. - *

- * - *

- * A {@code null} reference passed to this method is a no-op, or if any "search - * string" or "string to replace" is null, that replace will be ignored. This - * will not repeat. For repeating replaces, call the overloaded method. - *

- * - *
-	 *  StringUtils.replaceEach(null, *, *)        = null
-	 *  StringUtils.replaceEach("", *, *)          = ""
-	 *  StringUtils.replaceEach("aba", null, null) = "aba"
-	 *  StringUtils.replaceEach("aba", new String[0], null) = "aba"
-	 *  StringUtils.replaceEach("aba", null, new String[0]) = "aba"
-	 *  StringUtils.replaceEach("aba", new String[]{"a"}, null)  = "aba"
-	 *  StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""})  = "b"
-	 *  StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"})  = "aba"
-	 *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"})  = "wcte"
-	 *  (example of how it does not repeat)
-	 *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"})  = "dcte"
-	 * 
- * - * @param text text to search and replace in, no-op if null - * @param searchList the Strings to search for, no-op if null - * @param replacementList the Strings to replace them with, no-op if null - * @return the text with any replacements processed, {@code null} if null String - * input - * @throws IllegalArgumentException if the lengths of the arrays are not the - * same (null is ok, and/or size 0) - * @since 2.4 - */ - public static String replaceEach(final String text, final String[] searchList, final String[] replacementList) { - return replaceEach(text, searchList, replacementList, false, 0); - } - - /** - *

- * Replace all occurrences of Strings within another String. This is a private - * recursive helper method for - * {@link #replaceEachRepeatedly(String, String[], String[])} and - * {@link #replaceEach(String, String[], String[])} - *

- * - *

- * A {@code null} reference passed to this method is a no-op, or if any "search - * string" or "string to replace" is null, that replace will be ignored. - *

- * - *
-	 *  StringUtils.replaceEach(null, *, *, *, *) = null
-	 *  StringUtils.replaceEach("", *, *, *, *) = ""
-	 *  StringUtils.replaceEach("aba", null, null, *, *) = "aba"
-	 *  StringUtils.replaceEach("aba", new String[0], null, *, *) = "aba"
-	 *  StringUtils.replaceEach("aba", null, new String[0], *, *) = "aba"
-	 *  StringUtils.replaceEach("aba", new String[]{"a"}, null, *, *) = "aba"
-	 *  StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}, *, >=0) = "b"
-	 *  StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}, *, >=0) = "aba"
-	 *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *, >=0) = "wcte"
-	 *  (example of how it repeats)
-	 *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false, >=0) = "dcte"
-	 *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true, >=2) = "tcte"
-	 *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, *, *) = IllegalStateException
-	 * 
- * - * @param text text to search and replace in, no-op if null - * @param searchList the Strings to search for, no-op if null - * @param replacementList the Strings to replace them with, no-op if null - * @param repeat if true, then replace repeatedly until there are no - * more possible replacements or timeToLive < 0 - * @param timeToLive if less than 0 then there is a circular reference and - * endless loop - * @return the text with any replacements processed, {@code null} if null String - * input - * @throws IllegalStateException if the search is repeating and there is an - * endless loop due to outputs of one being - * inputs to another - * @throws IllegalArgumentException if the lengths of the arrays are not the - * same (null is ok, and/or size 0) - * @since 2.4 - */ - private static String replaceEach(final String text, final String[] searchList, final String[] replacementList, - final boolean repeat, final int timeToLive) { - - // mchyzer Performance note: This creates very few new objects (one major goal) - // let me know if there are performance requests, we can create a harness to - // measure - - // if recursing, this shouldn't be less than 0 - if (timeToLive < 0) { - final Set searchSet = new HashSet<>(Arrays.asList(searchList)); - final Set replacementSet = new HashSet<>(Arrays.asList(replacementList)); - searchSet.retainAll(replacementSet); - if (!searchSet.isEmpty()) { - throw new IllegalStateException("Aborting to protect against StackOverflowError - " - + "output of one loop is the input of another"); - } - } - - if (isEmpty(text) || searchList.length == 0 || replacementList.length == 0 - || (searchList.length > 0 && timeToLive == -1)) { - return text; - } - - final int searchLength = searchList.length; - final int replacementLength = replacementList.length; - - // make sure lengths are ok, these need to be equal - if (searchLength != replacementLength) { - throw new IllegalArgumentException( - "Search and Replace array lengths don't match: " + searchLength + " vs " + replacementLength); - } - - // keep track of which still have matches - final boolean[] noMoreMatchesForReplIndex = new boolean[searchLength]; - - // index on index that the match was found - int textIndex = -1; - int replaceIndex = -1; - int tempIndex = -1; - - // index of replace array that will replace the search string found - // NOTE: logic duplicated below START - for (int i = 0; i < searchLength; i++) { - if (noMoreMatchesForReplIndex[i] || isEmpty(searchList[i]) || replacementList[i] == null) { - continue; - } - tempIndex = text.indexOf(searchList[i]); - - // see if we need to keep searching for this - if (tempIndex == -1) { - noMoreMatchesForReplIndex[i] = true; - } else if (textIndex == -1 || tempIndex < textIndex) { - textIndex = tempIndex; - replaceIndex = i; - } - } - // NOTE: logic mostly below END - - // no search strings found, we are done - if (textIndex == -1) { - return text; - } - - int start = 0; - - // get a good guess on the size of the result buffer so it doesn't have to - // double if it goes over a bit - int increase = 0; - - // count the replacement text elements that are larger than their corresponding - // text being replaced - for (int i = 0; i < searchList.length; i++) { - if (searchList[i] == null || replacementList[i] == null) { - continue; - } - final int greater = replacementList[i].length() - searchList[i].length(); - if (greater > 0) { - increase += 3 * greater; // assume 3 matches - } - } - // have upper-bound at 20% increase, then let Java take over - increase = Math.min(increase, text.length() / 5); - - final StringBuilder buf = new StringBuilder(text.length() + increase); - - while (textIndex != -1) { - - for (int i = start; i < textIndex; i++) { - buf.append(text.charAt(i)); - } - buf.append(replacementList[replaceIndex]); - - start = textIndex + searchList[replaceIndex].length(); - - textIndex = -1; - replaceIndex = -1; - // find the next earliest match - // NOTE: logic mostly duplicated above START - for (int i = 0; i < searchLength; i++) { - if (noMoreMatchesForReplIndex[i] || searchList[i] == null || searchList[i].isEmpty() - || replacementList[i] == null) { - continue; - } - tempIndex = text.indexOf(searchList[i], start); - - // see if we need to keep searching for this - if (tempIndex == -1) { - noMoreMatchesForReplIndex[i] = true; - } else if (textIndex == -1 || tempIndex < textIndex) { - textIndex = tempIndex; - replaceIndex = i; - } - } - // NOTE: logic duplicated above END - - } - final int textLength = text.length(); - for (int i = start; i < textLength; i++) { - buf.append(text.charAt(i)); - } - final String result = buf.toString(); - if (!repeat) { - return result; - } - - return replaceEach(result, searchList, replacementList, repeat, timeToLive - 1); - } - - /** - *

- * Replaces all occurrences of Strings within another String. - *

- * - *

- * A {@code null} reference passed to this method is a no-op, or if any "search - * string" or "string to replace" is null, that replace will be ignored. - *

- * - *
-	 *  StringUtils.replaceEachRepeatedly(null, *, *) = null
-	 *  StringUtils.replaceEachRepeatedly("", *, *) = ""
-	 *  StringUtils.replaceEachRepeatedly("aba", null, null) = "aba"
-	 *  StringUtils.replaceEachRepeatedly("aba", new String[0], null) = "aba"
-	 *  StringUtils.replaceEachRepeatedly("aba", null, new String[0]) = "aba"
-	 *  StringUtils.replaceEachRepeatedly("aba", new String[]{"a"}, null) = "aba"
-	 *  StringUtils.replaceEachRepeatedly("aba", new String[]{"a"}, new String[]{""}) = "b"
-	 *  StringUtils.replaceEachRepeatedly("aba", new String[]{null}, new String[]{"a"}) = "aba"
-	 *  StringUtils.replaceEachRepeatedly("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}) = "wcte"
-	 *  (example of how it repeats)
-	 *  StringUtils.replaceEachRepeatedly("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}) = "tcte"
-	 *  StringUtils.replaceEachRepeatedly("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}) = IllegalStateException
-	 * 
- * - * @param text text to search and replace in, no-op if null - * @param searchList the Strings to search for, no-op if null - * @param replacementList the Strings to replace them with, no-op if null - * @return the text with any replacements processed, {@code null} if null String - * input - * @throws IllegalStateException if the search is repeating and there is an - * endless loop due to outputs of one being - * inputs to another - * @throws IllegalArgumentException if the lengths of the arrays are not the - * same (null is ok, and/or size 0) - * @since 2.4 - */ - public static String replaceEachRepeatedly(final String text, final String[] searchList, - final String[] replacementList) { - // timeToLive should be 0 if not used or nothing to replace, else it's - // the length of the replace array - final int timeToLive = searchList == null ? 0 : searchList.length; - return replaceEach(text, searchList, replacementList, true, timeToLive); - } - - /** - *

- * Replaces the first substring of the text string that matches the given - * regular expression with the given replacement. - *

- * - * This method is a {@code null} safe equivalent to: - *
    - *
  • {@code text.replaceFirst(regex, replacement)}
  • - *
  • {@code Pattern.compile(regex).matcher(text).replaceFirst(replacement)}
  • - *
- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *

- * The {@link Pattern#DOTALL} option is NOT automatically added. To use the - * DOTALL option prepend {@code "(?s)"} to the regex. DOTALL is also known as - * single-line mode in Perl. - *

- * - *
-	 * StringUtils.replaceFirst(null, *, *)       = null
-	 * StringUtils.replaceFirst("any", (String) null, *)   = "any"
-	 * StringUtils.replaceFirst("any", *, null)   = "any"
-	 * StringUtils.replaceFirst("", "", "zzz")    = "zzz"
-	 * StringUtils.replaceFirst("", ".*", "zzz")  = "zzz"
-	 * StringUtils.replaceFirst("", ".+", "zzz")  = ""
-	 * StringUtils.replaceFirst("abc", "", "ZZ")  = "ZZabc"
-	 * StringUtils.replaceFirst("<__>\n<__>", "<.*>", "z")      = "z\n<__>"
-	 * StringUtils.replaceFirst("<__>\n<__>", "(?s)<.*>", "z")  = "z"
-	 * StringUtils.replaceFirst("ABCabc123", "[a-z]", "_")          = "ABC_bc123"
-	 * StringUtils.replaceFirst("ABCabc123abc", "[^A-Z0-9]+", "_")  = "ABC_123abc"
-	 * StringUtils.replaceFirst("ABCabc123abc", "[^A-Z0-9]+", "")   = "ABC123abc"
-	 * StringUtils.replaceFirst("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum  dolor   sit"
-	 * 
- * - * @param text text to search and replace in, may be null - * @param regex the regular expression to which this string is to be - * matched - * @param replacement the string to be substituted for the first match - * @return the text with the first replacement processed, {@code null} if null - * String input - * - * @throws java.util.regex.PatternSyntaxException if the regular expression's - * syntax is invalid - * - * @see String#replaceFirst(String, String) - * @see java.util.regex.Pattern - * @see java.util.regex.Pattern#DOTALL - * @since 3.5 - * - * @deprecated Moved to RegExUtils. - */ - @Deprecated - public static String replaceFirst(final String text, final String regex, final String replacement) { - return RegExUtils.replaceFirst(text, regex, replacement); - } - - /** - *

- * Case insensitively replaces all occurrences of a String within another - * String. - *

- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *
-	 * StringUtils.replaceIgnoreCase(null, *, *)        = null
-	 * StringUtils.replaceIgnoreCase("", *, *)          = ""
-	 * StringUtils.replaceIgnoreCase("any", null, *)    = "any"
-	 * StringUtils.replaceIgnoreCase("any", *, null)    = "any"
-	 * StringUtils.replaceIgnoreCase("any", "", *)      = "any"
-	 * StringUtils.replaceIgnoreCase("aba", "a", null)  = "aba"
-	 * StringUtils.replaceIgnoreCase("abA", "A", "")    = "b"
-	 * StringUtils.replaceIgnoreCase("aba", "A", "z")   = "zbz"
-	 * 
- * - * @see #replaceIgnoreCase(String text, String searchString, String replacement, - * int max) - * @param text text to search and replace in, may be null - * @param searchString the String to search for (case insensitive), may be null - * @param replacement the String to replace it with, may be null - * @return the text with any replacements processed, {@code null} if null String - * input - * @since 3.5 - */ - public static String replaceIgnoreCase(final String text, final String searchString, final String replacement) { - return replaceIgnoreCase(text, searchString, replacement, -1); - } - - /** - *

- * Case insensitively replaces a String with another String inside a larger - * String, for the first {@code max} values of the search String. - *

- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *
-	 * StringUtils.replaceIgnoreCase(null, *, *, *)         = null
-	 * StringUtils.replaceIgnoreCase("", *, *, *)           = ""
-	 * StringUtils.replaceIgnoreCase("any", null, *, *)     = "any"
-	 * StringUtils.replaceIgnoreCase("any", *, null, *)     = "any"
-	 * StringUtils.replaceIgnoreCase("any", "", *, *)       = "any"
-	 * StringUtils.replaceIgnoreCase("any", *, *, 0)        = "any"
-	 * StringUtils.replaceIgnoreCase("abaa", "a", null, -1) = "abaa"
-	 * StringUtils.replaceIgnoreCase("abaa", "a", "", -1)   = "b"
-	 * StringUtils.replaceIgnoreCase("abaa", "a", "z", 0)   = "abaa"
-	 * StringUtils.replaceIgnoreCase("abaa", "A", "z", 1)   = "zbaa"
-	 * StringUtils.replaceIgnoreCase("abAa", "a", "z", 2)   = "zbza"
-	 * StringUtils.replaceIgnoreCase("abAa", "a", "z", -1)  = "zbzz"
-	 * 
- * - * @param text text to search and replace in, may be null - * @param searchString the String to search for (case insensitive), may be null - * @param replacement the String to replace it with, may be null - * @param max maximum number of values to replace, or {@code -1} if no - * maximum - * @return the text with any replacements processed, {@code null} if null String - * input - * @since 3.5 - */ - public static String replaceIgnoreCase(final String text, final String searchString, final String replacement, - final int max) { - return replace(text, searchString, replacement, max, true); - } - - /** - *

- * Replaces a String with another String inside a larger String, once. - *

- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *
-	 * StringUtils.replaceOnce(null, *, *)        = null
-	 * StringUtils.replaceOnce("", *, *)          = ""
-	 * StringUtils.replaceOnce("any", null, *)    = "any"
-	 * StringUtils.replaceOnce("any", *, null)    = "any"
-	 * StringUtils.replaceOnce("any", "", *)      = "any"
-	 * StringUtils.replaceOnce("aba", "a", null)  = "aba"
-	 * StringUtils.replaceOnce("aba", "a", "")    = "ba"
-	 * StringUtils.replaceOnce("aba", "a", "z")   = "zba"
-	 * 
- * - * @see #replace(String text, String searchString, String replacement, int max) - * @param text text to search and replace in, may be null - * @param searchString the String to search for, may be null - * @param replacement the String to replace with, may be null - * @return the text with any replacements processed, {@code null} if null String - * input - */ - public static String replaceOnce(final String text, final String searchString, final String replacement) { - return replace(text, searchString, replacement, 1); - } - - /** - *

- * Case insensitively replaces a String with another String inside a larger - * String, once. - *

- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *
-	 * StringUtils.replaceOnceIgnoreCase(null, *, *)        = null
-	 * StringUtils.replaceOnceIgnoreCase("", *, *)          = ""
-	 * StringUtils.replaceOnceIgnoreCase("any", null, *)    = "any"
-	 * StringUtils.replaceOnceIgnoreCase("any", *, null)    = "any"
-	 * StringUtils.replaceOnceIgnoreCase("any", "", *)      = "any"
-	 * StringUtils.replaceOnceIgnoreCase("aba", "a", null)  = "aba"
-	 * StringUtils.replaceOnceIgnoreCase("aba", "a", "")    = "ba"
-	 * StringUtils.replaceOnceIgnoreCase("aba", "a", "z")   = "zba"
-	 * StringUtils.replaceOnceIgnoreCase("FoOFoofoo", "foo", "") = "Foofoo"
-	 * 
- * - * @see #replaceIgnoreCase(String text, String searchString, String replacement, - * int max) - * @param text text to search and replace in, may be null - * @param searchString the String to search for (case insensitive), may be null - * @param replacement the String to replace with, may be null - * @return the text with any replacements processed, {@code null} if null String - * input - * @since 3.5 - */ - public static String replaceOnceIgnoreCase(final String text, final String searchString, final String replacement) { - return replaceIgnoreCase(text, searchString, replacement, 1); - } - - /** - *

- * Replaces each substring of the source String that matches the given regular - * expression with the given replacement using the {@link Pattern#DOTALL} - * option. DOTALL is also known as single-line mode in Perl. - *

- * - * This call is a {@code null} safe equivalent to: - *
    - *
  • {@code source.replaceAll("(?s)" + regex, replacement)}
  • - *
  • {@code Pattern.compile(regex, Pattern.DOTALL).matcher(source).replaceAll(replacement)}
  • - *
- * - *

- * A {@code null} reference passed to this method is a no-op. - *

- * - *
-	 * StringUtils.replacePattern(null, *, *)       = null
-	 * StringUtils.replacePattern("any", (String) null, *)   = "any"
-	 * StringUtils.replacePattern("any", *, null)   = "any"
-	 * StringUtils.replacePattern("", "", "zzz")    = "zzz"
-	 * StringUtils.replacePattern("", ".*", "zzz")  = "zzz"
-	 * StringUtils.replacePattern("", ".+", "zzz")  = ""
-	 * StringUtils.replacePattern("<__>\n<__>", "<.*>", "z")       = "z"
-	 * StringUtils.replacePattern("ABCabc123", "[a-z]", "_")       = "ABC___123"
-	 * StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "_")  = "ABC_123"
-	 * StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "")   = "ABC123"
-	 * StringUtils.replacePattern("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum_dolor_sit"
-	 * 
- * - * @param source the source string - * @param regex the regular expression to which this string is to be - * matched - * @param replacement the string to be substituted for each match - * @return The resulting {@code String} - * @see #replaceAll(String, String, String) - * @see String#replaceAll(String, String) - * @see Pattern#DOTALL - * @since 3.2 - * @since 3.5 Changed {@code null} reference passed to this method is a no-op. - * - * @deprecated Moved to RegExUtils. - */ - @Deprecated - public static String replacePattern(final String source, final String regex, final String replacement) { - return RegExUtils.replacePattern(source, regex, replacement); - } - - /** - *

- * Reverses a String as per {@link StringBuilder#reverse()}. - *

- * - *

- * A {@code null} String returns {@code null}. - *

- * - *
-	 * StringUtils.reverse(null)  = null
-	 * StringUtils.reverse("")    = ""
-	 * StringUtils.reverse("bat") = "tab"
-	 * 
- * - * @param str the String to reverse, may be null - * @return the reversed String, {@code null} if null String input - */ - public static String reverse(final String str) { - if (str == null) { - return null; - } - return new StringBuilder(str).reverse().toString(); - } - - /** - *

- * Reverses a String that is delimited by a specific character. - *

- * - *

- * The Strings between the delimiters are not reversed. Thus java.lang.String - * becomes String.lang.java (if the delimiter is {@code '.'}). - *

- * - *
-	 * StringUtils.reverseDelimited(null, *)      = null
-	 * StringUtils.reverseDelimited("", *)        = ""
-	 * StringUtils.reverseDelimited("a.b.c", 'x') = "a.b.c"
-	 * StringUtils.reverseDelimited("a.b.c", ".") = "c.b.a"
-	 * 
- * - * @param str the String to reverse, may be null - * @param separatorChar the separator character to use - * @return the reversed String, {@code null} if null String input - * @since 2.0 - */ - public static String reverseDelimited(final String str, final char separatorChar) { - if (str == null) { - return null; - } - // could implement manually, but simple way is to reuse other, - // probably slower, methods. - /* - * final String[] strs = split(str, separatorChar); ArrayUtils.reverse(strs); - * return join(strs, separatorChar); - */ - throw new UnsupportedOperationException("Not supported in TeaVM"); - } - - /** - *

- * Gets the rightmost {@code len} characters of a String. - *

- * - *

- * If {@code len} characters are not available, or the String is {@code null}, - * the String will be returned without an an exception. An empty String is - * returned if len is negative. - *

- * - *
-	 * StringUtils.right(null, *)    = null
-	 * StringUtils.right(*, -ve)     = ""
-	 * StringUtils.right("", *)      = ""
-	 * StringUtils.right("abc", 0)   = ""
-	 * StringUtils.right("abc", 2)   = "bc"
-	 * StringUtils.right("abc", 4)   = "abc"
-	 * 
- * - * @param str the String to get the rightmost characters from, may be null - * @param len the length of the required String - * @return the rightmost characters, {@code null} if null String input - */ - public static String right(final String str, final int len) { - if (str == null) { - return null; - } - if (len < 0) { - return EMPTY; - } - if (str.length() <= len) { - return str; - } - return str.substring(str.length() - len); - } - - /** - *

- * Right pad a String with spaces (' '). - *

- * - *

- * The String is padded to the size of {@code size}. - *

- * - *
-	 * StringUtils.rightPad(null, *)   = null
-	 * StringUtils.rightPad("", 3)     = "   "
-	 * StringUtils.rightPad("bat", 3)  = "bat"
-	 * StringUtils.rightPad("bat", 5)  = "bat  "
-	 * StringUtils.rightPad("bat", 1)  = "bat"
-	 * StringUtils.rightPad("bat", -1) = "bat"
-	 * 
- * - * @param str the String to pad out, may be null - * @param size the size to pad to - * @return right padded String or original String if no padding is necessary, - * {@code null} if null String input - */ - public static String rightPad(final String str, final int size) { - return rightPad(str, size, ' '); - } - - /** - *

- * Right pad a String with a specified character. - *

- * - *

- * The String is padded to the size of {@code size}. - *

- * - *
-	 * StringUtils.rightPad(null, *, *)     = null
-	 * StringUtils.rightPad("", 3, 'z')     = "zzz"
-	 * StringUtils.rightPad("bat", 3, 'z')  = "bat"
-	 * StringUtils.rightPad("bat", 5, 'z')  = "batzz"
-	 * StringUtils.rightPad("bat", 1, 'z')  = "bat"
-	 * StringUtils.rightPad("bat", -1, 'z') = "bat"
-	 * 
- * - * @param str the String to pad out, may be null - * @param size the size to pad to - * @param padChar the character to pad with - * @return right padded String or original String if no padding is necessary, - * {@code null} if null String input - * @since 2.0 - */ - public static String rightPad(final String str, final int size, final char padChar) { - if (str == null) { - return null; - } - final int pads = size - str.length(); - if (pads <= 0) { - return str; // returns original String when possible - } - if (pads > PAD_LIMIT) { - return rightPad(str, size, String.valueOf(padChar)); - } - return str.concat(repeat(padChar, pads)); - } - - /** - *

- * Right pad a String with a specified String. - *

- * - *

- * The String is padded to the size of {@code size}. - *

- * - *
-	 * StringUtils.rightPad(null, *, *)      = null
-	 * StringUtils.rightPad("", 3, "z")      = "zzz"
-	 * StringUtils.rightPad("bat", 3, "yz")  = "bat"
-	 * StringUtils.rightPad("bat", 5, "yz")  = "batyz"
-	 * StringUtils.rightPad("bat", 8, "yz")  = "batyzyzy"
-	 * StringUtils.rightPad("bat", 1, "yz")  = "bat"
-	 * StringUtils.rightPad("bat", -1, "yz") = "bat"
-	 * StringUtils.rightPad("bat", 5, null)  = "bat  "
-	 * StringUtils.rightPad("bat", 5, "")    = "bat  "
-	 * 
- * - * @param str the String to pad out, may be null - * @param size the size to pad to - * @param padStr the String to pad with, null or empty treated as single space - * @return right padded String or original String if no padding is necessary, - * {@code null} if null String input - */ - public static String rightPad(final String str, final int size, String padStr) { - if (str == null) { - return null; - } - if (isEmpty(padStr)) { - padStr = SPACE; - } - final int padLen = padStr.length(); - final int strLen = str.length(); - final int pads = size - strLen; - if (pads <= 0) { - return str; // returns original String when possible - } - if (padLen == 1 && pads <= PAD_LIMIT) { - return rightPad(str, size, padStr.charAt(0)); - } - - if (pads == padLen) { - return str.concat(padStr); - } else if (pads < padLen) { - return str.concat(padStr.substring(0, pads)); - } else { - final char[] padding = new char[pads]; - final char[] padChars = padStr.toCharArray(); - for (int i = 0; i < pads; i++) { - padding[i] = padChars[i % padLen]; - } - return str.concat(new String(padding)); - } - } - - /** - *

- * Rotate (circular shift) a String of {@code shift} characters. - *

- *
    - *
  • If {@code shift > 0}, right circular shift (ex : ABCDEF => - * FABCDE)
  • - *
  • If {@code shift < 0}, left circular shift (ex : ABCDEF => BCDEFA)
  • - *
- * - *
-	 * StringUtils.rotate(null, *)        = null
-	 * StringUtils.rotate("", *)          = ""
-	 * StringUtils.rotate("abcdefg", 0)   = "abcdefg"
-	 * StringUtils.rotate("abcdefg", 2)   = "fgabcde"
-	 * StringUtils.rotate("abcdefg", -2)  = "cdefgab"
-	 * StringUtils.rotate("abcdefg", 7)   = "abcdefg"
-	 * StringUtils.rotate("abcdefg", -7)  = "abcdefg"
-	 * StringUtils.rotate("abcdefg", 9)   = "fgabcde"
-	 * StringUtils.rotate("abcdefg", -9)  = "cdefgab"
-	 * 
- * - * @param str the String to rotate, may be null - * @param shift number of time to shift (positive : right shift, negative : left - * shift) - * @return the rotated String, or the original String if {@code shift == 0}, or - * {@code null} if null String input - * @since 3.5 - */ - public static String rotate(final String str, final int shift) { - if (str == null) { - return null; - } - - final int strLen = str.length(); - if (shift == 0 || strLen == 0 || shift % strLen == 0) { - return str; - } - - final StringBuilder builder = new StringBuilder(strLen); - final int offset = -(shift % strLen); - builder.append(substring(str, offset)); - builder.append(substring(str, 0, offset)); - return builder.toString(); - } - - /** - *

- * Splits the provided text into an array, using whitespace as the separator. - * Whitespace is defined by {@link Character#isWhitespace(char)}. - *

- * - *

- * The separator is not included in the returned String array. Adjacent - * separators are treated as one separator. For more control over the split use - * the StrTokenizer class. - *

- * - *

- * A {@code null} input String returns {@code null}. - *

- * - *
-	 * StringUtils.split(null)       = null
-	 * StringUtils.split("")         = []
-	 * StringUtils.split("abc def")  = ["abc", "def"]
-	 * StringUtils.split("abc  def") = ["abc", "def"]
-	 * StringUtils.split(" abc ")    = ["abc"]
-	 * 
- * - * @param str the String to parse, may be null - * @return an array of parsed Strings, {@code null} if null String input - */ - public static String[] split(final String str) { - return split(str, null, -1); - } - - /** - *

- * Splits the provided text into an array, separator specified. This is an - * alternative to using StringTokenizer. - *

- * - *

- * The separator is not included in the returned String array. Adjacent - * separators are treated as one separator. For more control over the split use - * the StrTokenizer class. - *

- * - *

- * A {@code null} input String returns {@code null}. - *

- * - *
-	 * StringUtils.split(null, *)         = null
-	 * StringUtils.split("", *)           = []
-	 * StringUtils.split("a.b.c", '.')    = ["a", "b", "c"]
-	 * StringUtils.split("a..b.c", '.')   = ["a", "b", "c"]
-	 * StringUtils.split("a:b:c", '.')    = ["a:b:c"]
-	 * StringUtils.split("a b c", ' ')    = ["a", "b", "c"]
-	 * 
- * - * @param str the String to parse, may be null - * @param separatorChar the character used as the delimiter - * @return an array of parsed Strings, {@code null} if null String input - * @since 2.0 - */ - public static String[] split(final String str, final char separatorChar) { - return splitWorker(str, separatorChar, false); - } - - /** - *

- * Splits the provided text into an array, separators specified. This is an - * alternative to using StringTokenizer. - *

- * - *

- * The separator is not included in the returned String array. Adjacent - * separators are treated as one separator. For more control over the split use - * the StrTokenizer class. - *

- * - *

- * A {@code null} input String returns {@code null}. A {@code null} - * separatorChars splits on whitespace. - *

- * - *
-	 * StringUtils.split(null, *)         = null
-	 * StringUtils.split("", *)           = []
-	 * StringUtils.split("abc def", null) = ["abc", "def"]
-	 * StringUtils.split("abc def", " ")  = ["abc", "def"]
-	 * StringUtils.split("abc  def", " ") = ["abc", "def"]
-	 * StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
-	 * 
- * - * @param str the String to parse, may be null - * @param separatorChars the characters used as the delimiters, {@code null} - * splits on whitespace - * @return an array of parsed Strings, {@code null} if null String input - */ - public static String[] split(final String str, final String separatorChars) { - return splitWorker(str, separatorChars, -1, false); - } - - /** - *

- * Splits the provided text into an array with a maximum length, separators - * specified. - *

- * - *

- * The separator is not included in the returned String array. Adjacent - * separators are treated as one separator. - *

- * - *

- * A {@code null} input String returns {@code null}. A {@code null} - * separatorChars splits on whitespace. - *

- * - *

- * If more than {@code max} delimited substrings are found, the last returned - * string includes all characters after the first {@code max - 1} returned - * strings (including separator characters). - *

- * - *
-	 * StringUtils.split(null, *, *)            = null
-	 * StringUtils.split("", *, *)              = []
-	 * StringUtils.split("ab cd ef", null, 0)   = ["ab", "cd", "ef"]
-	 * StringUtils.split("ab   cd ef", null, 0) = ["ab", "cd", "ef"]
-	 * StringUtils.split("ab:cd:ef", ":", 0)    = ["ab", "cd", "ef"]
-	 * StringUtils.split("ab:cd:ef", ":", 2)    = ["ab", "cd:ef"]
-	 * 
- * - * @param str the String to parse, may be null - * @param separatorChars the characters used as the delimiters, {@code null} - * splits on whitespace - * @param max the maximum number of elements to include in the array. - * A zero or negative value implies no limit - * @return an array of parsed Strings, {@code null} if null String input - */ - public static String[] split(final String str, final String separatorChars, final int max) { - return splitWorker(str, separatorChars, max, false); - } - - /** - *

- * Splits a String by Character type as returned by - * {@code java.lang.Character.getType(char)}. Groups of contiguous characters of - * the same type are returned as complete tokens. - * - *

-	 * StringUtils.splitByCharacterType(null)         = null
-	 * StringUtils.splitByCharacterType("")           = []
-	 * StringUtils.splitByCharacterType("ab de fg")   = ["ab", " ", "de", " ", "fg"]
-	 * StringUtils.splitByCharacterType("ab   de fg") = ["ab", "   ", "de", " ", "fg"]
-	 * StringUtils.splitByCharacterType("ab:cd:ef")   = ["ab", ":", "cd", ":", "ef"]
-	 * StringUtils.splitByCharacterType("number5")    = ["number", "5"]
-	 * StringUtils.splitByCharacterType("fooBar")     = ["foo", "B", "ar"]
-	 * StringUtils.splitByCharacterType("foo200Bar")  = ["foo", "200", "B", "ar"]
-	 * StringUtils.splitByCharacterType("ASFRules")   = ["ASFR", "ules"]
-	 * 
- * - * @param str the String to split, may be {@code null} - * @return an array of parsed Strings, {@code null} if null String input - * @since 2.4 - */ - public static String[] splitByCharacterType(final String str) { - return splitByCharacterType(str, false); - } - - /** - *

- * Splits a String by Character type as returned by - * {@code java.lang.Character.getType(char)}. Groups of contiguous characters of - * the same type are returned as complete tokens, with the following exception: - * if {@code camelCase} is {@code true}, the character of type - * {@code Character.UPPERCASE_LETTER}, if any, immediately preceding a token of - * type {@code Character.LOWERCASE_LETTER} will belong to the following token - * rather than to the preceding, if any, {@code Character.UPPERCASE_LETTER} - * token. - * - * @param str the String to split, may be {@code null} - * @param camelCase whether to use so-called "camel-case" for letter types - * @return an array of parsed Strings, {@code null} if null String input - * @since 2.4 - */ - private static String[] splitByCharacterType(final String str, final boolean camelCase) { - if (str == null) { - return null; - } - if (str.isEmpty()) { - return new String[0]; - } - final char[] c = str.toCharArray(); - final List list = new ArrayList<>(); - int tokenStart = 0; - int currentType = Character.getType(c[tokenStart]); - for (int pos = tokenStart + 1; pos < c.length; pos++) { - final int type = Character.getType(c[pos]); - if (type == currentType) { - continue; - } - if (camelCase && type == Character.LOWERCASE_LETTER && currentType == Character.UPPERCASE_LETTER) { - final int newTokenStart = pos - 1; - if (newTokenStart != tokenStart) { - list.add(new String(c, tokenStart, newTokenStart - tokenStart)); - tokenStart = newTokenStart; - } - } else { - list.add(new String(c, tokenStart, pos - tokenStart)); - tokenStart = pos; - } - currentType = type; - } - list.add(new String(c, tokenStart, c.length - tokenStart)); - return list.toArray(new String[0]); - } - - /** - *

- * Splits a String by Character type as returned by - * {@code java.lang.Character.getType(char)}. Groups of contiguous characters of - * the same type are returned as complete tokens, with the following exception: - * the character of type {@code Character.UPPERCASE_LETTER}, if any, immediately - * preceding a token of type {@code Character.LOWERCASE_LETTER} will belong to - * the following token rather than to the preceding, if any, - * {@code Character.UPPERCASE_LETTER} token. - * - *

-	 * StringUtils.splitByCharacterTypeCamelCase(null)         = null
-	 * StringUtils.splitByCharacterTypeCamelCase("")           = []
-	 * StringUtils.splitByCharacterTypeCamelCase("ab de fg")   = ["ab", " ", "de", " ", "fg"]
-	 * StringUtils.splitByCharacterTypeCamelCase("ab   de fg") = ["ab", "   ", "de", " ", "fg"]
-	 * StringUtils.splitByCharacterTypeCamelCase("ab:cd:ef")   = ["ab", ":", "cd", ":", "ef"]
-	 * StringUtils.splitByCharacterTypeCamelCase("number5")    = ["number", "5"]
-	 * StringUtils.splitByCharacterTypeCamelCase("fooBar")     = ["foo", "Bar"]
-	 * StringUtils.splitByCharacterTypeCamelCase("foo200Bar")  = ["foo", "200", "Bar"]
-	 * StringUtils.splitByCharacterTypeCamelCase("ASFRules")   = ["ASF", "Rules"]
-	 * 
- * - * @param str the String to split, may be {@code null} - * @return an array of parsed Strings, {@code null} if null String input - * @since 2.4 - */ - public static String[] splitByCharacterTypeCamelCase(final String str) { - return splitByCharacterType(str, true); - } - - /** - *

- * Splits the provided text into an array, separator string specified. - *

- * - *

- * The separator(s) will not be included in the returned String array. Adjacent - * separators are treated as one separator. - *

- * - *

- * A {@code null} input String returns {@code null}. A {@code null} separator - * splits on whitespace. - *

- * - *
-	 * StringUtils.splitByWholeSeparator(null, *)               = null
-	 * StringUtils.splitByWholeSeparator("", *)                 = []
-	 * StringUtils.splitByWholeSeparator("ab de fg", null)      = ["ab", "de", "fg"]
-	 * StringUtils.splitByWholeSeparator("ab   de fg", null)    = ["ab", "de", "fg"]
-	 * StringUtils.splitByWholeSeparator("ab:cd:ef", ":")       = ["ab", "cd", "ef"]
-	 * StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-") = ["ab", "cd", "ef"]
-	 * 
- * - * @param str the String to parse, may be null - * @param separator String containing the String to be used as a delimiter, - * {@code null} splits on whitespace - * @return an array of parsed Strings, {@code null} if null String was input - */ - public static String[] splitByWholeSeparator(final String str, final String separator) { - return splitByWholeSeparatorWorker(str, separator, -1, false); - } - - /** - *

- * Splits the provided text into an array, separator string specified. Returns a - * maximum of {@code max} substrings. - *

- * - *

- * The separator(s) will not be included in the returned String array. Adjacent - * separators are treated as one separator. - *

- * - *

- * A {@code null} input String returns {@code null}. A {@code null} separator - * splits on whitespace. - *

- * - *
-	 * StringUtils.splitByWholeSeparator(null, *, *)               = null
-	 * StringUtils.splitByWholeSeparator("", *, *)                 = []
-	 * StringUtils.splitByWholeSeparator("ab de fg", null, 0)      = ["ab", "de", "fg"]
-	 * StringUtils.splitByWholeSeparator("ab   de fg", null, 0)    = ["ab", "de", "fg"]
-	 * StringUtils.splitByWholeSeparator("ab:cd:ef", ":", 2)       = ["ab", "cd:ef"]
-	 * StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-", 5) = ["ab", "cd", "ef"]
-	 * StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-", 2) = ["ab", "cd-!-ef"]
-	 * 
- * - * @param str the String to parse, may be null - * @param separator String containing the String to be used as a delimiter, - * {@code null} splits on whitespace - * @param max the maximum number of elements to include in the returned - * array. A zero or negative value implies no limit. - * @return an array of parsed Strings, {@code null} if null String was input - */ - public static String[] splitByWholeSeparator(final String str, final String separator, final int max) { - return splitByWholeSeparatorWorker(str, separator, max, false); - } - - /** - *

- * Splits the provided text into an array, separator string specified. - *

- * - *

- * The separator is not included in the returned String array. Adjacent - * separators are treated as separators for empty tokens. For more control over - * the split use the StrTokenizer class. - *

- * - *

- * A {@code null} input String returns {@code null}. A {@code null} separator - * splits on whitespace. - *

- * - *
-	 * StringUtils.splitByWholeSeparatorPreserveAllTokens(null, *)               = null
-	 * StringUtils.splitByWholeSeparatorPreserveAllTokens("", *)                 = []
-	 * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab de fg", null)      = ["ab", "de", "fg"]
-	 * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab   de fg", null)    = ["ab", "", "", "de", "fg"]
-	 * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab:cd:ef", ":")       = ["ab", "cd", "ef"]
-	 * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-") = ["ab", "cd", "ef"]
-	 * 
- * - * @param str the String to parse, may be null - * @param separator String containing the String to be used as a delimiter, - * {@code null} splits on whitespace - * @return an array of parsed Strings, {@code null} if null String was input - * @since 2.4 - */ - public static String[] splitByWholeSeparatorPreserveAllTokens(final String str, final String separator) { - return splitByWholeSeparatorWorker(str, separator, -1, true); - } - - /** - *

- * Splits the provided text into an array, separator string specified. Returns a - * maximum of {@code max} substrings. - *

- * - *

- * The separator is not included in the returned String array. Adjacent - * separators are treated as separators for empty tokens. For more control over - * the split use the StrTokenizer class. - *

- * - *

- * A {@code null} input String returns {@code null}. A {@code null} separator - * splits on whitespace. - *

- * - *
-	 * StringUtils.splitByWholeSeparatorPreserveAllTokens(null, *, *)               = null
-	 * StringUtils.splitByWholeSeparatorPreserveAllTokens("", *, *)                 = []
-	 * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab de fg", null, 0)      = ["ab", "de", "fg"]
-	 * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab   de fg", null, 0)    = ["ab", "", "", "de", "fg"]
-	 * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab:cd:ef", ":", 2)       = ["ab", "cd:ef"]
-	 * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-", 5) = ["ab", "cd", "ef"]
-	 * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-", 2) = ["ab", "cd-!-ef"]
-	 * 
- * - * @param str the String to parse, may be null - * @param separator String containing the String to be used as a delimiter, - * {@code null} splits on whitespace - * @param max the maximum number of elements to include in the returned - * array. A zero or negative value implies no limit. - * @return an array of parsed Strings, {@code null} if null String was input - * @since 2.4 - */ - public static String[] splitByWholeSeparatorPreserveAllTokens(final String str, final String separator, - final int max) { - return splitByWholeSeparatorWorker(str, separator, max, true); - } - - /** - * Performs the logic for the {@code splitByWholeSeparatorPreserveAllTokens} - * methods. - * - * @param str the String to parse, may be {@code null} - * @param separator String containing the String to be used as a - * delimiter, {@code null} splits on whitespace - * @param max the maximum number of elements to include in the - * returned array. A zero or negative value implies no - * limit. - * @param preserveAllTokens if {@code true}, adjacent separators are treated as - * empty token separators; if {@code false}, adjacent - * separators are treated as one separator. - * @return an array of parsed Strings, {@code null} if null String input - * @since 2.4 - */ - private static String[] splitByWholeSeparatorWorker(final String str, final String separator, final int max, - final boolean preserveAllTokens) { - if (str == null) { - return null; - } - - final int len = str.length(); - - if (len == 0) { - return new String[0]; - } - - if (separator == null || EMPTY.equals(separator)) { - // Split on whitespace. - return splitWorker(str, null, max, preserveAllTokens); - } - - final int separatorLength = separator.length(); - - final ArrayList substrings = new ArrayList<>(); - int numberOfSubstrings = 0; - int beg = 0; - int end = 0; - while (end < len) { - end = str.indexOf(separator, beg); - - if (end > -1) { - if (end > beg) { - numberOfSubstrings += 1; - - if (numberOfSubstrings == max) { - end = len; - substrings.add(str.substring(beg)); - } else { - // The following is OK, because String.substring( beg, end ) excludes - // the character at the position 'end'. - substrings.add(str.substring(beg, end)); - - // Set the starting point for the next search. - // The following is equivalent to beg = end + (separatorLength - 1) + 1, - // which is the right calculation: - beg = end + separatorLength; - } - } else { - // We found a consecutive occurrence of the separator, so skip it. - if (preserveAllTokens) { - numberOfSubstrings += 1; - if (numberOfSubstrings == max) { - end = len; - substrings.add(str.substring(beg)); - } else { - substrings.add(EMPTY); - } - } - beg = end + separatorLength; - } - } else { - // String.substring( beg ) goes from 'beg' to the end of the String. - substrings.add(str.substring(beg)); - end = len; - } - } - - return substrings.toArray(new String[0]); - } - - /** - *

- * Splits the provided text into an array, using whitespace as the separator, - * preserving all tokens, including empty tokens created by adjacent separators. - * This is an alternative to using StringTokenizer. Whitespace is defined by - * {@link Character#isWhitespace(char)}. - *

- * - *

- * The separator is not included in the returned String array. Adjacent - * separators are treated as separators for empty tokens. For more control over - * the split use the StrTokenizer class. - *

- * - *

- * A {@code null} input String returns {@code null}. - *

- * - *
-	 * StringUtils.splitPreserveAllTokens(null)       = null
-	 * StringUtils.splitPreserveAllTokens("")         = []
-	 * StringUtils.splitPreserveAllTokens("abc def")  = ["abc", "def"]
-	 * StringUtils.splitPreserveAllTokens("abc  def") = ["abc", "", "def"]
-	 * StringUtils.splitPreserveAllTokens(" abc ")    = ["", "abc", ""]
-	 * 
- * - * @param str the String to parse, may be {@code null} - * @return an array of parsed Strings, {@code null} if null String input - * @since 2.1 - */ - public static String[] splitPreserveAllTokens(final String str) { - return splitWorker(str, null, -1, true); - } - - /** - *

- * Splits the provided text into an array, separator specified, preserving all - * tokens, including empty tokens created by adjacent separators. This is an - * alternative to using StringTokenizer. - *

- * - *

- * The separator is not included in the returned String array. Adjacent - * separators are treated as separators for empty tokens. For more control over - * the split use the StrTokenizer class. - *

- * - *

- * A {@code null} input String returns {@code null}. - *

- * - *
-	 * StringUtils.splitPreserveAllTokens(null, *)         = null
-	 * StringUtils.splitPreserveAllTokens("", *)           = []
-	 * StringUtils.splitPreserveAllTokens("a.b.c", '.')    = ["a", "b", "c"]
-	 * StringUtils.splitPreserveAllTokens("a..b.c", '.')   = ["a", "", "b", "c"]
-	 * StringUtils.splitPreserveAllTokens("a:b:c", '.')    = ["a:b:c"]
-	 * StringUtils.splitPreserveAllTokens("a\tb\nc", null) = ["a", "b", "c"]
-	 * StringUtils.splitPreserveAllTokens("a b c", ' ')    = ["a", "b", "c"]
-	 * StringUtils.splitPreserveAllTokens("a b c ", ' ')   = ["a", "b", "c", ""]
-	 * StringUtils.splitPreserveAllTokens("a b c  ", ' ')   = ["a", "b", "c", "", ""]
-	 * StringUtils.splitPreserveAllTokens(" a b c", ' ')   = ["", a", "b", "c"]
-	 * StringUtils.splitPreserveAllTokens("  a b c", ' ')  = ["", "", a", "b", "c"]
-	 * StringUtils.splitPreserveAllTokens(" a b c ", ' ')  = ["", a", "b", "c", ""]
-	 * 
- * - * @param str the String to parse, may be {@code null} - * @param separatorChar the character used as the delimiter, {@code null} splits - * on whitespace - * @return an array of parsed Strings, {@code null} if null String input - * @since 2.1 - */ - public static String[] splitPreserveAllTokens(final String str, final char separatorChar) { - return splitWorker(str, separatorChar, true); - } - - /** - *

- * Splits the provided text into an array, separators specified, preserving all - * tokens, including empty tokens created by adjacent separators. This is an - * alternative to using StringTokenizer. - *

- * - *

- * The separator is not included in the returned String array. Adjacent - * separators are treated as separators for empty tokens. For more control over - * the split use the StrTokenizer class. - *

- * - *

- * A {@code null} input String returns {@code null}. A {@code null} - * separatorChars splits on whitespace. - *

- * - *
-	 * StringUtils.splitPreserveAllTokens(null, *)           = null
-	 * StringUtils.splitPreserveAllTokens("", *)             = []
-	 * StringUtils.splitPreserveAllTokens("abc def", null)   = ["abc", "def"]
-	 * StringUtils.splitPreserveAllTokens("abc def", " ")    = ["abc", "def"]
-	 * StringUtils.splitPreserveAllTokens("abc  def", " ")   = ["abc", "", def"]
-	 * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":")   = ["ab", "cd", "ef"]
-	 * StringUtils.splitPreserveAllTokens("ab:cd:ef:", ":")  = ["ab", "cd", "ef", ""]
-	 * StringUtils.splitPreserveAllTokens("ab:cd:ef::", ":") = ["ab", "cd", "ef", "", ""]
-	 * StringUtils.splitPreserveAllTokens("ab::cd:ef", ":")  = ["ab", "", cd", "ef"]
-	 * StringUtils.splitPreserveAllTokens(":cd:ef", ":")     = ["", cd", "ef"]
-	 * StringUtils.splitPreserveAllTokens("::cd:ef", ":")    = ["", "", cd", "ef"]
-	 * StringUtils.splitPreserveAllTokens(":cd:ef:", ":")    = ["", cd", "ef", ""]
-	 * 
- * - * @param str the String to parse, may be {@code null} - * @param separatorChars the characters used as the delimiters, {@code null} - * splits on whitespace - * @return an array of parsed Strings, {@code null} if null String input - * @since 2.1 - */ - public static String[] splitPreserveAllTokens(final String str, final String separatorChars) { - return splitWorker(str, separatorChars, -1, true); - } - - /** - *

- * Splits the provided text into an array with a maximum length, separators - * specified, preserving all tokens, including empty tokens created by adjacent - * separators. - *

- * - *

- * The separator is not included in the returned String array. Adjacent - * separators are treated as separators for empty tokens. Adjacent separators - * are treated as one separator. - *

- * - *

- * A {@code null} input String returns {@code null}. A {@code null} - * separatorChars splits on whitespace. - *

- * - *

- * If more than {@code max} delimited substrings are found, the last returned - * string includes all characters after the first {@code max - 1} returned - * strings (including separator characters). - *

- * - *
-	 * StringUtils.splitPreserveAllTokens(null, *, *)            = null
-	 * StringUtils.splitPreserveAllTokens("", *, *)              = []
-	 * StringUtils.splitPreserveAllTokens("ab de fg", null, 0)   = ["ab", "de", "fg"]
-	 * StringUtils.splitPreserveAllTokens("ab   de fg", null, 0) = ["ab", "", "", "de", "fg"]
-	 * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":", 0)    = ["ab", "cd", "ef"]
-	 * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":", 2)    = ["ab", "cd:ef"]
-	 * StringUtils.splitPreserveAllTokens("ab   de fg", null, 2) = ["ab", "  de fg"]
-	 * StringUtils.splitPreserveAllTokens("ab   de fg", null, 3) = ["ab", "", " de fg"]
-	 * StringUtils.splitPreserveAllTokens("ab   de fg", null, 4) = ["ab", "", "", "de fg"]
-	 * 
- * - * @param str the String to parse, may be {@code null} - * @param separatorChars the characters used as the delimiters, {@code null} - * splits on whitespace - * @param max the maximum number of elements to include in the array. - * A zero or negative value implies no limit - * @return an array of parsed Strings, {@code null} if null String input - * @since 2.1 - */ - public static String[] splitPreserveAllTokens(final String str, final String separatorChars, final int max) { - return splitWorker(str, separatorChars, max, true); - } - - /** - * Performs the logic for the {@code split} and {@code splitPreserveAllTokens} - * methods that do not return a maximum array length. - * - * @param str the String to parse, may be {@code null} - * @param separatorChar the separate character - * @param preserveAllTokens if {@code true}, adjacent separators are treated as - * empty token separators; if {@code false}, adjacent - * separators are treated as one separator. - * @return an array of parsed Strings, {@code null} if null String input - */ - private static String[] splitWorker(final String str, final char separatorChar, final boolean preserveAllTokens) { - // Performance tuned for 2.0 (JDK1.4) - - if (str == null) { - return null; - } - final int len = str.length(); - if (len == 0) { - return new String[0]; - } - final List list = new ArrayList<>(); - int i = 0; - int start = 0; - boolean match = false; - boolean lastMatch = false; - while (i < len) { - if (str.charAt(i) == separatorChar) { - if (match || preserveAllTokens) { - list.add(str.substring(start, i)); - match = false; - lastMatch = true; - } - start = ++i; - continue; - } - lastMatch = false; - match = true; - i++; - } - if (match || preserveAllTokens && lastMatch) { - list.add(str.substring(start, i)); - } - return list.toArray(new String[0]); - } - - /** - * Performs the logic for the {@code split} and {@code splitPreserveAllTokens} - * methods that return a maximum array length. - * - * @param str the String to parse, may be {@code null} - * @param separatorChars the separate character - * @param max the maximum number of elements to include in the - * array. A zero or negative value implies no limit. - * @param preserveAllTokens if {@code true}, adjacent separators are treated as - * empty token separators; if {@code false}, adjacent - * separators are treated as one separator. - * @return an array of parsed Strings, {@code null} if null String input - */ - private static String[] splitWorker(final String str, final String separatorChars, final int max, - final boolean preserveAllTokens) { - // Performance tuned for 2.0 (JDK1.4) - // Direct code is quicker than StringTokenizer. - // Also, StringTokenizer uses isSpace() not isWhitespace() - - if (str == null) { - return null; - } - final int len = str.length(); - if (len == 0) { - return new String[0]; - } - final List list = new ArrayList<>(); - int sizePlus1 = 1; - int i = 0; - int start = 0; - boolean match = false; - boolean lastMatch = false; - if (separatorChars == null) { - // Null separator means use whitespace - while (i < len) { - if (Character.isWhitespace(str.charAt(i))) { - if (match || preserveAllTokens) { - lastMatch = true; - if (sizePlus1++ == max) { - i = len; - lastMatch = false; - } - list.add(str.substring(start, i)); - match = false; - } - start = ++i; - continue; - } - lastMatch = false; - match = true; - i++; - } - } else if (separatorChars.length() == 1) { - // Optimise 1 character case - final char sep = separatorChars.charAt(0); - while (i < len) { - if (str.charAt(i) == sep) { - if (match || preserveAllTokens) { - lastMatch = true; - if (sizePlus1++ == max) { - i = len; - lastMatch = false; - } - list.add(str.substring(start, i)); - match = false; - } - start = ++i; - continue; - } - lastMatch = false; - match = true; - i++; - } - } else { - // standard case - while (i < len) { - if (separatorChars.indexOf(str.charAt(i)) >= 0) { - if (match || preserveAllTokens) { - lastMatch = true; - if (sizePlus1++ == max) { - i = len; - lastMatch = false; - } - list.add(str.substring(start, i)); - match = false; - } - start = ++i; - continue; - } - lastMatch = false; - match = true; - i++; - } - } - if (match || preserveAllTokens && lastMatch) { - list.add(str.substring(start, i)); - } - return list.toArray(new String[0]); - } - - /** - *

- * Check if a CharSequence starts with a specified prefix. - *

- * - *

- * {@code null}s are handled without exceptions. Two {@code null} references are - * considered to be equal. The comparison is case sensitive. - *

- * - *
-	 * StringUtils.startsWith(null, null)      = true
-	 * StringUtils.startsWith(null, "abc")     = false
-	 * StringUtils.startsWith("abcdef", null)  = false
-	 * StringUtils.startsWith("abcdef", "abc") = true
-	 * StringUtils.startsWith("ABCDEF", "abc") = false
-	 * 
- * - * @see java.lang.String#startsWith(String) - * @param str the CharSequence to check, may be null - * @param prefix the prefix to find, may be null - * @return {@code true} if the CharSequence starts with the prefix, case - * sensitive, or both {@code null} - * @since 2.4 - * @since 3.0 Changed signature from startsWith(String, String) to - * startsWith(CharSequence, CharSequence) - */ - public static boolean startsWith(final CharSequence str, final CharSequence prefix) { - return startsWith(str, prefix, false); - } - - /** - *

- * Check if a CharSequence starts with a specified prefix (optionally case - * insensitive). - *

- * - * @see java.lang.String#startsWith(String) - * @param str the CharSequence to check, may be null - * @param prefix the prefix to find, may be null - * @param ignoreCase indicates whether the compare should ignore case (case - * insensitive) or not. - * @return {@code true} if the CharSequence starts with the prefix or both - * {@code null} - */ - private static boolean startsWith(final CharSequence str, final CharSequence prefix, final boolean ignoreCase) { - if (str == null || prefix == null) { - return str == prefix; - } - // Get length once instead of twice in the unlikely case that it changes. - final int preLen = prefix.length(); - if (preLen > str.length()) { - return false; - } - return CharSequenceUtils.regionMatches(str, ignoreCase, 0, prefix, 0, preLen); - } - - /** - *

- * Check if a CharSequence starts with any of the provided case-sensitive - * prefixes. - *

- * - *
-	 * StringUtils.startsWithAny(null, null)      = false
-	 * StringUtils.startsWithAny(null, new String[] {"abc"})  = false
-	 * StringUtils.startsWithAny("abcxyz", null)     = false
-	 * StringUtils.startsWithAny("abcxyz", new String[] {""}) = true
-	 * StringUtils.startsWithAny("abcxyz", new String[] {"abc"}) = true
-	 * StringUtils.startsWithAny("abcxyz", new String[] {null, "xyz", "abc"}) = true
-	 * StringUtils.startsWithAny("abcxyz", null, "xyz", "ABCX") = false
-	 * StringUtils.startsWithAny("ABCXYZ", null, "xyz", "abc") = false
-	 * 
- * - * @param sequence the CharSequence to check, may be null - * @param searchStrings the case-sensitive CharSequence prefixes, may be empty - * or contain {@code null} - * @see StringUtils#startsWith(CharSequence, CharSequence) - * @return {@code true} if the input {@code sequence} is {@code null} AND no - * {@code searchStrings} are provided, or the input {@code sequence} - * begins with any of the provided case-sensitive {@code searchStrings}. - * @since 2.5 - * @since 3.0 Changed signature from startsWithAny(String, String[]) to - * startsWithAny(CharSequence, CharSequence...) - */ - public static boolean startsWithAny(final CharSequence sequence, final CharSequence... searchStrings) { - if (isEmpty(sequence) || searchStrings.length == 0) { - return false; - } - for (final CharSequence searchString : searchStrings) { - if (startsWith(sequence, searchString)) { - return true; - } - } - return false; - } - - /** - *

- * Case insensitive check if a CharSequence starts with a specified prefix. - *

- * - *

- * {@code null}s are handled without exceptions. Two {@code null} references are - * considered to be equal. The comparison is case insensitive. - *

- * - *
-	 * StringUtils.startsWithIgnoreCase(null, null)      = true
-	 * StringUtils.startsWithIgnoreCase(null, "abc")     = false
-	 * StringUtils.startsWithIgnoreCase("abcdef", null)  = false
-	 * StringUtils.startsWithIgnoreCase("abcdef", "abc") = true
-	 * StringUtils.startsWithIgnoreCase("ABCDEF", "abc") = true
-	 * 
- * - * @see java.lang.String#startsWith(String) - * @param str the CharSequence to check, may be null - * @param prefix the prefix to find, may be null - * @return {@code true} if the CharSequence starts with the prefix, case - * insensitive, or both {@code null} - * @since 2.4 - * @since 3.0 Changed signature from startsWithIgnoreCase(String, String) to - * startsWithIgnoreCase(CharSequence, CharSequence) - */ - public static boolean startsWithIgnoreCase(final CharSequence str, final CharSequence prefix) { - return startsWith(str, prefix, true); - } - - /** - *

- * Strips whitespace from the start and end of a String. - *

- * - *

- * This is similar to {@link #trim(String)} but removes whitespace. Whitespace - * is defined by {@link Character#isWhitespace(char)}. - *

- * - *

- * A {@code null} input String returns {@code null}. - *

- * - *
-	 * StringUtils.strip(null)     = null
-	 * StringUtils.strip("")       = ""
-	 * StringUtils.strip("   ")    = ""
-	 * StringUtils.strip("abc")    = "abc"
-	 * StringUtils.strip("  abc")  = "abc"
-	 * StringUtils.strip("abc  ")  = "abc"
-	 * StringUtils.strip(" abc ")  = "abc"
-	 * StringUtils.strip(" ab c ") = "ab c"
-	 * 
- * - * @param str the String to remove whitespace from, may be null - * @return the stripped String, {@code null} if null String input - */ - public static String strip(final String str) { - return strip(str, null); - } - - /** - *

- * Strips any of a set of characters from the start and end of a String. This is - * similar to {@link String#trim()} but allows the characters to be stripped to - * be controlled. - *

- * - *

- * A {@code null} input String returns {@code null}. An empty string ("") input - * returns the empty string. - *

- * - *

- * If the stripChars String is {@code null}, whitespace is stripped as defined - * by {@link Character#isWhitespace(char)}. Alternatively use - * {@link #strip(String)}. - *

- * - *
-	 * StringUtils.strip(null, *)          = null
-	 * StringUtils.strip("", *)            = ""
-	 * StringUtils.strip("abc", null)      = "abc"
-	 * StringUtils.strip("  abc", null)    = "abc"
-	 * StringUtils.strip("abc  ", null)    = "abc"
-	 * StringUtils.strip(" abc ", null)    = "abc"
-	 * StringUtils.strip("  abcyx", "xyz") = "  abc"
-	 * 
- * - * @param str the String to remove characters from, may be null - * @param stripChars the characters to remove, null treated as whitespace - * @return the stripped String, {@code null} if null String input - */ - public static String strip(String str, final String stripChars) { - str = stripStart(str, stripChars); - return stripEnd(str, stripChars); - } - - /** - *

- * Removes diacritics (~= accents) from a string. The case will not be altered. - *

- *

- * For instance, 'à' will be replaced by 'a'. - *

- *

- * Note that ligatures will be left as is. - *

- * - *
-	 * StringUtils.stripAccents(null)                = null
-	 * StringUtils.stripAccents("")                  = ""
-	 * StringUtils.stripAccents("control")           = "control"
-	 * StringUtils.stripAccents("éclair")     = "eclair"
-	 * 
- * - * @param input String to be stripped - * @return input text with diacritics removed - * - * @since 3.0 - */ - // See also Lucene's ASCIIFoldingFilter (Lucene 2.9) that replaces accented - // characters by their unaccented equivalent (and uncommitted bug fix: - // https://issues.apache.org/jira/browse/LUCENE-1343?focusedCommentId=12858907&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#action_12858907). - public static String stripAccents(final String input) { - if (input == null) { - return null; - } - final StringBuilder decomposed = new StringBuilder(Normalizer.normalize(input, Normalizer.Form.NFD)); - convertRemainingAccentCharacters(decomposed); - // Note that this doesn't correctly remove ligatures... - return STRIP_ACCENTS_PATTERN.matcher(decomposed).replaceAll(EMPTY); - } - - /** - *

- * Strips whitespace from the start and end of every String in an array. - * Whitespace is defined by {@link Character#isWhitespace(char)}. - *

- * - *

- * A new array is returned each time, except for length zero. A {@code null} - * array will return {@code null}. An empty array will return itself. A - * {@code null} array entry will be ignored. - *

- * - *
-	 * StringUtils.stripAll(null)             = null
-	 * StringUtils.stripAll([])               = []
-	 * StringUtils.stripAll(["abc", "  abc"]) = ["abc", "abc"]
-	 * StringUtils.stripAll(["abc  ", null])  = ["abc", null]
-	 * 
- * - * @param strs the array to remove whitespace from, may be null - * @return the stripped Strings, {@code null} if null array input - */ - public static String[] stripAll(final String... strs) { - return stripAll(strs, null); - } - - /** - *

- * Strips any of a set of characters from the start and end of every String in - * an array. - *

- *

- * Whitespace is defined by {@link Character#isWhitespace(char)}. - *

- * - *

- * A new array is returned each time, except for length zero. A {@code null} - * array will return {@code null}. An empty array will return itself. A - * {@code null} array entry will be ignored. A {@code null} stripChars will - * strip whitespace as defined by {@link Character#isWhitespace(char)}. - *

- * - *
-	 * StringUtils.stripAll(null, *)                = null
-	 * StringUtils.stripAll([], *)                  = []
-	 * StringUtils.stripAll(["abc", "  abc"], null) = ["abc", "abc"]
-	 * StringUtils.stripAll(["abc  ", null], null)  = ["abc", null]
-	 * StringUtils.stripAll(["abc  ", null], "yz")  = ["abc  ", null]
-	 * StringUtils.stripAll(["yabcz", null], "yz")  = ["abc", null]
-	 * 
- * - * @param strs the array to remove characters from, may be null - * @param stripChars the characters to remove, null treated as whitespace - * @return the stripped Strings, {@code null} if null array input - */ - public static String[] stripAll(final String[] strs, final String stripChars) { - final int strsLen = strs.length; - if (strsLen == 0) { - return strs; - } - final String[] newArr = new String[strsLen]; - for (int i = 0; i < strsLen; i++) { - newArr[i] = strip(strs[i], stripChars); - } - return newArr; - } - - /** - *

- * Strips any of a set of characters from the end of a String. - *

- * - *

- * A {@code null} input String returns {@code null}. An empty string ("") input - * returns the empty string. - *

- * - *

- * If the stripChars String is {@code null}, whitespace is stripped as defined - * by {@link Character#isWhitespace(char)}. - *

- * - *
-	 * StringUtils.stripEnd(null, *)          = null
-	 * StringUtils.stripEnd("", *)            = ""
-	 * StringUtils.stripEnd("abc", "")        = "abc"
-	 * StringUtils.stripEnd("abc", null)      = "abc"
-	 * StringUtils.stripEnd("  abc", null)    = "  abc"
-	 * StringUtils.stripEnd("abc  ", null)    = "abc"
-	 * StringUtils.stripEnd(" abc ", null)    = " abc"
-	 * StringUtils.stripEnd("  abcyx", "xyz") = "  abc"
-	 * StringUtils.stripEnd("120.00", ".0")   = "12"
-	 * 
- * - * @param str the String to remove characters from, may be null - * @param stripChars the set of characters to remove, null treated as whitespace - * @return the stripped String, {@code null} if null String input - */ - public static String stripEnd(final String str, final String stripChars) { - int end = length(str); - if (end == 0) { - return str; - } - - if (stripChars == null) { - while (end != 0 && Character.isWhitespace(str.charAt(end - 1))) { - end--; - } - } else if (stripChars.isEmpty()) { - return str; - } else { - while (end != 0 && stripChars.indexOf(str.charAt(end - 1)) != INDEX_NOT_FOUND) { - end--; - } - } - return str.substring(0, end); - } - - /** - *

- * Strips any of a set of characters from the start of a String. - *

- * - *

- * A {@code null} input String returns {@code null}. An empty string ("") input - * returns the empty string. - *

- * - *

- * If the stripChars String is {@code null}, whitespace is stripped as defined - * by {@link Character#isWhitespace(char)}. - *

- * - *
-	 * StringUtils.stripStart(null, *)          = null
-	 * StringUtils.stripStart("", *)            = ""
-	 * StringUtils.stripStart("abc", "")        = "abc"
-	 * StringUtils.stripStart("abc", null)      = "abc"
-	 * StringUtils.stripStart("  abc", null)    = "abc"
-	 * StringUtils.stripStart("abc  ", null)    = "abc  "
-	 * StringUtils.stripStart(" abc ", null)    = "abc "
-	 * StringUtils.stripStart("yxabc  ", "xyz") = "abc  "
-	 * 
- * - * @param str the String to remove characters from, may be null - * @param stripChars the characters to remove, null treated as whitespace - * @return the stripped String, {@code null} if null String input - */ - public static String stripStart(final String str, final String stripChars) { - final int strLen = length(str); - if (strLen == 0) { - return str; - } - int start = 0; - if (stripChars == null) { - while (start != strLen && Character.isWhitespace(str.charAt(start))) { - start++; - } - } else if (stripChars.isEmpty()) { - return str; - } else { - while (start != strLen && stripChars.indexOf(str.charAt(start)) != INDEX_NOT_FOUND) { - start++; - } - } - return str.substring(start); - } - - /** - *

- * Strips whitespace from the start and end of a String returning an empty - * String if {@code null} input. - *

- * - *

- * This is similar to {@link #trimToEmpty(String)} but removes whitespace. - * Whitespace is defined by {@link Character#isWhitespace(char)}. - *

- * - *
-	 * StringUtils.stripToEmpty(null)     = ""
-	 * StringUtils.stripToEmpty("")       = ""
-	 * StringUtils.stripToEmpty("   ")    = ""
-	 * StringUtils.stripToEmpty("abc")    = "abc"
-	 * StringUtils.stripToEmpty("  abc")  = "abc"
-	 * StringUtils.stripToEmpty("abc  ")  = "abc"
-	 * StringUtils.stripToEmpty(" abc ")  = "abc"
-	 * StringUtils.stripToEmpty(" ab c ") = "ab c"
-	 * 
- * - * @param str the String to be stripped, may be null - * @return the trimmed String, or an empty String if {@code null} input - * @since 2.0 - */ - public static String stripToEmpty(final String str) { - return str == null ? EMPTY : strip(str, null); - } - - /** - *

- * Strips whitespace from the start and end of a String returning {@code null} - * if the String is empty ("") after the strip. - *

- * - *

- * This is similar to {@link #trimToNull(String)} but removes whitespace. - * Whitespace is defined by {@link Character#isWhitespace(char)}. - *

- * - *
-	 * StringUtils.stripToNull(null)     = null
-	 * StringUtils.stripToNull("")       = null
-	 * StringUtils.stripToNull("   ")    = null
-	 * StringUtils.stripToNull("abc")    = "abc"
-	 * StringUtils.stripToNull("  abc")  = "abc"
-	 * StringUtils.stripToNull("abc  ")  = "abc"
-	 * StringUtils.stripToNull(" abc ")  = "abc"
-	 * StringUtils.stripToNull(" ab c ") = "ab c"
-	 * 
- * - * @param str the String to be stripped, may be null - * @return the stripped String, {@code null} if whitespace, empty or null String - * input - * @since 2.0 - */ - public static String stripToNull(String str) { - if (str == null) { - return null; - } - str = strip(str, null); - return str.isEmpty() ? null : str; // NOSONARLINT str cannot be null here - } - - /** - *

- * Gets a substring from the specified String avoiding exceptions. - *

- * - *

- * A negative start position can be used to start {@code n} characters from the - * end of the String. - *

- * - *

- * A {@code null} String will return {@code null}. An empty ("") String will - * return "". - *

- * - *
-	 * StringUtils.substring(null, *)   = null
-	 * StringUtils.substring("", *)     = ""
-	 * StringUtils.substring("abc", 0)  = "abc"
-	 * StringUtils.substring("abc", 2)  = "c"
-	 * StringUtils.substring("abc", 4)  = ""
-	 * StringUtils.substring("abc", -2) = "bc"
-	 * StringUtils.substring("abc", -4) = "abc"
-	 * 
- * - * @param str the String to get the substring from, may be null - * @param start the position to start from, negative means count back from the - * end of the String by this many characters - * @return substring from start position, {@code null} if null String input - */ - public static String substring(final String str, int start) { - if (str == null) { - return null; - } - - // handle negatives, which means last n characters - if (start < 0) { - start = str.length() + start; // remember start is negative - } - - if (start < 0) { - start = 0; - } - if (start > str.length()) { - return EMPTY; - } - - return str.substring(start); - } - - /** - *

- * Gets a substring from the specified String avoiding exceptions. - *

- * - *

- * A negative start position can be used to start/end {@code n} characters from - * the end of the String. - *

- * - *

- * The returned substring starts with the character in the {@code start} - * position and ends before the {@code end} position. All position counting is - * zero-based -- i.e., to start at the beginning of the string use - * {@code start = 0}. Negative start and end positions can be used to specify - * offsets relative to the end of the String. - *

- * - *

- * If {@code start} is not strictly to the left of {@code end}, "" is returned. - *

- * - *
-	 * StringUtils.substring(null, *, *)    = null
-	 * StringUtils.substring("", * ,  *)    = "";
-	 * StringUtils.substring("abc", 0, 2)   = "ab"
-	 * StringUtils.substring("abc", 2, 0)   = ""
-	 * StringUtils.substring("abc", 2, 4)   = "c"
-	 * StringUtils.substring("abc", 4, 6)   = ""
-	 * StringUtils.substring("abc", 2, 2)   = ""
-	 * StringUtils.substring("abc", -2, -1) = "b"
-	 * StringUtils.substring("abc", -4, 2)  = "ab"
-	 * 
- * - * @param str the String to get the substring from, may be null - * @param start the position to start from, negative means count back from the - * end of the String by this many characters - * @param end the position to end at (exclusive), negative means count back - * from the end of the String by this many characters - * @return substring from start position to end position, {@code null} if null - * String input - */ - public static String substring(final String str, int start, int end) { - if (str == null) { - return null; - } - - // handle negatives - if (end < 0) { - end = str.length() + end; // remember end is negative - } - if (start < 0) { - start = str.length() + start; // remember start is negative - } - - // check length next - if (end > str.length()) { - end = str.length(); - } - - // if start is greater than end, return "" - if (start > end) { - return EMPTY; - } - - if (start < 0) { - start = 0; - } - if (end < 0) { - end = 0; - } - - return str.substring(start, end); - } - - /** - *

- * Gets the substring after the first occurrence of a separator. The separator - * is not returned. - *

- * - *

- * A {@code null} string input will return {@code null}. An empty ("") string - * input will return the empty string. - * - *

- * If nothing is found, the empty string is returned. - *

- * - *
-	 * StringUtils.substringAfter(null, *)      = null
-	 * StringUtils.substringAfter("", *)        = ""
-	 * StringUtils.substringAfter("abc", 'a')   = "bc"
-	 * StringUtils.substringAfter("abcba", 'b') = "cba"
-	 * StringUtils.substringAfter("abc", 'c')   = ""
-	 * StringUtils.substringAfter("abc", 'd')   = ""
-	 * StringUtils.substringAfter(" abc", 32)   = "abc"
-	 * 
- * - * @param str the String to get a substring from, may be null - * @param separator the character to search. - * @return the substring after the first occurrence of the separator, - * {@code null} if null String input - * @since 3.11 - */ - public static String substringAfter(final String str, final int separator) { - if (isEmpty(str)) { - return str; - } - final int pos = str.indexOf(separator); - if (pos == INDEX_NOT_FOUND) { - return EMPTY; - } - return str.substring(pos + 1); - } - - /** - *

- * Gets the substring after the first occurrence of a separator. The separator - * is not returned. - *

- * - *

- * A {@code null} string input will return {@code null}. An empty ("") string - * input will return the empty string. A {@code null} separator will return the - * empty string if the input string is not {@code null}. - *

- * - *

- * If nothing is found, the empty string is returned. - *

- * - *
-	 * StringUtils.substringAfter(null, *)      = null
-	 * StringUtils.substringAfter("", *)        = ""
-	 * StringUtils.substringAfter(*, null)      = ""
-	 * StringUtils.substringAfter("abc", "a")   = "bc"
-	 * StringUtils.substringAfter("abcba", "b") = "cba"
-	 * StringUtils.substringAfter("abc", "c")   = ""
-	 * StringUtils.substringAfter("abc", "d")   = ""
-	 * StringUtils.substringAfter("abc", "")    = "abc"
-	 * 
- * - * @param str the String to get a substring from, may be null - * @param separator the String to search for, may be null - * @return the substring after the first occurrence of the separator, - * {@code null} if null String input - * @since 2.0 - */ - public static String substringAfter(final String str, final String separator) { - if (isEmpty(str)) { - return str; - } - if (separator == null) { - return EMPTY; - } - final int pos = str.indexOf(separator); - if (pos == INDEX_NOT_FOUND) { - return EMPTY; - } - return str.substring(pos + separator.length()); - } - - /** - *

- * Gets the substring after the last occurrence of a separator. The separator is - * not returned. - *

- * - *

- * A {@code null} string input will return {@code null}. An empty ("") string - * input will return the empty string. - * - *

- * If nothing is found, the empty string is returned. - *

- * - *
-	 * StringUtils.substringAfterLast(null, *)      = null
-	 * StringUtils.substringAfterLast("", *)        = ""
-	 * StringUtils.substringAfterLast("abc", 'a')   = "bc"
-	 * StringUtils.substringAfterLast(" bc", 32)    = "bc"
-	 * StringUtils.substringAfterLast("abcba", 'b') = "a"
-	 * StringUtils.substringAfterLast("abc", 'c')   = ""
-	 * StringUtils.substringAfterLast("a", 'a')     = ""
-	 * StringUtils.substringAfterLast("a", 'z')     = ""
-	 * 
- * - * @param str the String to get a substring from, may be null - * @param separator the String to search for, may be null - * @return the substring after the last occurrence of the separator, - * {@code null} if null String input - * @since 3.11 - */ - public static String substringAfterLast(final String str, final int separator) { - if (isEmpty(str)) { - return str; - } - final int pos = str.lastIndexOf(separator); - if (pos == INDEX_NOT_FOUND || pos == str.length() - 1) { - return EMPTY; - } - return str.substring(pos + 1); - } - - /** - *

- * Gets the substring after the last occurrence of a separator. The separator is - * not returned. - *

- * - *

- * A {@code null} string input will return {@code null}. An empty ("") string - * input will return the empty string. An empty or {@code null} separator will - * return the empty string if the input string is not {@code null}. - *

- * - *

- * If nothing is found, the empty string is returned. - *

- * - *
-	 * StringUtils.substringAfterLast(null, *)      = null
-	 * StringUtils.substringAfterLast("", *)        = ""
-	 * StringUtils.substringAfterLast(*, "")        = ""
-	 * StringUtils.substringAfterLast(*, null)      = ""
-	 * StringUtils.substringAfterLast("abc", "a")   = "bc"
-	 * StringUtils.substringAfterLast("abcba", "b") = "a"
-	 * StringUtils.substringAfterLast("abc", "c")   = ""
-	 * StringUtils.substringAfterLast("a", "a")     = ""
-	 * StringUtils.substringAfterLast("a", "z")     = ""
-	 * 
- * - * @param str the String to get a substring from, may be null - * @param separator the String to search for, may be null - * @return the substring after the last occurrence of the separator, - * {@code null} if null String input - * @since 2.0 - */ - public static String substringAfterLast(final String str, final String separator) { - if (isEmpty(str)) { - return str; - } - if (isEmpty(separator)) { - return EMPTY; - } - final int pos = str.lastIndexOf(separator); - if (pos == INDEX_NOT_FOUND || pos == str.length() - separator.length()) { - return EMPTY; - } - return str.substring(pos + separator.length()); - } - - /** - *

- * Gets the substring before the first occurrence of a separator. The separator - * is not returned. - *

- * - *

- * A {@code null} string input will return {@code null}. An empty ("") string - * input will return the empty string. - *

- * - *

- * If nothing is found, the string input is returned. - *

- * - *
-	 * StringUtils.substringBefore(null, *)      = null
-	 * StringUtils.substringBefore("", *)        = ""
-	 * StringUtils.substringBefore("abc", 'a')   = ""
-	 * StringUtils.substringBefore("abcba", 'b') = "a"
-	 * StringUtils.substringBefore("abc", 'c')   = "ab"
-	 * StringUtils.substringBefore("abc", 'd')   = "abc"
-	 * 
- * - * @param str the String to get a substring from, may be null - * @param separator the String to search for, may be null - * @return the substring before the first occurrence of the separator, - * {@code null} if null String input - * @since 3.12.0 - */ - public static String substringBefore(final String str, final int separator) { - if (isEmpty(str)) { - return str; - } - final int pos = str.indexOf(separator); - if (pos == INDEX_NOT_FOUND) { - return str; - } - return str.substring(0, pos); - } - - /** - *

- * Gets the substring before the first occurrence of a separator. The separator - * is not returned. - *

- * - *

- * A {@code null} string input will return {@code null}. An empty ("") string - * input will return the empty string. A {@code null} separator will return the - * input string. - *

- * - *

- * If nothing is found, the string input is returned. - *

- * - *
-	 * StringUtils.substringBefore(null, *)      = null
-	 * StringUtils.substringBefore("", *)        = ""
-	 * StringUtils.substringBefore("abc", "a")   = ""
-	 * StringUtils.substringBefore("abcba", "b") = "a"
-	 * StringUtils.substringBefore("abc", "c")   = "ab"
-	 * StringUtils.substringBefore("abc", "d")   = "abc"
-	 * StringUtils.substringBefore("abc", "")    = ""
-	 * StringUtils.substringBefore("abc", null)  = "abc"
-	 * 
- * - * @param str the String to get a substring from, may be null - * @param separator the String to search for, may be null - * @return the substring before the first occurrence of the separator, - * {@code null} if null String input - * @since 2.0 - */ - public static String substringBefore(final String str, final String separator) { - if (isEmpty(str) || separator == null) { - return str; - } - if (separator.isEmpty()) { - return EMPTY; - } - final int pos = str.indexOf(separator); - if (pos == INDEX_NOT_FOUND) { - return str; - } - return str.substring(0, pos); - } - - /** - *

- * Gets the substring before the last occurrence of a separator. The separator - * is not returned. - *

- * - *

- * A {@code null} string input will return {@code null}. An empty ("") string - * input will return the empty string. An empty or {@code null} separator will - * return the input string. - *

- * - *

- * If nothing is found, the string input is returned. - *

- * - *
-	 * StringUtils.substringBeforeLast(null, *)      = null
-	 * StringUtils.substringBeforeLast("", *)        = ""
-	 * StringUtils.substringBeforeLast("abcba", "b") = "abc"
-	 * StringUtils.substringBeforeLast("abc", "c")   = "ab"
-	 * StringUtils.substringBeforeLast("a", "a")     = ""
-	 * StringUtils.substringBeforeLast("a", "z")     = "a"
-	 * StringUtils.substringBeforeLast("a", null)    = "a"
-	 * StringUtils.substringBeforeLast("a", "")      = "a"
-	 * 
- * - * @param str the String to get a substring from, may be null - * @param separator the String to search for, may be null - * @return the substring before the last occurrence of the separator, - * {@code null} if null String input - * @since 2.0 - */ - public static String substringBeforeLast(final String str, final String separator) { - if (isEmpty(str) || isEmpty(separator)) { - return str; - } - final int pos = str.lastIndexOf(separator); - if (pos == INDEX_NOT_FOUND) { - return str; - } - return str.substring(0, pos); - } - - /** - *

- * Gets the String that is nested in between two instances of the same String. - *

- * - *

- * A {@code null} input String returns {@code null}. A {@code null} tag returns - * {@code null}. - *

- * - *
-	 * StringUtils.substringBetween(null, *)            = null
-	 * StringUtils.substringBetween("", "")             = ""
-	 * StringUtils.substringBetween("", "tag")          = null
-	 * StringUtils.substringBetween("tagabctag", null)  = null
-	 * StringUtils.substringBetween("tagabctag", "")    = ""
-	 * StringUtils.substringBetween("tagabctag", "tag") = "abc"
-	 * 
- * - * @param str the String containing the substring, may be null - * @param tag the String before and after the substring, may be null - * @return the substring, {@code null} if no match - * @since 2.0 - */ - public static String substringBetween(final String str, final String tag) { - return substringBetween(str, tag, tag); - } - - /** - *

- * Gets the String that is nested in between two Strings. Only the first match - * is returned. - *

- * - *

- * A {@code null} input String returns {@code null}. A {@code null} open/close - * returns {@code null} (no match). An empty ("") open and close returns an - * empty string. - *

- * - *
-	 * StringUtils.substringBetween("wx[b]yz", "[", "]") = "b"
-	 * StringUtils.substringBetween(null, *, *)          = null
-	 * StringUtils.substringBetween(*, null, *)          = null
-	 * StringUtils.substringBetween(*, *, null)          = null
-	 * StringUtils.substringBetween("", "", "")          = ""
-	 * StringUtils.substringBetween("", "", "]")         = null
-	 * StringUtils.substringBetween("", "[", "]")        = null
-	 * StringUtils.substringBetween("yabcz", "", "")     = ""
-	 * StringUtils.substringBetween("yabcz", "y", "z")   = "abc"
-	 * StringUtils.substringBetween("yabczyabcz", "y", "z")   = "abc"
-	 * 
- * - * @param str the String containing the substring, may be null - * @param open the String before the substring, may be null - * @param close the String after the substring, may be null - * @return the substring, {@code null} if no match - * @since 2.0 - */ - public static String substringBetween(final String str, final String open, final String close) { - if (!ObjectUtils.allNotNull(str, open, close)) { - return null; - } - final int start = str.indexOf(open); - if (start != INDEX_NOT_FOUND) { - final int end = str.indexOf(close, start + open.length()); - if (end != INDEX_NOT_FOUND) { - return str.substring(start + open.length(), end); - } - } - return null; - } - - /** - *

- * Searches a String for substrings delimited by a start and end tag, returning - * all matching substrings in an array. - *

- * - *

- * A {@code null} input String returns {@code null}. A {@code null} open/close - * returns {@code null} (no match). An empty ("") open/close returns - * {@code null} (no match). - *

- * - *
-	 * StringUtils.substringsBetween("[a][b][c]", "[", "]") = ["a","b","c"]
-	 * StringUtils.substringsBetween(null, *, *)            = null
-	 * StringUtils.substringsBetween(*, null, *)            = null
-	 * StringUtils.substringsBetween(*, *, null)            = null
-	 * StringUtils.substringsBetween("", "[", "]")          = []
-	 * 
- * - * @param str the String containing the substrings, null returns null, empty - * returns empty - * @param open the String identifying the start of the substring, empty returns - * null - * @param close the String identifying the end of the substring, empty returns - * null - * @return a String Array of substrings, or {@code null} if no match - * @since 2.3 - */ - public static String[] substringsBetween(final String str, final String open, final String close) { - if (str == null || isEmpty(open) || isEmpty(close)) { - return null; - } - final int strLen = str.length(); - if (strLen == 0) { - return new String[0]; - } - final int closeLen = close.length(); - final int openLen = open.length(); - final List list = new ArrayList<>(); - int pos = 0; - while (pos < strLen - closeLen) { - int start = str.indexOf(open, pos); - if (start < 0) { - break; - } - start += openLen; - final int end = str.indexOf(close, start); - if (end < 0) { - break; - } - list.add(str.substring(start, end)); - pos = end + closeLen; - } - if (list.isEmpty()) { - return null; - } - return list.toArray(new String[0]); - } - - /** - *

- * Swaps the case of a String changing upper and title case to lower case, and - * lower case to upper case. - *

- * - *
    - *
  • Upper case character converts to Lower case
  • - *
  • Title case character converts to Lower case
  • - *
  • Lower case character converts to Upper case
  • - *
- * - *

- * For a word based algorithm, see - * {@link org.apache.commons.lang3.text.WordUtils#swapCase(String)}. A - * {@code null} input String returns {@code null}. - *

- * - *
-	 * StringUtils.swapCase(null)                 = null
-	 * StringUtils.swapCase("")                   = ""
-	 * StringUtils.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
-	 * 
- * - *

- * NOTE: This method changed in Lang version 2.0. It no longer performs a word - * based algorithm. If you only use ASCII, you will notice no change. That - * functionality is available in org.apache.commons.lang3.text.WordUtils. - *

- * - * @param str the String to swap case, may be null - * @return the changed String, {@code null} if null String input - */ - public static String swapCase(final String str) { - if (isEmpty(str)) { - return str; - } - - final int strLen = str.length(); - final int[] newCodePoints = new int[strLen]; // cannot be longer than the char array - int outOffset = 0; - for (int i = 0; i < strLen;) { - final int oldCodepoint = str.codePointAt(i); - final int newCodePoint; - if (Character.isUpperCase(oldCodepoint) || Character.isTitleCase(oldCodepoint)) { - newCodePoint = Character.toLowerCase(oldCodepoint); - } else if (Character.isLowerCase(oldCodepoint)) { - newCodePoint = Character.toUpperCase(oldCodepoint); - } else { - newCodePoint = oldCodepoint; - } - newCodePoints[outOffset++] = newCodePoint; - i += Character.charCount(newCodePoint); - } - return new String(newCodePoints, 0, outOffset); - } - - /** - *

- * Converts a {@code CharSequence} into an array of code points. - *

- * - *

- * Valid pairs of surrogate code units will be converted into a single - * supplementary code point. Isolated surrogate code units (i.e. a high - * surrogate not followed by a low surrogate or a low surrogate not preceded by - * a high surrogate) will be returned as-is. - *

- * - *
-	 * StringUtils.toCodePoints(null)   =  null
-	 * StringUtils.toCodePoints("")     =  []  // empty array
-	 * 
- * - * @param cs the character sequence to convert - * @return an array of code points - * @since 3.6 - */ - public static int[] toCodePoints(final CharSequence cs) { - if (cs == null) { - return null; - } - if (cs.length() == 0) { - return new int[0]; - } - - final String s = cs.toString(); - final int[] result = new int[s.codePointCount(0, s.length())]; - int index = 0; - for (int i = 0; i < result.length; i++) { - result[i] = s.codePointAt(index); - index += Character.charCount(result[i]); - } - return result; - } - - /** - * Converts a {@code byte[]} to a String using the specified character encoding. - * - * @param bytes the byte array to read from - * @param charset the encoding to use, if null then use the platform default - * @return a new String - * @throws NullPointerException if {@code bytes} is null - * @since 3.2 - * @since 3.3 No longer throws {@link UnsupportedEncodingException}. - */ - public static String toEncodedString(final byte[] bytes, final Charset charset) { - return new String(bytes, Charsets.toCharset(charset)); - } - - /** - * Converts the given source String as a lower-case using the - * {@link Locale#ROOT} locale in a null-safe manner. - * - * @param source A source String or null. - * @return the given source String as a lower-case using the {@link Locale#ROOT} - * locale or null. - * @since 3.10 - */ - public static String toRootLowerCase(final String source) { - return source == null ? null : source.toLowerCase(Locale.ROOT); - } - - /** - * Converts the given source String as a upper-case using the - * {@link Locale#ROOT} locale in a null-safe manner. - * - * @param source A source String or null. - * @return the given source String as a upper-case using the {@link Locale#ROOT} - * locale or null. - * @since 3.10 - */ - public static String toRootUpperCase(final String source) { - return source == null ? null : source.toUpperCase(Locale.ROOT); - } - - /** - * Converts a {@code byte[]} to a String using the specified character encoding. - * - * @param bytes the byte array to read from - * @param charsetName the encoding to use, if null then use the platform default - * @return a new String - * @throws UnsupportedEncodingException If the named charset is not supported - * @throws NullPointerException if the input is null - * @deprecated use {@link StringUtils#toEncodedString(byte[], Charset)} instead - * of String constants in your code - * @since 3.1 - */ - @Deprecated - public static String toString(final byte[] bytes, final String charsetName) throws UnsupportedEncodingException { - return new String(bytes, Charsets.toCharset(charsetName)); - } - - private static String toStringOrEmpty(final Object obj) { - return Objects.toString(obj, EMPTY); - } - - /** - *

- * Removes control characters (char <= 32) from both ends of this String, - * handling {@code null} by returning {@code null}. - *

- * - *

- * The String is trimmed using {@link String#trim()}. Trim removes start and end - * characters <= 32. To strip whitespace use {@link #strip(String)}. - *

- * - *

- * To trim your choice of characters, use the {@link #strip(String, String)} - * methods. - *

- * - *
-	 * StringUtils.trim(null)          = null
-	 * StringUtils.trim("")            = ""
-	 * StringUtils.trim("     ")       = ""
-	 * StringUtils.trim("abc")         = "abc"
-	 * StringUtils.trim("    abc    ") = "abc"
-	 * 
- * - * @param str the String to be trimmed, may be null - * @return the trimmed string, {@code null} if null String input - */ - public static String trim(final String str) { - return str == null ? null : str.trim(); - } - - /** - *

- * Removes control characters (char <= 32) from both ends of this String - * returning an empty String ("") if the String is empty ("") after the trim or - * if it is {@code null}. - * - *

- * The String is trimmed using {@link String#trim()}. Trim removes start and end - * characters <= 32. To strip whitespace use {@link #stripToEmpty(String)}. - *

- * - *
-	 * StringUtils.trimToEmpty(null)          = ""
-	 * StringUtils.trimToEmpty("")            = ""
-	 * StringUtils.trimToEmpty("     ")       = ""
-	 * StringUtils.trimToEmpty("abc")         = "abc"
-	 * StringUtils.trimToEmpty("    abc    ") = "abc"
-	 * 
- * - * @param str the String to be trimmed, may be null - * @return the trimmed String, or an empty String if {@code null} input - * @since 2.0 - */ - public static String trimToEmpty(final String str) { - return str == null ? EMPTY : str.trim(); - } - - /** - *

- * Removes control characters (char <= 32) from both ends of this String - * returning {@code null} if the String is empty ("") after the trim or if it is - * {@code null}. - * - *

- * The String is trimmed using {@link String#trim()}. Trim removes start and end - * characters <= 32. To strip whitespace use {@link #stripToNull(String)}. - *

- * - *
-	 * StringUtils.trimToNull(null)          = null
-	 * StringUtils.trimToNull("")            = null
-	 * StringUtils.trimToNull("     ")       = null
-	 * StringUtils.trimToNull("abc")         = "abc"
-	 * StringUtils.trimToNull("    abc    ") = "abc"
-	 * 
- * - * @param str the String to be trimmed, may be null - * @return the trimmed String, {@code null} if only chars <= 32, empty or - * null String input - * @since 2.0 - */ - public static String trimToNull(final String str) { - final String ts = trim(str); - return isEmpty(ts) ? null : ts; - } - - /** - *

- * Truncates a String. This will turn "Now is the time for all good men" into - * "Now is the time for". - *

- * - *

- * Specifically: - *

- *
    - *
  • If {@code str} is less than {@code maxWidth} characters long, return - * it.
  • - *
  • Else truncate it to {@code substring(str, 0, maxWidth)}.
  • - *
  • If {@code maxWidth} is less than {@code 0}, throw an - * {@code IllegalArgumentException}.
  • - *
  • In no case will it return a String of length greater than - * {@code maxWidth}.
  • - *
- * - *
-	 * StringUtils.truncate(null, 0)       = null
-	 * StringUtils.truncate(null, 2)       = null
-	 * StringUtils.truncate("", 4)         = ""
-	 * StringUtils.truncate("abcdefg", 4)  = "abcd"
-	 * StringUtils.truncate("abcdefg", 6)  = "abcdef"
-	 * StringUtils.truncate("abcdefg", 7)  = "abcdefg"
-	 * StringUtils.truncate("abcdefg", 8)  = "abcdefg"
-	 * StringUtils.truncate("abcdefg", -1) = throws an IllegalArgumentException
-	 * 
- * - * @param str the String to truncate, may be null - * @param maxWidth maximum length of result String, must be positive - * @return truncated String, {@code null} if null String input - * @throws IllegalArgumentException If {@code maxWidth} is less than {@code 0} - * @since 3.5 - */ - public static String truncate(final String str, final int maxWidth) { - return truncate(str, 0, maxWidth); - } - - /** - *

- * Truncates a String. This will turn "Now is the time for all good men" into - * "is the time for all". - *

- * - *

- * Works like {@code truncate(String, int)}, but allows you to specify a "left - * edge" offset. - * - *

- * Specifically: - *

- *
    - *
  • If {@code str} is less than {@code maxWidth} characters long, return - * it.
  • - *
  • Else truncate it to {@code substring(str, offset, maxWidth)}.
  • - *
  • If {@code maxWidth} is less than {@code 0}, throw an - * {@code IllegalArgumentException}.
  • - *
  • If {@code offset} is less than {@code 0}, throw an - * {@code IllegalArgumentException}.
  • - *
  • In no case will it return a String of length greater than - * {@code maxWidth}.
  • - *
- * - *
-	 * StringUtils.truncate(null, 0, 0) = null
-	 * StringUtils.truncate(null, 2, 4) = null
-	 * StringUtils.truncate("", 0, 10) = ""
-	 * StringUtils.truncate("", 2, 10) = ""
-	 * StringUtils.truncate("abcdefghij", 0, 3) = "abc"
-	 * StringUtils.truncate("abcdefghij", 5, 6) = "fghij"
-	 * StringUtils.truncate("raspberry peach", 10, 15) = "peach"
-	 * StringUtils.truncate("abcdefghijklmno", 0, 10) = "abcdefghij"
-	 * StringUtils.truncate("abcdefghijklmno", -1, 10) = throws an IllegalArgumentException
-	 * StringUtils.truncate("abcdefghijklmno", Integer.MIN_VALUE, 10) = throws an IllegalArgumentException
-	 * StringUtils.truncate("abcdefghijklmno", Integer.MIN_VALUE, Integer.MAX_VALUE) = throws an IllegalArgumentException
-	 * StringUtils.truncate("abcdefghijklmno", 0, Integer.MAX_VALUE) = "abcdefghijklmno"
-	 * StringUtils.truncate("abcdefghijklmno", 1, 10) = "bcdefghijk"
-	 * StringUtils.truncate("abcdefghijklmno", 2, 10) = "cdefghijkl"
-	 * StringUtils.truncate("abcdefghijklmno", 3, 10) = "defghijklm"
-	 * StringUtils.truncate("abcdefghijklmno", 4, 10) = "efghijklmn"
-	 * StringUtils.truncate("abcdefghijklmno", 5, 10) = "fghijklmno"
-	 * StringUtils.truncate("abcdefghijklmno", 5, 5) = "fghij"
-	 * StringUtils.truncate("abcdefghijklmno", 5, 3) = "fgh"
-	 * StringUtils.truncate("abcdefghijklmno", 10, 3) = "klm"
-	 * StringUtils.truncate("abcdefghijklmno", 10, Integer.MAX_VALUE) = "klmno"
-	 * StringUtils.truncate("abcdefghijklmno", 13, 1) = "n"
-	 * StringUtils.truncate("abcdefghijklmno", 13, Integer.MAX_VALUE) = "no"
-	 * StringUtils.truncate("abcdefghijklmno", 14, 1) = "o"
-	 * StringUtils.truncate("abcdefghijklmno", 14, Integer.MAX_VALUE) = "o"
-	 * StringUtils.truncate("abcdefghijklmno", 15, 1) = ""
-	 * StringUtils.truncate("abcdefghijklmno", 15, Integer.MAX_VALUE) = ""
-	 * StringUtils.truncate("abcdefghijklmno", Integer.MAX_VALUE, Integer.MAX_VALUE) = ""
-	 * StringUtils.truncate("abcdefghij", 3, -1) = throws an IllegalArgumentException
-	 * StringUtils.truncate("abcdefghij", -2, 4) = throws an IllegalArgumentException
-	 * 
- * - * @param str the String to truncate, may be null - * @param offset left edge of source String - * @param maxWidth maximum length of result String, must be positive - * @return truncated String, {@code null} if null String input - * @throws IllegalArgumentException If {@code offset} or {@code maxWidth} is - * less than {@code 0} - * @since 3.5 - */ - public static String truncate(final String str, final int offset, final int maxWidth) { - if (offset < 0) { - throw new IllegalArgumentException("offset cannot be negative"); - } - if (maxWidth < 0) { - throw new IllegalArgumentException("maxWith cannot be negative"); - } - if (str == null) { - return null; - } - if (offset > str.length()) { - return EMPTY; - } - if (str.length() > maxWidth) { - final int ix = Math.min(offset + maxWidth, str.length()); - return str.substring(offset, ix); - } - return str.substring(offset); - } - - /** - *

- * Uncapitalizes a String, changing the first character to lower case as per - * {@link Character#toLowerCase(int)}. No other characters are changed. - *

- * - *

- * For a word based algorithm, see - * {@link org.apache.commons.lang3.text.WordUtils#uncapitalize(String)}. A - * {@code null} input String returns {@code null}. - *

- * - *
-	 * StringUtils.uncapitalize(null)  = null
-	 * StringUtils.uncapitalize("")    = ""
-	 * StringUtils.uncapitalize("cat") = "cat"
-	 * StringUtils.uncapitalize("Cat") = "cat"
-	 * StringUtils.uncapitalize("CAT") = "cAT"
-	 * 
- * - * @param str the String to uncapitalize, may be null - * @return the uncapitalized String, {@code null} if null String input - * @see org.apache.commons.lang3.text.WordUtils#uncapitalize(String) - * @see #capitalize(String) - * @since 2.0 - */ - public static String uncapitalize(final String str) { - final int strLen = length(str); - if (strLen == 0) { - return str; - } - - final int firstCodepoint = str.codePointAt(0); - final int newCodePoint = Character.toLowerCase(firstCodepoint); - if (firstCodepoint == newCodePoint) { - // already capitalized - return str; - } - - final int[] newCodePoints = new int[strLen]; // cannot be longer than the char array - int outOffset = 0; - newCodePoints[outOffset++] = newCodePoint; // copy the first codepoint - for (int inOffset = Character.charCount(firstCodepoint); inOffset < strLen;) { - final int codepoint = str.codePointAt(inOffset); - newCodePoints[outOffset++] = codepoint; // copy the remaining ones - inOffset += Character.charCount(codepoint); - } - return new String(newCodePoints, 0, outOffset); - } - - /** - *

- * Unwraps a given string from a character. - *

- * - *
-	 * StringUtils.unwrap(null, null)         = null
-	 * StringUtils.unwrap(null, '\0')         = null
-	 * StringUtils.unwrap(null, '1')          = null
-	 * StringUtils.unwrap("a", 'a')           = "a"
-	 * StringUtils.unwrap("aa", 'a')           = ""
-	 * StringUtils.unwrap("\'abc\'", '\'')    = "abc"
-	 * StringUtils.unwrap("AABabcBAA", 'A')   = "ABabcBA"
-	 * StringUtils.unwrap("A", '#')           = "A"
-	 * StringUtils.unwrap("#A", '#')          = "#A"
-	 * StringUtils.unwrap("A#", '#')          = "A#"
-	 * 
- * - * @param str the String to be unwrapped, can be null - * @param wrapChar the character used to unwrap - * @return unwrapped String or the original string if it is not quoted properly - * with the wrapChar - * @since 3.6 - */ - public static String unwrap(final String str, final char wrapChar) { - if (isEmpty(str) || wrapChar == CharUtils.NUL || str.length() == 1) { - return str; - } - - if (str.charAt(0) == wrapChar && str.charAt(str.length() - 1) == wrapChar) { - final int startIndex = 0; - final int endIndex = str.length() - 1; - - return str.substring(startIndex + 1, endIndex); - } - - return str; - } - - /** - *

- * Unwraps a given string from anther string. - *

- * - *
-	 * StringUtils.unwrap(null, null)         = null
-	 * StringUtils.unwrap(null, "")           = null
-	 * StringUtils.unwrap(null, "1")          = null
-	 * StringUtils.unwrap("a", "a")           = "a"
-	 * StringUtils.unwrap("aa", "a")          = ""
-	 * StringUtils.unwrap("\'abc\'", "\'")    = "abc"
-	 * StringUtils.unwrap("\"abc\"", "\"")    = "abc"
-	 * StringUtils.unwrap("AABabcBAA", "AA")  = "BabcB"
-	 * StringUtils.unwrap("A", "#")           = "A"
-	 * StringUtils.unwrap("#A", "#")          = "#A"
-	 * StringUtils.unwrap("A#", "#")          = "A#"
-	 * 
- * - * @param str the String to be unwrapped, can be null - * @param wrapToken the String used to unwrap - * @return unwrapped String or the original string if it is not quoted properly - * with the wrapToken - * @since 3.6 - */ - public static String unwrap(final String str, final String wrapToken) { - if (isEmpty(str) || isEmpty(wrapToken) || str.length() < 2 * wrapToken.length()) { - return str; - } - - if (startsWith(str, wrapToken) && endsWith(str, wrapToken)) { - final int startIndex = str.indexOf(wrapToken); - final int endIndex = str.lastIndexOf(wrapToken); - final int wrapLength = wrapToken.length(); - - if (startIndex != -1 && endIndex != -1) { - return str.substring(startIndex + wrapLength, endIndex); - } - } - - return str; - } - - /** - *

- * Converts a String to upper case as per {@link String#toUpperCase()}. - *

- * - *

- * A {@code null} input String returns {@code null}. - *

- * - *
-	 * StringUtils.upperCase(null)  = null
-	 * StringUtils.upperCase("")    = ""
-	 * StringUtils.upperCase("aBc") = "ABC"
-	 * 
- * - *

- * Note: As described in the documentation for - * {@link String#toUpperCase()}, the result of this method is affected by the - * current locale. For platform-independent case transformations, the method - * {@link #lowerCase(String, Locale)} should be used with a specific locale - * (e.g. {@link Locale#ENGLISH}). - *

- * - * @param str the String to upper case, may be null - * @return the upper cased String, {@code null} if null String input - */ - public static String upperCase(final String str) { - if (str == null) { - return null; - } - return str.toUpperCase(); - } - - /** - *

- * Converts a String to upper case as per {@link String#toUpperCase(Locale)}. - *

- * - *

- * A {@code null} input String returns {@code null}. - *

- * - *
-	 * StringUtils.upperCase(null, Locale.ENGLISH)  = null
-	 * StringUtils.upperCase("", Locale.ENGLISH)    = ""
-	 * StringUtils.upperCase("aBc", Locale.ENGLISH) = "ABC"
-	 * 
- * - * @param str the String to upper case, may be null - * @param locale the locale that defines the case transformation rules, must not - * be null - * @return the upper cased String, {@code null} if null String input - * @since 2.5 - */ - public static String upperCase(final String str, final Locale locale) { - if (str == null) { - return null; - } - return str.toUpperCase(LocaleUtils.toLocale(locale)); - } - - /** - * Returns the string representation of the {@code char} array or null. - * - * @param value the character array. - * @return a String or null - * @see String#valueOf(char[]) - * @since 3.9 - */ - public static String valueOf(final char[] value) { - return value == null ? null : String.valueOf(value); - } - - /** - *

- * Wraps a string with a char. - *

- * - *
-	 * StringUtils.wrap(null, *)        = null
-	 * StringUtils.wrap("", *)          = ""
-	 * StringUtils.wrap("ab", '\0')     = "ab"
-	 * StringUtils.wrap("ab", 'x')      = "xabx"
-	 * StringUtils.wrap("ab", '\'')     = "'ab'"
-	 * StringUtils.wrap("\"ab\"", '\"') = "\"\"ab\"\""
-	 * 
- * - * @param str the string to be wrapped, may be {@code null} - * @param wrapWith the char that will wrap {@code str} - * @return the wrapped string, or {@code null} if {@code str==null} - * @since 3.4 - */ - public static String wrap(final String str, final char wrapWith) { - - if (isEmpty(str) || wrapWith == CharUtils.NUL) { - return str; - } - - return wrapWith + str + wrapWith; - } - - /** - *

- * Wraps a String with another String. - *

- * - *

- * A {@code null} input String returns {@code null}. - *

- * - *
-	 * StringUtils.wrap(null, *)         = null
-	 * StringUtils.wrap("", *)           = ""
-	 * StringUtils.wrap("ab", null)      = "ab"
-	 * StringUtils.wrap("ab", "x")       = "xabx"
-	 * StringUtils.wrap("ab", "\"")      = "\"ab\""
-	 * StringUtils.wrap("\"ab\"", "\"")  = "\"\"ab\"\""
-	 * StringUtils.wrap("ab", "'")       = "'ab'"
-	 * StringUtils.wrap("'abcd'", "'")   = "''abcd''"
-	 * StringUtils.wrap("\"abcd\"", "'") = "'\"abcd\"'"
-	 * StringUtils.wrap("'abcd'", "\"")  = "\"'abcd'\""
-	 * 
- * - * @param str the String to be wrapper, may be null - * @param wrapWith the String that will wrap str - * @return wrapped String, {@code null} if null String input - * @since 3.4 - */ - public static String wrap(final String str, final String wrapWith) { - - if (isEmpty(str) || isEmpty(wrapWith)) { - return str; - } - - return wrapWith.concat(str).concat(wrapWith); - } - - /** - *

- * Wraps a string with a char if that char is missing from the start or end of - * the given string. - *

- * - *

- * A new {@code String} will not be created if {@code str} is already wrapped. - *

- * - *
-	 * StringUtils.wrapIfMissing(null, *)        = null
-	 * StringUtils.wrapIfMissing("", *)          = ""
-	 * StringUtils.wrapIfMissing("ab", '\0')     = "ab"
-	 * StringUtils.wrapIfMissing("ab", 'x')      = "xabx"
-	 * StringUtils.wrapIfMissing("ab", '\'')     = "'ab'"
-	 * StringUtils.wrapIfMissing("\"ab\"", '\"') = "\"ab\""
-	 * StringUtils.wrapIfMissing("/", '/')  = "/"
-	 * StringUtils.wrapIfMissing("a/b/c", '/')  = "/a/b/c/"
-	 * StringUtils.wrapIfMissing("/a/b/c", '/')  = "/a/b/c/"
-	 * StringUtils.wrapIfMissing("a/b/c/", '/')  = "/a/b/c/"
-	 * 
- * - * @param str the string to be wrapped, may be {@code null} - * @param wrapWith the char that will wrap {@code str} - * @return the wrapped string, or {@code null} if {@code str==null} - * @since 3.5 - */ - public static String wrapIfMissing(final String str, final char wrapWith) { - if (isEmpty(str) || wrapWith == CharUtils.NUL) { - return str; - } - final boolean wrapStart = str.charAt(0) != wrapWith; - final boolean wrapEnd = str.charAt(str.length() - 1) != wrapWith; - if (!wrapStart && !wrapEnd) { - return str; - } - - final StringBuilder builder = new StringBuilder(str.length() + 2); - if (wrapStart) { - builder.append(wrapWith); - } - builder.append(str); - if (wrapEnd) { - builder.append(wrapWith); - } - return builder.toString(); - } - - /** - *

- * Wraps a string with a string if that string is missing from the start or end - * of the given string. - *

- * - *

- * A new {@code String} will not be created if {@code str} is already wrapped. - *

- * - *
-	 * StringUtils.wrapIfMissing(null, *)         = null
-	 * StringUtils.wrapIfMissing("", *)           = ""
-	 * StringUtils.wrapIfMissing("ab", null)      = "ab"
-	 * StringUtils.wrapIfMissing("ab", "x")       = "xabx"
-	 * StringUtils.wrapIfMissing("ab", "\"")      = "\"ab\""
-	 * StringUtils.wrapIfMissing("\"ab\"", "\"")  = "\"ab\""
-	 * StringUtils.wrapIfMissing("ab", "'")       = "'ab'"
-	 * StringUtils.wrapIfMissing("'abcd'", "'")   = "'abcd'"
-	 * StringUtils.wrapIfMissing("\"abcd\"", "'") = "'\"abcd\"'"
-	 * StringUtils.wrapIfMissing("'abcd'", "\"")  = "\"'abcd'\""
-	 * StringUtils.wrapIfMissing("/", "/")  = "/"
-	 * StringUtils.wrapIfMissing("a/b/c", "/")  = "/a/b/c/"
-	 * StringUtils.wrapIfMissing("/a/b/c", "/")  = "/a/b/c/"
-	 * StringUtils.wrapIfMissing("a/b/c/", "/")  = "/a/b/c/"
-	 * 
- * - * @param str the string to be wrapped, may be {@code null} - * @param wrapWith the string that will wrap {@code str} - * @return the wrapped string, or {@code null} if {@code str==null} - * @since 3.5 - */ - public static String wrapIfMissing(final String str, final String wrapWith) { - if (isEmpty(str) || isEmpty(wrapWith)) { - return str; - } - - final boolean wrapStart = !str.startsWith(wrapWith); - final boolean wrapEnd = !str.endsWith(wrapWith); - if (!wrapStart && !wrapEnd) { - return str; - } - - final StringBuilder builder = new StringBuilder(str.length() + wrapWith.length() + wrapWith.length()); - if (wrapStart) { - builder.append(wrapWith); - } - builder.append(str); - if (wrapEnd) { - builder.append(wrapWith); - } - return builder.toString(); - } - - /** - *

- * {@code StringUtils} instances should NOT be constructed in standard - * programming. Instead, the class should be used as - * {@code StringUtils.trim(" foo ");}. - *

- * - *

- * This constructor is public to permit tools that require a JavaBean instance - * to operate. - *

- */ - public StringUtils() { - } - -} diff --git a/src/main/java/org/apache/commons/lang3/SystemUtils.java b/src/main/java/org/apache/commons/lang3/SystemUtils.java deleted file mode 100755 index 456b1c21..00000000 --- a/src/main/java/org/apache/commons/lang3/SystemUtils.java +++ /dev/null @@ -1,1968 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -/** - *

- * Helpers for {@code java.lang.System}. - *

- *

- * If a system property cannot be read due to security restrictions, the - * corresponding field in this class will be set to {@code null} and a message - * will be written to {@code System.err}. - *

- *

- * #ThreadSafe# - *

- * - * @since 1.0 - */ -public class SystemUtils { - - /** - * The prefix String for all Windows OS. - */ - private static final String OS_NAME_WINDOWS_PREFIX = "Windows"; - - // System property constants - // ----------------------------------------------------------------------- - // These MUST be declared first. Other constants depend on this. - - /** - * The System property key for the user home directory. - */ - private static final String USER_HOME_KEY = "user.home"; - - /** - * The System property key for the user name. - */ - private static final String USER_NAME_KEY = "user.name"; - - /** - * The System property key for the user directory. - */ - private static final String USER_DIR_KEY = "user.dir"; - - /** - * The System property key for the Java IO temporary directory. - */ - private static final String JAVA_IO_TMPDIR_KEY = "java.io.tmpdir"; - - /** - * The System property key for the Java home directory. - */ - private static final String JAVA_HOME_KEY = "java.home"; - - /** - *

- * The {@code awt.toolkit} System Property. - *

- *

- * Holds a class name, on Windows XP this is {@code sun.awt.windows.WToolkit}. - *

- *

- * On platforms without a GUI, this value is {@code null}. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since 2.1 - */ - public static final String AWT_TOOLKIT = getSystemProperty("awt.toolkit"); - - /** - *

- * The {@code file.encoding} System Property. - *

- *

- * File encoding, such as {@code Cp1252}. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since 2.0 - * @since Java 1.2 - */ - public static final String FILE_ENCODING = getSystemProperty("file.encoding"); - - /** - *

- * The {@code file.separator} System Property. The file separator is: - *

- *
    - *
  • {@code "/"} on UNIX
  • - *
  • {@code "\"} on Windows.
  • - *
- * - *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @deprecated Use {@link File#separator}, since it is guaranteed to be a string - * containing a single character and it does not require a privilege - * check. - * @since Java 1.1 - */ - @Deprecated - public static final String FILE_SEPARATOR = getSystemProperty("file.separator"); - - /** - *

- * The {@code java.awt.fonts} System Property. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since 2.1 - */ - public static final String JAVA_AWT_FONTS = getSystemProperty("java.awt.fonts"); - - /** - *

- * The {@code java.awt.graphicsenv} System Property. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since 2.1 - */ - public static final String JAVA_AWT_GRAPHICSENV = getSystemProperty("java.awt.graphicsenv"); - - /** - *

- * The {@code java.awt.headless} System Property. The value of this property is - * the String {@code "true"} or {@code "false"}. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @see #isJavaAwtHeadless() - * @since 2.1 - * @since Java 1.4 - */ - public static final String JAVA_AWT_HEADLESS = getSystemProperty("java.awt.headless"); - - /** - *

- * The {@code java.awt.printerjob} System Property. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since 2.1 - */ - public static final String JAVA_AWT_PRINTERJOB = getSystemProperty("java.awt.printerjob"); - - /** - *

- * The {@code java.class.path} System Property. Java class path. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.1 - */ - public static final String JAVA_CLASS_PATH = getSystemProperty("java.class.path"); - - /** - *

- * The {@code java.class.version} System Property. Java class format version - * number. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.1 - */ - public static final String JAVA_CLASS_VERSION = getSystemProperty("java.class.version"); - - /** - *

- * The {@code java.compiler} System Property. Name of JIT compiler to use. First - * in JDK version 1.2. Not used in Sun JDKs after 1.2. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.2. Not used in Sun versions after 1.2. - */ - public static final String JAVA_COMPILER = getSystemProperty("java.compiler"); - - /** - *

- * The {@code java.endorsed.dirs} System Property. Path of endorsed directory or - * directories. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.4 - */ - public static final String JAVA_ENDORSED_DIRS = getSystemProperty("java.endorsed.dirs"); - - /** - *

- * The {@code java.ext.dirs} System Property. Path of extension directory or - * directories. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.3 - */ - public static final String JAVA_EXT_DIRS = getSystemProperty("java.ext.dirs"); - - /** - *

- * The {@code java.home} System Property. Java installation directory. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.1 - */ - public static final String JAVA_HOME = getSystemProperty(JAVA_HOME_KEY); - - /** - *

- * The {@code java.io.tmpdir} System Property. Default temp file path. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.2 - */ - public static final String JAVA_IO_TMPDIR = getSystemProperty(JAVA_IO_TMPDIR_KEY); - - /** - *

- * The {@code java.library.path} System Property. List of paths to search when - * loading libraries. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.2 - */ - public static final String JAVA_LIBRARY_PATH = getSystemProperty("java.library.path"); - - /** - *

- * The {@code java.runtime.name} System Property. Java Runtime Environment name. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since 2.0 - * @since Java 1.3 - */ - public static final String JAVA_RUNTIME_NAME = getSystemProperty("java.runtime.name"); - - /** - *

- * The {@code java.runtime.version} System Property. Java Runtime Environment - * version. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since 2.0 - * @since Java 1.3 - */ - public static final String JAVA_RUNTIME_VERSION = getSystemProperty("java.runtime.version"); - - /** - *

- * The {@code java.specification.name} System Property. Java Runtime Environment - * specification name. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.2 - */ - public static final String JAVA_SPECIFICATION_NAME = getSystemProperty("java.specification.name"); - - /** - *

- * The {@code java.specification.vendor} System Property. Java Runtime - * Environment specification vendor. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.2 - */ - public static final String JAVA_SPECIFICATION_VENDOR = getSystemProperty("java.specification.vendor"); - - /** - *

- * The {@code java.specification.version} System Property. Java Runtime - * Environment specification version. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.3 - */ - public static final String JAVA_SPECIFICATION_VERSION = getSystemProperty("java.specification.version"); - private static final JavaVersion JAVA_SPECIFICATION_VERSION_AS_ENUM = JavaVersion.get(JAVA_SPECIFICATION_VERSION); - - /** - *

- * The {@code java.util.prefs.PreferencesFactory} System Property. A class name. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since 2.1 - * @since Java 1.4 - */ - public static final String JAVA_UTIL_PREFS_PREFERENCES_FACTORY = getSystemProperty( - "java.util.prefs.PreferencesFactory"); - - /** - *

- * The {@code java.vendor} System Property. Java vendor-specific string. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.1 - */ - public static final String JAVA_VENDOR = getSystemProperty("java.vendor"); - - /** - *

- * The {@code java.vendor.url} System Property. Java vendor URL. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.1 - */ - public static final String JAVA_VENDOR_URL = getSystemProperty("java.vendor.url"); - - /** - *

- * The {@code java.version} System Property. Java version number. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.1 - */ - public static final String JAVA_VERSION = getSystemProperty("java.version"); - - /** - *

- * The {@code java.vm.info} System Property. Java Virtual Machine implementation - * info. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since 2.0 - * @since Java 1.2 - */ - public static final String JAVA_VM_INFO = getSystemProperty("java.vm.info"); - - /** - *

- * The {@code java.vm.name} System Property. Java Virtual Machine implementation - * name. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.2 - */ - public static final String JAVA_VM_NAME = getSystemProperty("java.vm.name"); - - /** - *

- * The {@code java.vm.specification.name} System Property. Java Virtual Machine - * specification name. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.2 - */ - public static final String JAVA_VM_SPECIFICATION_NAME = getSystemProperty("java.vm.specification.name"); - - /** - *

- * The {@code java.vm.specification.vendor} System Property. Java Virtual - * Machine specification vendor. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.2 - */ - public static final String JAVA_VM_SPECIFICATION_VENDOR = getSystemProperty("java.vm.specification.vendor"); - - /** - *

- * The {@code java.vm.specification.version} System Property. Java Virtual - * Machine specification version. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.2 - */ - public static final String JAVA_VM_SPECIFICATION_VERSION = getSystemProperty("java.vm.specification.version"); - - /** - *

- * The {@code java.vm.vendor} System Property. Java Virtual Machine - * implementation vendor. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.2 - */ - public static final String JAVA_VM_VENDOR = getSystemProperty("java.vm.vendor"); - - /** - *

- * The {@code java.vm.version} System Property. Java Virtual Machine - * implementation version. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.2 - */ - public static final String JAVA_VM_VERSION = getSystemProperty("java.vm.version"); - - /** - *

- * The {@code line.separator} System Property. Line separator - * ({@code "\n"} on UNIX). - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @deprecated Use {@link System#lineSeparator()} instead, since it does not - * require a privilege check. - * @since Java 1.1 - */ - @Deprecated - public static final String LINE_SEPARATOR = getSystemProperty("line.separator"); - - /** - *

- * The {@code os.arch} System Property. Operating system architecture. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.1 - */ - public static final String OS_ARCH = getSystemProperty("os.arch"); - - /** - *

- * The {@code os.name} System Property. Operating system name. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.1 - */ - public static final String OS_NAME = getSystemProperty("os.name"); - - /** - *

- * The {@code os.version} System Property. Operating system version. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.1 - */ - public static final String OS_VERSION = getSystemProperty("os.version"); - - /** - *

- * The {@code path.separator} System Property. Path separator - * ({@code ":"} on UNIX). - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @deprecated Use {@link File#pathSeparator}, since it is guaranteed to be a - * string containing a single character and it does not require a - * privilege check. - * @since Java 1.1 - */ - @Deprecated - public static final String PATH_SEPARATOR = getSystemProperty("path.separator"); - - /** - *

- * The {@code user.country} or {@code user.region} System Property. User's - * country code, such as {@code GB}. First in Java version 1.2 as - * {@code user.region}. Renamed to {@code user.country} in 1.4 - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since 2.0 - * @since Java 1.2 - */ - public static final String USER_COUNTRY = getSystemProperty("user.country") == null - ? getSystemProperty("user.region") - : getSystemProperty("user.country"); - - /** - *

- * The {@code user.dir} System Property. User's current working directory. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.1 - */ - public static final String USER_DIR = getSystemProperty(USER_DIR_KEY); - - /** - *

- * The {@code user.home} System Property. User's home directory. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.1 - */ - public static final String USER_HOME = getSystemProperty(USER_HOME_KEY); - - /** - *

- * The {@code user.language} System Property. User's language code, such as - * {@code "en"}. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since 2.0 - * @since Java 1.2 - */ - public static final String USER_LANGUAGE = getSystemProperty("user.language"); - - /** - *

- * The {@code user.name} System Property. User's account name. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since Java 1.1 - */ - public static final String USER_NAME = getSystemProperty(USER_NAME_KEY); - - /** - *

- * The {@code user.timezone} System Property. For example: - * {@code "America/Los_Angeles"}. - *

- *

- * Defaults to {@code null} if the runtime does not have security access to read - * this property or the property does not exist. - *

- *

- * This value is initialized when the class is loaded. If - * {@link System#setProperty(String,String)} or - * {@link System#setProperties(java.util.Properties)} is called after this class - * is loaded, the value will be out of sync with that System property. - *

- * - * @since 2.1 - */ - public static final String USER_TIMEZONE = getSystemProperty("user.timezone"); - - // Java version checks - // ----------------------------------------------------------------------- - // These MUST be declared after those above as they depend on the - // values being set up - - /** - *

- * Is {@code true} if this is Java version 1.1 (also 1.1.x versions). - *

- *

- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. - *

- */ - public static final boolean IS_JAVA_1_1 = getJavaVersionMatches("1.1"); - - /** - *

- * Is {@code true} if this is Java version 1.2 (also 1.2.x versions). - *

- *

- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. - *

- */ - public static final boolean IS_JAVA_1_2 = getJavaVersionMatches("1.2"); - - /** - *

- * Is {@code true} if this is Java version 1.3 (also 1.3.x versions). - *

- *

- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. - *

- */ - public static final boolean IS_JAVA_1_3 = getJavaVersionMatches("1.3"); - - /** - *

- * Is {@code true} if this is Java version 1.4 (also 1.4.x versions). - *

- *

- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. - *

- */ - public static final boolean IS_JAVA_1_4 = getJavaVersionMatches("1.4"); - - /** - *

- * Is {@code true} if this is Java version 1.5 (also 1.5.x versions). - *

- *

- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. - *

- */ - public static final boolean IS_JAVA_1_5 = getJavaVersionMatches("1.5"); - - /** - *

- * Is {@code true} if this is Java version 1.6 (also 1.6.x versions). - *

- *

- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. - *

- */ - public static final boolean IS_JAVA_1_6 = getJavaVersionMatches("1.6"); - - /** - *

- * Is {@code true} if this is Java version 1.7 (also 1.7.x versions). - *

- *

- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. - *

- * - * @since 3.0 - */ - public static final boolean IS_JAVA_1_7 = getJavaVersionMatches("1.7"); - - /** - *

- * Is {@code true} if this is Java version 1.8 (also 1.8.x versions). - *

- *

- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. - *

- * - * @since 3.3.2 - */ - public static final boolean IS_JAVA_1_8 = getJavaVersionMatches("1.8"); - - /** - *

- * Is {@code true} if this is Java version 1.9 (also 1.9.x versions). - *

- *

- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. - *

- * - * @since 3.4 - * - * @deprecated As of release 3.5, replaced by {@link #IS_JAVA_9} - */ - @Deprecated - public static final boolean IS_JAVA_1_9 = getJavaVersionMatches("9"); - - /** - *

- * Is {@code true} if this is Java version 9 (also 9.x versions). - *

- *

- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. - *

- * - * @since 3.5 - */ - public static final boolean IS_JAVA_9 = getJavaVersionMatches("9"); - - /** - *

- * Is {@code true} if this is Java version 10 (also 10.x versions). - *

- *

- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. - *

- * - * @since 3.7 - */ - public static final boolean IS_JAVA_10 = getJavaVersionMatches("10"); - - /** - *

- * Is {@code true} if this is Java version 11 (also 11.x versions). - *

- *

- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. - *

- * - * @since 3.8 - */ - public static final boolean IS_JAVA_11 = getJavaVersionMatches("11"); - - /** - *

- * Is {@code true} if this is Java version 12 (also 12.x versions). - *

- *

- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. - *

- * - * @since 3.9 - */ - public static final boolean IS_JAVA_12 = getJavaVersionMatches("12"); - - /** - *

- * Is {@code true} if this is Java version 13 (also 13.x versions). - *

- *

- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. - *

- * - * @since 3.9 - */ - public static final boolean IS_JAVA_13 = getJavaVersionMatches("13"); - - /** - *

- * Is {@code true} if this is Java version 14 (also 14.x versions). - *

- *

- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. - *

- * - * @since 3.10 - */ - public static final boolean IS_JAVA_14 = getJavaVersionMatches("14"); - - /** - *

- * Is {@code true} if this is Java version 15 (also 15.x versions). - *

- *

- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. - *

- * - * @since 3.10 - */ - public static final boolean IS_JAVA_15 = getJavaVersionMatches("15"); - - // Operating system checks - // ----------------------------------------------------------------------- - // These MUST be declared after those above as they depend on the - // values being set up - // OS names from http://www.vamphq.com/os.html - // Selected ones included - please advise dev@commons.apache.org - // if you want another added or a mistake corrected - - /** - *

- * Is {@code true} if this is AIX. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 2.0 - */ - public static final boolean IS_OS_AIX = getOsMatchesName("AIX"); - - /** - *

- * Is {@code true} if this is HP-UX. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 2.0 - */ - public static final boolean IS_OS_HP_UX = getOsMatchesName("HP-UX"); - - /** - *

- * Is {@code true} if this is IBM OS/400. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.3 - */ - public static final boolean IS_OS_400 = getOsMatchesName("OS/400"); - - /** - *

- * Is {@code true} if this is Irix. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 2.0 - */ - public static final boolean IS_OS_IRIX = getOsMatchesName("Irix"); - - /** - *

- * Is {@code true} if this is Linux. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 2.0 - */ - public static final boolean IS_OS_LINUX = getOsMatchesName("Linux") || getOsMatchesName("LINUX"); - - /** - *

- * Is {@code true} if this is Mac. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 2.0 - */ - public static final boolean IS_OS_MAC = getOsMatchesName("Mac"); - - /** - *

- * Is {@code true} if this is Mac. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 2.0 - */ - public static final boolean IS_OS_MAC_OSX = getOsMatchesName("Mac OS X"); - - /** - *

- * Is {@code true} if this is Mac OS X Cheetah. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.4 - */ - public static final boolean IS_OS_MAC_OSX_CHEETAH = getOsMatches("Mac OS X", "10.0"); - - /** - *

- * Is {@code true} if this is Mac OS X Puma. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.4 - */ - public static final boolean IS_OS_MAC_OSX_PUMA = getOsMatches("Mac OS X", "10.1"); - - /** - *

- * Is {@code true} if this is Mac OS X Jaguar. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.4 - */ - public static final boolean IS_OS_MAC_OSX_JAGUAR = getOsMatches("Mac OS X", "10.2"); - - /** - *

- * Is {@code true} if this is Mac OS X Panther. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.4 - */ - public static final boolean IS_OS_MAC_OSX_PANTHER = getOsMatches("Mac OS X", "10.3"); - - /** - *

- * Is {@code true} if this is Mac OS X Tiger. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.4 - */ - public static final boolean IS_OS_MAC_OSX_TIGER = getOsMatches("Mac OS X", "10.4"); - - /** - *

- * Is {@code true} if this is Mac OS X Leopard. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.4 - */ - public static final boolean IS_OS_MAC_OSX_LEOPARD = getOsMatches("Mac OS X", "10.5"); - - /** - *

- * Is {@code true} if this is Mac OS X Snow Leopard. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.4 - */ - public static final boolean IS_OS_MAC_OSX_SNOW_LEOPARD = getOsMatches("Mac OS X", "10.6"); - - /** - *

- * Is {@code true} if this is Mac OS X Lion. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.4 - */ - public static final boolean IS_OS_MAC_OSX_LION = getOsMatches("Mac OS X", "10.7"); - - /** - *

- * Is {@code true} if this is Mac OS X Mountain Lion. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.4 - */ - public static final boolean IS_OS_MAC_OSX_MOUNTAIN_LION = getOsMatches("Mac OS X", "10.8"); - - /** - *

- * Is {@code true} if this is Mac OS X Mavericks. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.4 - */ - public static final boolean IS_OS_MAC_OSX_MAVERICKS = getOsMatches("Mac OS X", "10.9"); - - /** - *

- * Is {@code true} if this is Mac OS X Yosemite. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.4 - */ - public static final boolean IS_OS_MAC_OSX_YOSEMITE = getOsMatches("Mac OS X", "10.10"); - - /** - *

- * Is {@code true} if this is Mac OS X El Capitan. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.5 - */ - public static final boolean IS_OS_MAC_OSX_EL_CAPITAN = getOsMatches("Mac OS X", "10.11"); - - /** - *

- * Is {@code true} if this is Mac OS X Sierra. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.12.0 - */ - public static final boolean IS_OS_MAC_OSX_SIERRA = getOsMatches("Mac OS X", "10.12"); - - /** - *

- * Is {@code true} if this is Mac OS X High Sierra. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.12.0 - */ - public static final boolean IS_OS_MAC_OSX_HIGH_SIERRA = getOsMatches("Mac OS X", "10.13"); - - /** - *

- * Is {@code true} if this is Mac OS X Mojave. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.12.0 - */ - public static final boolean IS_OS_MAC_OSX_MOJAVE = getOsMatches("Mac OS X", "10.14"); - - /** - *

- * Is {@code true} if this is Mac OS X Catalina. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.12.0 - */ - public static final boolean IS_OS_MAC_OSX_CATALINA = getOsMatches("Mac OS X", "10.15"); - - /** - *

- * Is {@code true} if this is Mac OS X Big Sur. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.12.0 - */ - public static final boolean IS_OS_MAC_OSX_BIG_SUR = getOsMatches("Mac OS X", "10.16"); - - /** - *

- * Is {@code true} if this is FreeBSD. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.1 - */ - public static final boolean IS_OS_FREE_BSD = getOsMatchesName("FreeBSD"); - - /** - *

- * Is {@code true} if this is OpenBSD. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.1 - */ - public static final boolean IS_OS_OPEN_BSD = getOsMatchesName("OpenBSD"); - - /** - *

- * Is {@code true} if this is NetBSD. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.1 - */ - public static final boolean IS_OS_NET_BSD = getOsMatchesName("NetBSD"); - - /** - *

- * Is {@code true} if this is OS/2. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 2.0 - */ - public static final boolean IS_OS_OS2 = getOsMatchesName("OS/2"); - - /** - *

- * Is {@code true} if this is Solaris. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 2.0 - */ - public static final boolean IS_OS_SOLARIS = getOsMatchesName("Solaris"); - - /** - *

- * Is {@code true} if this is SunOS. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 2.0 - */ - public static final boolean IS_OS_SUN_OS = getOsMatchesName("SunOS"); - - /** - *

- * Is {@code true} if this is a UNIX like system, as in any of AIX, HP-UX, Irix, - * Linux, MacOSX, Solaris or SUN OS. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 2.1 - */ - public static final boolean IS_OS_UNIX = IS_OS_AIX || IS_OS_HP_UX || IS_OS_IRIX || IS_OS_LINUX || IS_OS_MAC_OSX - || IS_OS_SOLARIS || IS_OS_SUN_OS || IS_OS_FREE_BSD || IS_OS_OPEN_BSD || IS_OS_NET_BSD; - - /** - *

- * Is {@code true} if this is Windows. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 2.0 - */ - public static final boolean IS_OS_WINDOWS = getOsMatchesName(OS_NAME_WINDOWS_PREFIX); - - /** - *

- * Is {@code true} if this is Windows 2000. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 2.0 - */ - public static final boolean IS_OS_WINDOWS_2000 = getOsMatchesName(OS_NAME_WINDOWS_PREFIX + " 2000"); - - /** - *

- * Is {@code true} if this is Windows 2003. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.1 - */ - public static final boolean IS_OS_WINDOWS_2003 = getOsMatchesName(OS_NAME_WINDOWS_PREFIX + " 2003"); - - /** - *

- * Is {@code true} if this is Windows Server 2008. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.1 - */ - public static final boolean IS_OS_WINDOWS_2008 = getOsMatchesName(OS_NAME_WINDOWS_PREFIX + " Server 2008"); - - /** - *

- * Is {@code true} if this is Windows Server 2012. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.4 - */ - public static final boolean IS_OS_WINDOWS_2012 = getOsMatchesName(OS_NAME_WINDOWS_PREFIX + " Server 2012"); - - /** - *

- * Is {@code true} if this is Windows 95. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 2.0 - */ - public static final boolean IS_OS_WINDOWS_95 = getOsMatchesName(OS_NAME_WINDOWS_PREFIX + " 95"); - - /** - *

- * Is {@code true} if this is Windows 98. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 2.0 - */ - public static final boolean IS_OS_WINDOWS_98 = getOsMatchesName(OS_NAME_WINDOWS_PREFIX + " 98"); - - /** - *

- * Is {@code true} if this is Windows ME. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 2.0 - */ - public static final boolean IS_OS_WINDOWS_ME = getOsMatchesName(OS_NAME_WINDOWS_PREFIX + " Me"); - - /** - *

- * Is {@code true} if this is Windows NT. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 2.0 - */ - public static final boolean IS_OS_WINDOWS_NT = getOsMatchesName(OS_NAME_WINDOWS_PREFIX + " NT"); - - /** - *

- * Is {@code true} if this is Windows XP. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 2.0 - */ - public static final boolean IS_OS_WINDOWS_XP = getOsMatchesName(OS_NAME_WINDOWS_PREFIX + " XP"); - - // ----------------------------------------------------------------------- - /** - *

- * Is {@code true} if this is Windows Vista. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 2.4 - */ - public static final boolean IS_OS_WINDOWS_VISTA = getOsMatchesName(OS_NAME_WINDOWS_PREFIX + " Vista"); - - /** - *

- * Is {@code true} if this is Windows 7. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.0 - */ - public static final boolean IS_OS_WINDOWS_7 = getOsMatchesName(OS_NAME_WINDOWS_PREFIX + " 7"); - - /** - *

- * Is {@code true} if this is Windows 8. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.2 - */ - public static final boolean IS_OS_WINDOWS_8 = getOsMatchesName(OS_NAME_WINDOWS_PREFIX + " 8"); - - /** - *

- * Is {@code true} if this is Windows 10. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.5 - */ - public static final boolean IS_OS_WINDOWS_10 = getOsMatchesName(OS_NAME_WINDOWS_PREFIX + " 10"); - - /** - *

- * Is {@code true} if this is z/OS. - *

- *

- * The field will return {@code false} if {@code OS_NAME} is {@code null}. - *

- * - * @since 3.5 - */ - // Values on a z/OS system I tested (Gary Gregory - 2016-03-12) - // os.arch = s390x - // os.encoding = ISO8859_1 - // os.name = z/OS - // os.version = 02.02.00 - public static final boolean IS_OS_ZOS = getOsMatchesName("z/OS"); - - /** - *

- * Gets an environment variable, defaulting to {@code defaultValue} if the - * variable cannot be read. - *

- *

- * If a {@code SecurityException} is caught, the return value is - * {@code defaultValue} and a message is written to {@code System.err}. - *

- * - * @param name the environment variable name - * @param defaultValue the default value - * @return the environment variable value or {@code defaultValue} if a security - * problem occurs - * @since 3.8 - */ - public static String getEnvironmentVariable(final String name, final String defaultValue) { - try { - final String value = System.getenv(name); - return value == null ? defaultValue : value; - } catch (final SecurityException ex) { - // we are not allowed to look at this property - // System.err.println("Caught a SecurityException reading the environment - // variable '" + name + "'."); - return defaultValue; - } - } - - /** - * Gets the host name from an environment variable (COMPUTERNAME on Windows, - * HOSTNAME elsewhere). - * - *

- * If you want to know what the network stack says is the host name, you should - * use {@code InetAddress.getLocalHost().getHostName()}. - *

- * - * @return the host name. Will be {@code null} if the environment variable is - * not defined. - * @since 3.6 - */ - public static String getHostName() { - return IS_OS_WINDOWS ? System.getenv("COMPUTERNAME") : System.getenv("HOSTNAME"); - } - - /** - *

- * Decides if the Java version matches. - *

- * - * @param versionPrefix the prefix for the java version - * @return true if matches, or false if not or can't determine - */ - private static boolean getJavaVersionMatches(final String versionPrefix) { - return isJavaVersionMatch(JAVA_SPECIFICATION_VERSION, versionPrefix); - } - - /** - * Decides if the operating system matches. - * - * @param osNamePrefix the prefix for the OS name - * @param osVersionPrefix the prefix for the version - * @return true if matches, or false if not or can't determine - */ - private static boolean getOsMatches(final String osNamePrefix, final String osVersionPrefix) { - return isOSMatch(OS_NAME, OS_VERSION, osNamePrefix, osVersionPrefix); - } - - /** - * Decides if the operating system matches. - * - * @param osNamePrefix the prefix for the OS name - * @return true if matches, or false if not or can't determine - */ - private static boolean getOsMatchesName(final String osNamePrefix) { - return isOSNameMatch(OS_NAME, osNamePrefix); - } - - // ----------------------------------------------------------------------- - /** - *

- * Gets a System property, defaulting to {@code null} if the property cannot be - * read. - *

- *

- * If a {@code SecurityException} is caught, the return value is {@code null} - * and a message is written to {@code System.err}. - *

- * - * @param property the system property name - * @return the system property value or {@code null} if a security problem - * occurs - */ - private static String getSystemProperty(final String property) { - try { - return System.getProperty(property); - } catch (final SecurityException ex) { - // we are not allowed to look at this property - // System.err.println("Caught a SecurityException reading the system property '" - // + property - // + "'; the SystemUtils property value will default to null."); - return null; - } - } - - /** - *

- * Gets the user name. - *

- * - * @return a name - * @throws SecurityException if a security manager exists and its - * {@code checkPropertyAccess} method doesn't allow - * access to the specified system property. - * @see System#getProperty(String) - * @since 3.10 - */ - public static String getUserName() { - return System.getProperty(USER_NAME_KEY); - } - - /** - *

- * Gets the user name. - *

- * - * @param defaultValue A default value. - * @return a name - * @throws SecurityException if a security manager exists and its - * {@code checkPropertyAccess} method doesn't allow - * access to the specified system property. - * @see System#getProperty(String) - * @since 3.10 - */ - public static String getUserName(final String defaultValue) { - return System.getProperty(USER_NAME_KEY, defaultValue); - } - - /** - * Returns whether the {@link #JAVA_AWT_HEADLESS} value is {@code true}. - * - * @return {@code true} if {@code JAVA_AWT_HEADLESS} is {@code "true"}, - * {@code false} otherwise. - * @see #JAVA_AWT_HEADLESS - * @since 2.1 - * @since Java 1.4 - */ - public static boolean isJavaAwtHeadless() { - return Boolean.TRUE.toString().equals(JAVA_AWT_HEADLESS); - } - - /** - *

- * Is the Java version at least the requested version. - *

- *

- * - * @param requiredVersion the required version, for example 1.31f - * @return {@code true} if the actual version is equal or greater than the - * required version - */ - public static boolean isJavaVersionAtLeast(final JavaVersion requiredVersion) { - return JAVA_SPECIFICATION_VERSION_AS_ENUM.atLeast(requiredVersion); - } - - /** - *

- * Is the Java version at most the requested version. - *

- *

- * Example input: - *

- * - * @param requiredVersion the required version, for example 1.31f - * @return {@code true} if the actual version is equal or less than the required - * version - * @since 3.9 - */ - public static boolean isJavaVersionAtMost(final JavaVersion requiredVersion) { - return JAVA_SPECIFICATION_VERSION_AS_ENUM.atMost(requiredVersion); - } - - /** - *

- * Decides if the Java version matches. - *

- *

- * This method is package private instead of private to support unit test - * invocation. - *

- * - * @param version the actual Java version - * @param versionPrefix the prefix for the expected Java version - * @return true if matches, or false if not or can't determine - */ - static boolean isJavaVersionMatch(final String version, final String versionPrefix) { - if (version == null) { - return false; - } - return version.startsWith(versionPrefix); - } - - /** - * Decides if the operating system matches. - *

- * This method is package private instead of private to support unit test - * invocation. - *

- * - * @param osName the actual OS name - * @param osVersion the actual OS version - * @param osNamePrefix the prefix for the expected OS name - * @param osVersionPrefix the prefix for the expected OS version - * @return true if matches, or false if not or can't determine - */ - static boolean isOSMatch(final String osName, final String osVersion, final String osNamePrefix, - final String osVersionPrefix) { - if (osName == null || osVersion == null) { - return false; - } - return isOSNameMatch(osName, osNamePrefix) && isOSVersionMatch(osVersion, osVersionPrefix); - } - - /** - * Decides if the operating system matches. - *

- * This method is package private instead of private to support unit test - * invocation. - *

- * - * @param osName the actual OS name - * @param osNamePrefix the prefix for the expected OS name - * @return true if matches, or false if not or can't determine - */ - static boolean isOSNameMatch(final String osName, final String osNamePrefix) { - if (osName == null) { - return false; - } - return osName.startsWith(osNamePrefix); - } - - /** - * Decides if the operating system version matches. - *

- * This method is package private instead of private to support unit test - * invocation. - *

- * - * @param osVersion the actual OS version - * @param osVersionPrefix the prefix for the expected OS version - * @return true if matches, or false if not or can't determine - */ - static boolean isOSVersionMatch(final String osVersion, final String osVersionPrefix) { - if (StringUtils.isEmpty(osVersion)) { - return false; - } - // Compare parts of the version string instead of using - // String.startsWith(String) because otherwise - // osVersionPrefix 10.1 would also match osVersion 10.10 - final String[] versionPrefixParts = osVersionPrefix.split("\\."); - final String[] versionParts = osVersion.split("\\."); - for (int i = 0; i < Math.min(versionPrefixParts.length, versionParts.length); i++) { - if (!versionPrefixParts[i].equals(versionParts[i])) { - return false; - } - } - return true; - } - - // ----------------------------------------------------------------------- - /** - *

- * SystemUtils instances should NOT be constructed in standard programming. - * Instead, the class should be used as {@code SystemUtils.FILE_SEPARATOR}. - *

- *

- * This constructor is public to permit tools that require a JavaBean instance - * to operate. - *

- */ - public SystemUtils() { - } - -} diff --git a/src/main/java/org/apache/commons/lang3/ThreadUtils.java b/src/main/java/org/apache/commons/lang3/ThreadUtils.java deleted file mode 100755 index 34b7836c..00000000 --- a/src/main/java/org/apache/commons/lang3/ThreadUtils.java +++ /dev/null @@ -1,526 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import java.time.Duration; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import org.apache.commons.lang3.time.DurationUtils; - -/** - *

- * Helpers for {@code java.lang.Thread} and {@code java.lang.ThreadGroup}. - *

- *

- * #ThreadSafe# - *

- * - * @see java.lang.Thread - * @see java.lang.ThreadGroup - * @since 3.5 - */ -public class ThreadUtils { - - /** - * A predicate implementation which always returns true. - */ - private static final class AlwaysTruePredicate implements ThreadPredicate, ThreadGroupPredicate { - - private AlwaysTruePredicate() { - } - - @Override - public boolean test(final Thread thread) { - return true; - } - - @Override - public boolean test(final ThreadGroup threadGroup) { - return true; - } - } - - /** - * A predicate implementation which matches a thread or threadgroup name. - */ - public static class NamePredicate implements ThreadPredicate, ThreadGroupPredicate { - - private final String name; - - /** - * Predicate constructor - * - * @param name thread or threadgroup name - * @throws IllegalArgumentException if the name is {@code null} - */ - public NamePredicate(final String name) { - Validate.notNull(name, "name"); - this.name = name; - } - - @Override - public boolean test(final Thread thread) { - return thread != null && thread.getName().equals(name); - } - - @Override - public boolean test(final ThreadGroup threadGroup) { - return threadGroup != null && threadGroup.getName().equals(name); - } - } - - /** - * A predicate for selecting threadgroups. - */ - // When breaking BC, replace this with Predicate - @FunctionalInterface - public interface ThreadGroupPredicate { - - /** - * Evaluates this predicate on the given threadgroup. - * - * @param threadGroup the threadgroup - * @return {@code true} if the threadGroup matches the predicate, otherwise - * {@code false} - */ - boolean test(ThreadGroup threadGroup); - } - - /** - * A predicate implementation which matches a thread id. - */ - public static class ThreadIdPredicate implements ThreadPredicate { - - private final long threadId; - - /** - * Predicate constructor - * - * @param threadId the threadId to match - * @throws IllegalArgumentException if the threadId is zero or negative - */ - public ThreadIdPredicate(final long threadId) { - if (threadId <= 0) { - throw new IllegalArgumentException("The thread id must be greater than zero"); - } - this.threadId = threadId; - } - - @Override - public boolean test(final Thread thread) { - return thread != null && thread.getId() == threadId; - } - } - - /** - * A predicate for selecting threads. - */ - // When breaking BC, replace this with Predicate - @FunctionalInterface - public interface ThreadPredicate { - - /** - * Evaluates this predicate on the given thread. - * - * @param thread the thread - * @return {@code true} if the thread matches the predicate, otherwise - * {@code false} - */ - boolean test(Thread thread); - } - - /** - * Predicate which always returns true. - */ - public static final AlwaysTruePredicate ALWAYS_TRUE_PREDICATE = new AlwaysTruePredicate(); - - /** - * Finds the active thread with the specified id. - * - * @param threadId The thread id - * @return The thread with the specified id or {@code null} if no such thread - * exists - * @throws IllegalArgumentException if the specified id is zero or negative - * @throws SecurityException if the current thread cannot access the - * system thread group - * - * @throws SecurityException if the current thread cannot modify thread - * groups from this thread's thread group up to - * the system thread group - */ - public static Thread findThreadById(final long threadId) { - final Collection result = findThreads(new ThreadIdPredicate(threadId)); - return result.isEmpty() ? null : result.iterator().next(); - } - - /** - * Finds the active thread with the specified id if it belongs to a thread group - * with the specified group name. - * - * @param threadId The thread id - * @param threadGroupName The thread group name - * @return The threads which belongs to a thread group with the specified group - * name and the thread's id match the specified id. {@code null} is - * returned if no such thread exists - * @throws IllegalArgumentException if the specified id is zero or negative or - * the group name is null - * @throws SecurityException if the current thread cannot access the - * system thread group - * - * @throws SecurityException if the current thread cannot modify thread - * groups from this thread's thread group up to - * the system thread group - */ - public static Thread findThreadById(final long threadId, final String threadGroupName) { - Validate.notNull(threadGroupName, "threadGroupName"); - final Thread thread = findThreadById(threadId); - if (thread != null && thread.getThreadGroup() != null - && thread.getThreadGroup().getName().equals(threadGroupName)) { - return thread; - } - return null; - } - - /** - * Finds the active thread with the specified id if it belongs to the specified - * thread group. - * - * @param threadId The thread id - * @param threadGroup The thread group - * @return The thread which belongs to a specified thread group and the thread's - * id match the specified id. {@code null} is returned if no such thread - * exists - * @throws IllegalArgumentException if the specified id is zero or negative or - * the group is null - * @throws SecurityException if the current thread cannot access the - * system thread group - * - * @throws SecurityException if the current thread cannot modify thread - * groups from this thread's thread group up to - * the system thread group - */ - public static Thread findThreadById(final long threadId, final ThreadGroup threadGroup) { - Validate.notNull(threadGroup, "threadGroup"); - final Thread thread = findThreadById(threadId); - if (thread != null && threadGroup.equals(thread.getThreadGroup())) { - return thread; - } - return null; - } - - /** - * Select all active threadgroups which match the given predicate and which is a - * subgroup of the given thread group (or one of its subgroups). - * - * @param group the thread group - * @param recurse if {@code true} then evaluate the predicate recursively on - * all threadgroups in all subgroups of the given group - * @param predicate the predicate - * @return An unmodifiable {@code Collection} of active threadgroups which match - * the given predicate and which is a subgroup of the given thread group - * @throws IllegalArgumentException if the given group or predicate is null - * @throws SecurityException if the current thread cannot modify thread - * groups from this thread's thread group up to - * the system thread group - */ - public static Collection findThreadGroups(final ThreadGroup group, final boolean recurse, - final ThreadGroupPredicate predicate) { - Validate.notNull(group, "group"); - Validate.notNull(predicate, "predicate"); - - int count = group.activeGroupCount(); - ThreadGroup[] threadGroups; - do { - threadGroups = new ThreadGroup[count + (count / 2) + 1]; // slightly grow the array size - count = group.enumerate(threadGroups, recurse); - // return value of enumerate() must be strictly less than the array size - // according to javadoc - } while (count >= threadGroups.length); - - final List result = new ArrayList<>(count); - for (int i = 0; i < count; ++i) { - if (predicate.test(threadGroups[i])) { - result.add(threadGroups[i]); - } - } - return Collections.unmodifiableCollection(result); - } - - /** - * Select all active threadgroups which match the given predicate. - * - * @param predicate the predicate - * @return An unmodifiable {@code Collection} of active threadgroups matching - * the given predicate - * @throws IllegalArgumentException if the predicate is null - * @throws SecurityException if the current thread cannot access the - * system thread group - * @throws SecurityException if the current thread cannot modify thread - * groups from this thread's thread group up to - * the system thread group - */ - public static Collection findThreadGroups(final ThreadGroupPredicate predicate) { - return findThreadGroups(getSystemThreadGroup(), true, predicate); - } - - /** - * Finds active thread groups with the specified group name. - * - * @param threadGroupName The thread group name - * @return the thread groups with the specified group name or an empty - * collection if no such thread group exists. The collection returned is - * always unmodifiable. - * @throws IllegalArgumentException if group name is null - * @throws SecurityException if the current thread cannot access the - * system thread group - * - * @throws SecurityException if the current thread cannot modify thread - * groups from this thread's thread group up to - * the system thread group - */ - public static Collection findThreadGroupsByName(final String threadGroupName) { - return findThreadGroups(new NamePredicate(threadGroupName)); - } - - /** - * Select all active threads which match the given predicate and which belongs - * to the given thread group (or one of its subgroups). - * - * @param group the thread group - * @param recurse if {@code true} then evaluate the predicate recursively on - * all threads in all subgroups of the given group - * @param predicate the predicate - * @return An unmodifiable {@code Collection} of active threads which match the - * given predicate and which belongs to the given thread group - * @throws IllegalArgumentException if the given group or predicate is null - * @throws SecurityException if the current thread cannot modify thread - * groups from this thread's thread group up to - * the system thread group - */ - public static Collection findThreads(final ThreadGroup group, final boolean recurse, - final ThreadPredicate predicate) { - Validate.notNull(group, "The group must not be null"); - Validate.notNull(predicate, "The predicate must not be null"); - - int count = group.activeCount(); - Thread[] threads; - do { - threads = new Thread[count + (count / 2) + 1]; // slightly grow the array size - count = group.enumerate(threads, recurse); - // return value of enumerate() must be strictly less than the array size - // according to javadoc - } while (count >= threads.length); - - final List result = new ArrayList<>(count); - for (int i = 0; i < count; ++i) { - if (predicate.test(threads[i])) { - result.add(threads[i]); - } - } - return Collections.unmodifiableCollection(result); - } - - /** - * Select all active threads which match the given predicate. - * - * @param predicate the predicate - * @return An unmodifiable {@code Collection} of active threads matching the - * given predicate - * - * @throws IllegalArgumentException if the predicate is null - * @throws SecurityException if the current thread cannot access the - * system thread group - * @throws SecurityException if the current thread cannot modify thread - * groups from this thread's thread group up to - * the system thread group - */ - public static Collection findThreads(final ThreadPredicate predicate) { - return findThreads(getSystemThreadGroup(), true, predicate); - } - - /** - * Finds active threads with the specified name. - * - * @param threadName The thread name - * @return The threads with the specified name or an empty collection if no such - * thread exists. The collection returned is always unmodifiable. - * @throws IllegalArgumentException if the specified name is null - * @throws SecurityException if the current thread cannot access the - * system thread group - * - * @throws SecurityException if the current thread cannot modify thread - * groups from this thread's thread group up to - * the system thread group - */ - public static Collection findThreadsByName(final String threadName) { - return findThreads(new NamePredicate(threadName)); - } - - /** - * Finds active threads with the specified name if they belong to a thread group - * with the specified group name. - * - * @param threadName The thread name - * @param threadGroupName The thread group name - * @return The threads which belongs to a thread group with the specified group - * name and the thread's name match the specified name, An empty - * collection is returned if no such thread exists. The collection - * returned is always unmodifiable. - * @throws IllegalArgumentException if the specified thread name or group name - * is null - * @throws SecurityException if the current thread cannot access the - * system thread group - * - * @throws SecurityException if the current thread cannot modify thread - * groups from this thread's thread group up to - * the system thread group - */ - public static Collection findThreadsByName(final String threadName, final String threadGroupName) { - Validate.notNull(threadName, "threadName"); - Validate.notNull(threadGroupName, "threadGroupName"); - - final Collection threadGroups = findThreadGroups(new NamePredicate(threadGroupName)); - - if (threadGroups.isEmpty()) { - return Collections.emptyList(); - } - - final Collection result = new ArrayList<>(); - final NamePredicate threadNamePredicate = new NamePredicate(threadName); - for (final ThreadGroup group : threadGroups) { - result.addAll(findThreads(group, false, threadNamePredicate)); - } - return Collections.unmodifiableCollection(result); - } - - /** - * Finds active threads with the specified name if they belong to a specified - * thread group. - * - * @param threadName The thread name - * @param threadGroup The thread group - * @return The threads which belongs to a thread group and the thread's name - * match the specified name, An empty collection is returned if no such - * thread exists. The collection returned is always unmodifiable. - * @throws IllegalArgumentException if the specified thread name or group is - * null - * @throws SecurityException if the current thread cannot access the - * system thread group - * - * @throws SecurityException if the current thread cannot modify thread - * groups from this thread's thread group up to - * the system thread group - */ - public static Collection findThreadsByName(final String threadName, final ThreadGroup threadGroup) { - return findThreads(threadGroup, false, new NamePredicate(threadName)); - } - - /** - * Gets all active thread groups excluding the system thread group (A thread - * group is active if it has been not destroyed). - * - * @return all thread groups excluding the system thread group. The collection - * returned is always unmodifiable. - * @throws SecurityException if the current thread cannot access the system - * thread group - * - * @throws SecurityException if the current thread cannot modify thread groups - * from this thread's thread group up to the system - * thread group - */ - public static Collection getAllThreadGroups() { - return findThreadGroups(ALWAYS_TRUE_PREDICATE); - } - - /** - * Gets all active threads (A thread is active if it has been started and has - * not yet died). - * - * @return all active threads. The collection returned is always unmodifiable. - * @throws SecurityException if the current thread cannot access the system - * thread group - * - * @throws SecurityException if the current thread cannot modify thread groups - * from this thread's thread group up to the system - * thread group - */ - public static Collection getAllThreads() { - return findThreads(ALWAYS_TRUE_PREDICATE); - } - - /** - * Gets the system thread group (sometimes also referred as "root thread - * group"). - * - * @return the system thread group - * @throws SecurityException if the current thread cannot modify thread groups - * from this thread's thread group up to the system - * thread group - */ - public static ThreadGroup getSystemThreadGroup() { - ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); - while (threadGroup.getParent() != null) { - threadGroup = threadGroup.getParent(); - } - return threadGroup; - } - - /** - * Waits for the given thread to die for the given duration. Implemented using - * {@link Thread#join(long, int)}. - * - * @param thread The thread to join. - * @param duration How long to wait. - * @throws InterruptedException if any thread has interrupted the current - * thread. - * @see Thread#join(long, int) - * @since 3.12.0 - */ - public static void join(final Thread thread, final Duration duration) throws InterruptedException { - DurationUtils.accept(thread::join, duration); - } - - /** - * Sleeps the current thread for the given duration. Implemented using - * {@link Thread#sleep(long, int)}. - * - * @param duration How long to sleep. - * @throws InterruptedException if any thread has interrupted the current - * thread. - * @see Thread#sleep(long, int) - * @since 3.12.0 - */ - public static void sleep(final Duration duration) throws InterruptedException { - DurationUtils.accept(Thread::sleep, duration); - } - - /** - *

- * ThreadUtils instances should NOT be constructed in standard programming. - * Instead, the class should be used as {@code ThreadUtils.getAllThreads()} - *

- *

- * This constructor is public to permit tools that require a JavaBean instance - * to operate. - *

- */ - public ThreadUtils() { - } -} diff --git a/src/main/java/org/apache/commons/lang3/Validate.java b/src/main/java/org/apache/commons/lang3/Validate.java deleted file mode 100755 index fd672b17..00000000 --- a/src/main/java/org/apache/commons/lang3/Validate.java +++ /dev/null @@ -1,1620 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.regex.Pattern; - -import net.lax1dude.eaglercraft.v1_8.HString; -import net.lax1dude.eaglercraft.v1_8.JDKBackports; - -/** - *

- * This class assists in validating arguments. The validation methods are based - * along the following principles: - *

    - *
  • An invalid {@code null} argument causes a - * {@link NullPointerException}.
  • - *
  • A non-{@code null} argument causes an - * {@link IllegalArgumentException}.
  • - *
  • An invalid index into an array/collection/map/string causes an - * {@link IndexOutOfBoundsException}.
  • - *
- * - *

- * All exceptions messages are format - * strings as defined by the Java platform. For example: - *

- * - *
- * Validate.isTrue(i > 0, "The value must be greater than zero: %d", i);
- * Validate.notNull(surname, "The surname must not be %s", null);
- * 
- * - *

- * #ThreadSafe# - *

- * - * @see java.lang.String#format(String, Object...) - * @since 2.0 - */ -public class Validate { - - private static final String DEFAULT_NOT_NAN_EX_MESSAGE = "The validated value is not a number"; - private static final String DEFAULT_FINITE_EX_MESSAGE = "The value is invalid: %f"; - private static final String DEFAULT_EXCLUSIVE_BETWEEN_EX_MESSAGE = "The value %s is not in the specified exclusive range of %s to %s"; - private static final String DEFAULT_INCLUSIVE_BETWEEN_EX_MESSAGE = "The value %s is not in the specified inclusive range of %s to %s"; - private static final String DEFAULT_MATCHES_PATTERN_EX = "The string %s does not match the pattern %s"; - private static final String DEFAULT_IS_NULL_EX_MESSAGE = "The validated object is null"; - private static final String DEFAULT_IS_TRUE_EX_MESSAGE = "The validated expression is false"; - private static final String DEFAULT_NO_NULL_ELEMENTS_ARRAY_EX_MESSAGE = "The validated array contains null element at index: %d"; - private static final String DEFAULT_NO_NULL_ELEMENTS_COLLECTION_EX_MESSAGE = "The validated collection contains null element at index: %d"; - private static final String DEFAULT_NOT_BLANK_EX_MESSAGE = "The validated character sequence is blank"; - private static final String DEFAULT_NOT_EMPTY_ARRAY_EX_MESSAGE = "The validated array is empty"; - private static final String DEFAULT_NOT_EMPTY_CHAR_SEQUENCE_EX_MESSAGE = "The validated character sequence is empty"; - private static final String DEFAULT_NOT_EMPTY_COLLECTION_EX_MESSAGE = "The validated collection is empty"; - private static final String DEFAULT_NOT_EMPTY_MAP_EX_MESSAGE = "The validated map is empty"; - private static final String DEFAULT_VALID_INDEX_ARRAY_EX_MESSAGE = "The validated array index is invalid: %d"; - private static final String DEFAULT_VALID_INDEX_CHAR_SEQUENCE_EX_MESSAGE = "The validated character sequence index is invalid: %d"; - private static final String DEFAULT_VALID_INDEX_COLLECTION_EX_MESSAGE = "The validated collection index is invalid: %d"; - private static final String DEFAULT_VALID_STATE_EX_MESSAGE = "The validated state is false"; - private static final String DEFAULT_IS_ASSIGNABLE_EX_MESSAGE = "Cannot assign a %s to a %s"; - private static final String DEFAULT_IS_INSTANCE_OF_EX_MESSAGE = "Expected type: %s, actual: %s"; - - /** - * Constructor. This class should not normally be instantiated. - */ - public Validate() { - } - - // isTrue - // --------------------------------------------------------------------------------- - - /** - *

- * Validate that the argument condition is {@code true}; otherwise throwing an - * exception with the specified message. This method is useful when validating - * according to an arbitrary boolean expression, such as validating a primitive - * number or using your own custom validation expression. - *

- * - *
-	 * Validate.isTrue(i > 0.0, "The value must be greater than zero: %d", i);
-	 * 
- * - *

- * For performance reasons, the long value is passed as a separate parameter and - * appended to the exception message only in the case of an error. - *

- * - * @param expression the boolean expression to check - * @param message the {@link String#format(String, Object...)} exception - * message if invalid, not null - * @param value the value to append to the message when invalid - * @throws IllegalArgumentException if expression is {@code false} - * @see #isTrue(boolean) - * @see #isTrue(boolean, String, double) - * @see #isTrue(boolean, String, Object...) - */ - public static void isTrue(final boolean expression, final String message, final long value) { - if (!expression) { - throw new IllegalArgumentException(HString.format(message, Long.valueOf(value))); - } - } - - /** - *

- * Validate that the argument condition is {@code true}; otherwise throwing an - * exception with the specified message. This method is useful when validating - * according to an arbitrary boolean expression, such as validating a primitive - * number or using your own custom validation expression. - *

- * - *
-	 * Validate.isTrue(d > 0.0, "The value must be greater than zero: %s", d);
-	 * 
- * - *

- * For performance reasons, the double value is passed as a separate parameter - * and appended to the exception message only in the case of an error. - *

- * - * @param expression the boolean expression to check - * @param message the {@link String#format(String, Object...)} exception - * message if invalid, not null - * @param value the value to append to the message when invalid - * @throws IllegalArgumentException if expression is {@code false} - * @see #isTrue(boolean) - * @see #isTrue(boolean, String, long) - * @see #isTrue(boolean, String, Object...) - */ - public static void isTrue(final boolean expression, final String message, final double value) { - if (!expression) { - throw new IllegalArgumentException(HString.format(message, Double.valueOf(value))); - } - } - - /** - *

- * Validate that the argument condition is {@code true}; otherwise throwing an - * exception with the specified message. This method is useful when validating - * according to an arbitrary boolean expression, such as validating a primitive - * number or using your own custom validation expression. - *

- * - *
-	 * Validate.isTrue(i >= min && i <= max, "The value must be between %d and %d", min, max);
-	 * Validate.isTrue(myObject.isOk(), "The object is not okay");
-	 * 
- * - * @param expression the boolean expression to check - * @param message the {@link String#format(String, Object...)} exception - * message if invalid, not null - * @param values the optional values for the formatted exception message, - * null array not recommended - * @throws IllegalArgumentException if expression is {@code false} - * @see #isTrue(boolean) - * @see #isTrue(boolean, String, long) - * @see #isTrue(boolean, String, double) - */ - public static void isTrue(final boolean expression, final String message, final Object... values) { - if (!expression) { - throw new IllegalArgumentException(HString.format(message, values)); - } - } - - /** - *

- * Validate that the argument condition is {@code true}; otherwise throwing an - * exception. This method is useful when validating according to an arbitrary - * boolean expression, such as validating a primitive number or using your own - * custom validation expression. - *

- * - *
-	 * Validate.isTrue(i > 0);
-	 * Validate.isTrue(myObject.isOk());
-	 * 
- * - *

- * The message of the exception is "The validated expression is - * false". - *

- * - * @param expression the boolean expression to check - * @throws IllegalArgumentException if expression is {@code false} - * @see #isTrue(boolean, String, long) - * @see #isTrue(boolean, String, double) - * @see #isTrue(boolean, String, Object...) - */ - public static void isTrue(final boolean expression) { - if (!expression) { - throw new IllegalArgumentException(DEFAULT_IS_TRUE_EX_MESSAGE); - } - } - - // notNull - // --------------------------------------------------------------------------------- - - /** - *

- * Validate that the specified argument is not {@code null}; otherwise throwing - * an exception. - * - *

-	 * Validate.notNull(myObject, "The object must not be null");
-	 * 
- * - *

- * The message of the exception is "The validated object is null". - *

- * - * @param the object type - * @param object the object to check - * @return the validated object (never {@code null} for method chaining) - * @throws NullPointerException if the object is {@code null} - * @see #notNull(Object, String, Object...) - */ - public static T notNull(final T object) { - return notNull(object, DEFAULT_IS_NULL_EX_MESSAGE); - } - - /** - *

- * Validate that the specified argument is not {@code null}; otherwise throwing - * an exception with the specified message. - * - *

-	 * Validate.notNull(myObject, "The object must not be null");
-	 * 
- * - * @param the object type - * @param object the object to check - * @param message the {@link String#format(String, Object...)} exception message - * if invalid, not null - * @param values the optional values for the formatted exception message - * @return the validated object (never {@code null} for method chaining) - * @throws NullPointerException if the object is {@code null} - * @see #notNull(Object) - */ - public static T notNull(final T object, final String message, final Object... values) { - return JDKBackports.javaUtilObject_requireNonNull(object, () -> HString.format(message, values)); - } - - // notEmpty array - // --------------------------------------------------------------------------------- - - /** - *

- * Validate that the specified argument array is neither {@code null} nor a - * length of zero (no elements); otherwise throwing an exception with the - * specified message. - * - *

-	 * Validate.notEmpty(myArray, "The array must not be empty");
-	 * 
- * - * @param the array type - * @param array the array to check, validated not null by this method - * @param message the {@link String#format(String, Object...)} exception message - * if invalid, not null - * @param values the optional values for the formatted exception message, null - * array not recommended - * @return the validated array (never {@code null} method for chaining) - * @throws NullPointerException if the array is {@code null} - * @throws IllegalArgumentException if the array is empty - * @see #notEmpty(Object[]) - */ - public static T[] notEmpty(final T[] array, final String message, final Object... values) { - JDKBackports.javaUtilObject_requireNonNull(array, () -> HString.format(message, values)); - if (array.length == 0) { - throw new IllegalArgumentException(HString.format(message, values)); - } - return array; - } - - /** - *

- * Validate that the specified argument array is neither {@code null} nor a - * length of zero (no elements); otherwise throwing an exception. - * - *

-	 * Validate.notEmpty(myArray);
-	 * 
- * - *

- * The message in the exception is "The validated array is empty". - * - * @param the array type - * @param array the array to check, validated not null by this method - * @return the validated array (never {@code null} method for chaining) - * @throws NullPointerException if the array is {@code null} - * @throws IllegalArgumentException if the array is empty - * @see #notEmpty(Object[], String, Object...) - */ - public static T[] notEmpty(final T[] array) { - return notEmpty(array, DEFAULT_NOT_EMPTY_ARRAY_EX_MESSAGE); - } - - // notEmpty collection - // --------------------------------------------------------------------------------- - - /** - *

- * Validate that the specified argument collection is neither {@code null} nor a - * size of zero (no elements); otherwise throwing an exception with the - * specified message. - * - *

-	 * Validate.notEmpty(myCollection, "The collection must not be empty");
-	 * 
- * - * @param the collection type - * @param collection the collection to check, validated not null by this method - * @param message the {@link String#format(String, Object...)} exception - * message if invalid, not null - * @param values the optional values for the formatted exception message, - * null array not recommended - * @return the validated collection (never {@code null} method for chaining) - * @throws NullPointerException if the collection is {@code null} - * @throws IllegalArgumentException if the collection is empty - * @see #notEmpty(Object[]) - */ - public static > T notEmpty(final T collection, final String message, - final Object... values) { - JDKBackports.javaUtilObject_requireNonNull(collection, () -> HString.format(message, values)); - if (collection.isEmpty()) { - throw new IllegalArgumentException(HString.format(message, values)); - } - return collection; - } - - /** - *

- * Validate that the specified argument collection is neither {@code null} nor a - * size of zero (no elements); otherwise throwing an exception. - * - *

-	 * Validate.notEmpty(myCollection);
-	 * 
- * - *

- * The message in the exception is "The validated collection is - * empty". - *

- * - * @param the collection type - * @param collection the collection to check, validated not null by this method - * @return the validated collection (never {@code null} method for chaining) - * @throws NullPointerException if the collection is {@code null} - * @throws IllegalArgumentException if the collection is empty - * @see #notEmpty(Collection, String, Object...) - */ - public static > T notEmpty(final T collection) { - return notEmpty(collection, DEFAULT_NOT_EMPTY_COLLECTION_EX_MESSAGE); - } - - // notEmpty map - // --------------------------------------------------------------------------------- - - /** - *

- * Validate that the specified argument map is neither {@code null} nor a size - * of zero (no elements); otherwise throwing an exception with the specified - * message. - * - *

-	 * Validate.notEmpty(myMap, "The map must not be empty");
-	 * 
- * - * @param the map type - * @param map the map to check, validated not null by this method - * @param message the {@link String#format(String, Object...)} exception message - * if invalid, not null - * @param values the optional values for the formatted exception message, null - * array not recommended - * @return the validated map (never {@code null} method for chaining) - * @throws NullPointerException if the map is {@code null} - * @throws IllegalArgumentException if the map is empty - * @see #notEmpty(Object[]) - */ - public static > T notEmpty(final T map, final String message, final Object... values) { - JDKBackports.javaUtilObject_requireNonNull(map, () -> HString.format(message, values)); - if (map.isEmpty()) { - throw new IllegalArgumentException(HString.format(message, values)); - } - return map; - } - - /** - *

- * Validate that the specified argument map is neither {@code null} nor a size - * of zero (no elements); otherwise throwing an exception. - * - *

-	 * Validate.notEmpty(myMap);
-	 * 
- * - *

- * The message in the exception is "The validated map is empty". - *

- * - * @param the map type - * @param map the map to check, validated not null by this method - * @return the validated map (never {@code null} method for chaining) - * @throws NullPointerException if the map is {@code null} - * @throws IllegalArgumentException if the map is empty - * @see #notEmpty(Map, String, Object...) - */ - public static > T notEmpty(final T map) { - return notEmpty(map, DEFAULT_NOT_EMPTY_MAP_EX_MESSAGE); - } - - // notEmpty string - // --------------------------------------------------------------------------------- - - /** - *

- * Validate that the specified argument character sequence is neither - * {@code null} nor a length of zero (no characters); otherwise throwing an - * exception with the specified message. - * - *

-	 * Validate.notEmpty(myString, "The string must not be empty");
-	 * 
- * - * @param the character sequence type - * @param chars the character sequence to check, validated not null by this - * method - * @param message the {@link String#format(String, Object...)} exception message - * if invalid, not null - * @param values the optional values for the formatted exception message, null - * array not recommended - * @return the validated character sequence (never {@code null} method for - * chaining) - * @throws NullPointerException if the character sequence is {@code null} - * @throws IllegalArgumentException if the character sequence is empty - * @see #notEmpty(CharSequence) - */ - public static T notEmpty(final T chars, final String message, final Object... values) { - JDKBackports.javaUtilObject_requireNonNull(chars, () -> HString.format(message, values)); - if (chars.length() == 0) { - throw new IllegalArgumentException(HString.format(message, values)); - } - return chars; - } - - /** - *

- * Validate that the specified argument character sequence is neither - * {@code null} nor a length of zero (no characters); otherwise throwing an - * exception with the specified message. - * - *

-	 * Validate.notEmpty(myString);
-	 * 
- * - *

- * The message in the exception is "The validated character sequence is - * empty". - *

- * - * @param the character sequence type - * @param chars the character sequence to check, validated not null by this - * method - * @return the validated character sequence (never {@code null} method for - * chaining) - * @throws NullPointerException if the character sequence is {@code null} - * @throws IllegalArgumentException if the character sequence is empty - * @see #notEmpty(CharSequence, String, Object...) - */ - public static T notEmpty(final T chars) { - return notEmpty(chars, DEFAULT_NOT_EMPTY_CHAR_SEQUENCE_EX_MESSAGE); - } - - // notBlank string - // --------------------------------------------------------------------------------- - - /** - *

- * Validate that the specified argument character sequence is neither - * {@code null}, a length of zero (no characters), empty nor whitespace; - * otherwise throwing an exception with the specified message. - * - *

-	 * Validate.notBlank(myString, "The string must not be blank");
-	 * 
- * - * @param the character sequence type - * @param chars the character sequence to check, validated not null by this - * method - * @param message the {@link String#format(String, Object...)} exception message - * if invalid, not null - * @param values the optional values for the formatted exception message, null - * array not recommended - * @return the validated character sequence (never {@code null} method for - * chaining) - * @throws NullPointerException if the character sequence is {@code null} - * @throws IllegalArgumentException if the character sequence is blank - * @see #notBlank(CharSequence) - * - * @since 3.0 - */ - public static T notBlank(final T chars, final String message, final Object... values) { - JDKBackports.javaUtilObject_requireNonNull(chars, () -> HString.format(message, values)); - if (StringUtils.isBlank(chars)) { - throw new IllegalArgumentException(HString.format(message, values)); - } - return chars; - } - - /** - *

- * Validate that the specified argument character sequence is neither - * {@code null}, a length of zero (no characters), empty nor whitespace; - * otherwise throwing an exception. - * - *

-	 * Validate.notBlank(myString);
-	 * 
- * - *

- * The message in the exception is "The validated character sequence is - * blank". - *

- * - * @param the character sequence type - * @param chars the character sequence to check, validated not null by this - * method - * @return the validated character sequence (never {@code null} method for - * chaining) - * @throws NullPointerException if the character sequence is {@code null} - * @throws IllegalArgumentException if the character sequence is blank - * @see #notBlank(CharSequence, String, Object...) - * - * @since 3.0 - */ - public static T notBlank(final T chars) { - return notBlank(chars, DEFAULT_NOT_BLANK_EX_MESSAGE); - } - - // noNullElements array - // --------------------------------------------------------------------------------- - - /** - *

- * Validate that the specified argument array is neither {@code null} nor - * contains any elements that are {@code null}; otherwise throwing an exception - * with the specified message. - * - *

-	 * Validate.noNullElements(myArray, "The array contain null at position %d");
-	 * 
- * - *

- * If the array is {@code null}, then the message in the exception is "The - * validated object is null". - *

- * - *

- * If the array has a {@code null} element, then the iteration index of the - * invalid element is appended to the {@code values} argument. - *

- * - * @param the array type - * @param array the array to check, validated not null by this method - * @param message the {@link String#format(String, Object...)} exception message - * if invalid, not null - * @param values the optional values for the formatted exception message, null - * array not recommended - * @return the validated array (never {@code null} method for chaining) - * @throws NullPointerException if the array is {@code null} - * @throws IllegalArgumentException if an element is {@code null} - * @see #noNullElements(Object[]) - */ - public static T[] noNullElements(final T[] array, final String message, final Object... values) { - notNull(array); - for (int i = 0; i < array.length; i++) { - if (array[i] == null) { - final Object[] values2 = new Object[values.length + 1]; - System.arraycopy(values, 0, values2, 0, values.length); - values2[values.length - 1] = i; - throw new IllegalArgumentException(HString.format(message, values2)); - } - } - return array; - } - - /** - *

- * Validate that the specified argument array is neither {@code null} nor - * contains any elements that are {@code null}; otherwise throwing an exception. - *

- * - *
-	 * Validate.noNullElements(myArray);
-	 * 
- * - *

- * If the array is {@code null}, then the message in the exception is "The - * validated object is null". - *

- * - *

- * If the array has a {@code null} element, then the message in the exception is - * "The validated array contains null element at index: " followed by - * the index. - *

- * - * @param the array type - * @param array the array to check, validated not null by this method - * @return the validated array (never {@code null} method for chaining) - * @throws NullPointerException if the array is {@code null} - * @throws IllegalArgumentException if an element is {@code null} - * @see #noNullElements(Object[], String, Object...) - */ - public static T[] noNullElements(final T[] array) { - return noNullElements(array, DEFAULT_NO_NULL_ELEMENTS_ARRAY_EX_MESSAGE); - } - - // noNullElements iterable - // --------------------------------------------------------------------------------- - - /** - *

- * Validate that the specified argument iterable is neither {@code null} nor - * contains any elements that are {@code null}; otherwise throwing an exception - * with the specified message. - * - *

-	 * Validate.noNullElements(myCollection, "The collection contains null at position %d");
-	 * 
- * - *

- * If the iterable is {@code null}, then the message in the exception is - * "The validated object is null". - *

- * - *

- * If the iterable has a {@code null} element, then the iteration index of the - * invalid element is appended to the {@code values} argument. - *

- * - * @param the iterable type - * @param iterable the iterable to check, validated not null by this method - * @param message the {@link String#format(String, Object...)} exception - * message if invalid, not null - * @param values the optional values for the formatted exception message, null - * array not recommended - * @return the validated iterable (never {@code null} method for chaining) - * @throws NullPointerException if the array is {@code null} - * @throws IllegalArgumentException if an element is {@code null} - * @see #noNullElements(Iterable) - */ - public static > T noNullElements(final T iterable, final String message, - final Object... values) { - notNull(iterable); - int i = 0; - for (final Iterator it = iterable.iterator(); it.hasNext(); i++) { - if (it.next() == null) { - final Object[] values2 = new Object[values.length + 1]; - System.arraycopy(values, 0, values2, 0, values.length); - values2[values.length - 1] = i; - throw new IllegalArgumentException(HString.format(message, values2)); - } - } - return iterable; - } - - /** - *

- * Validate that the specified argument iterable is neither {@code null} nor - * contains any elements that are {@code null}; otherwise throwing an exception. - * - *

-	 * Validate.noNullElements(myCollection);
-	 * 
- * - *

- * If the iterable is {@code null}, then the message in the exception is - * "The validated object is null". - *

- * - *

- * If the array has a {@code null} element, then the message in the exception is - * "The validated iterable contains null element at index: " followed - * by the index. - *

- * - * @param the iterable type - * @param iterable the iterable to check, validated not null by this method - * @return the validated iterable (never {@code null} method for chaining) - * @throws NullPointerException if the array is {@code null} - * @throws IllegalArgumentException if an element is {@code null} - * @see #noNullElements(Iterable, String, Object...) - */ - public static > T noNullElements(final T iterable) { - return noNullElements(iterable, DEFAULT_NO_NULL_ELEMENTS_COLLECTION_EX_MESSAGE); - } - - // validIndex array - // --------------------------------------------------------------------------------- - - /** - *

- * Validates that the index is within the bounds of the argument array; - * otherwise throwing an exception with the specified message. - *

- * - *
-	 * Validate.validIndex(myArray, 2, "The array index is invalid: ");
-	 * 
- * - *

- * If the array is {@code null}, then the message of the exception is "The - * validated object is null". - *

- * - * @param the array type - * @param array the array to check, validated not null by this method - * @param index the index to check - * @param message the {@link String#format(String, Object...)} exception message - * if invalid, not null - * @param values the optional values for the formatted exception message, null - * array not recommended - * @return the validated array (never {@code null} for method chaining) - * @throws NullPointerException if the array is {@code null} - * @throws IndexOutOfBoundsException if the index is invalid - * @see #validIndex(Object[], int) - * - * @since 3.0 - */ - public static T[] validIndex(final T[] array, final int index, final String message, final Object... values) { - notNull(array); - if (index < 0 || index >= array.length) { - throw new IndexOutOfBoundsException(HString.format(message, values)); - } - return array; - } - - /** - *

- * Validates that the index is within the bounds of the argument array; - * otherwise throwing an exception. - *

- * - *
-	 * Validate.validIndex(myArray, 2);
-	 * 
- * - *

- * If the array is {@code null}, then the message of the exception is "The - * validated object is null". - *

- * - *

- * If the index is invalid, then the message of the exception is "The - * validated array index is invalid: " followed by the index. - *

- * - * @param the array type - * @param array the array to check, validated not null by this method - * @param index the index to check - * @return the validated array (never {@code null} for method chaining) - * @throws NullPointerException if the array is {@code null} - * @throws IndexOutOfBoundsException if the index is invalid - * @see #validIndex(Object[], int, String, Object...) - * - * @since 3.0 - */ - public static T[] validIndex(final T[] array, final int index) { - return validIndex(array, index, DEFAULT_VALID_INDEX_ARRAY_EX_MESSAGE, Integer.valueOf(index)); - } - - // validIndex collection - // --------------------------------------------------------------------------------- - - /** - *

- * Validates that the index is within the bounds of the argument collection; - * otherwise throwing an exception with the specified message. - *

- * - *
-	 * Validate.validIndex(myCollection, 2, "The collection index is invalid: ");
-	 * 
- * - *

- * If the collection is {@code null}, then the message of the exception is - * "The validated object is null". - *

- * - * @param the collection type - * @param collection the collection to check, validated not null by this method - * @param index the index to check - * @param message the {@link String#format(String, Object...)} exception - * message if invalid, not null - * @param values the optional values for the formatted exception message, - * null array not recommended - * @return the validated collection (never {@code null} for chaining) - * @throws NullPointerException if the collection is {@code null} - * @throws IndexOutOfBoundsException if the index is invalid - * @see #validIndex(Collection, int) - * - * @since 3.0 - */ - public static > T validIndex(final T collection, final int index, final String message, - final Object... values) { - notNull(collection); - if (index < 0 || index >= collection.size()) { - throw new IndexOutOfBoundsException(HString.format(message, values)); - } - return collection; - } - - /** - *

- * Validates that the index is within the bounds of the argument collection; - * otherwise throwing an exception. - *

- * - *
-	 * Validate.validIndex(myCollection, 2);
-	 * 
- * - *

- * If the index is invalid, then the message of the exception is "The - * validated collection index is invalid: " followed by the index. - *

- * - * @param the collection type - * @param collection the collection to check, validated not null by this method - * @param index the index to check - * @return the validated collection (never {@code null} for method chaining) - * @throws NullPointerException if the collection is {@code null} - * @throws IndexOutOfBoundsException if the index is invalid - * @see #validIndex(Collection, int, String, Object...) - * - * @since 3.0 - */ - public static > T validIndex(final T collection, final int index) { - return validIndex(collection, index, DEFAULT_VALID_INDEX_COLLECTION_EX_MESSAGE, Integer.valueOf(index)); - } - - // validIndex string - // --------------------------------------------------------------------------------- - - /** - *

- * Validates that the index is within the bounds of the argument character - * sequence; otherwise throwing an exception with the specified message. - *

- * - *
-	 * Validate.validIndex(myStr, 2, "The string index is invalid: ");
-	 * 
- * - *

- * If the character sequence is {@code null}, then the message of the exception - * is "The validated object is null". - *

- * - * @param the character sequence type - * @param chars the character sequence to check, validated not null by this - * method - * @param index the index to check - * @param message the {@link String#format(String, Object...)} exception message - * if invalid, not null - * @param values the optional values for the formatted exception message, null - * array not recommended - * @return the validated character sequence (never {@code null} for method - * chaining) - * @throws NullPointerException if the character sequence is {@code null} - * @throws IndexOutOfBoundsException if the index is invalid - * @see #validIndex(CharSequence, int) - * - * @since 3.0 - */ - public static T validIndex(final T chars, final int index, final String message, - final Object... values) { - notNull(chars); - if (index < 0 || index >= chars.length()) { - throw new IndexOutOfBoundsException(HString.format(message, values)); - } - return chars; - } - - /** - *

- * Validates that the index is within the bounds of the argument character - * sequence; otherwise throwing an exception. - *

- * - *
-	 * Validate.validIndex(myStr, 2);
-	 * 
- * - *

- * If the character sequence is {@code null}, then the message of the exception - * is "The validated object is null". - *

- * - *

- * If the index is invalid, then the message of the exception is "The - * validated character sequence index is invalid: " followed by the index. - *

- * - * @param the character sequence type - * @param chars the character sequence to check, validated not null by this - * method - * @param index the index to check - * @return the validated character sequence (never {@code null} for method - * chaining) - * @throws NullPointerException if the character sequence is {@code null} - * @throws IndexOutOfBoundsException if the index is invalid - * @see #validIndex(CharSequence, int, String, Object...) - * - * @since 3.0 - */ - public static T validIndex(final T chars, final int index) { - return validIndex(chars, index, DEFAULT_VALID_INDEX_CHAR_SEQUENCE_EX_MESSAGE, Integer.valueOf(index)); - } - - // validState - // --------------------------------------------------------------------------------- - - /** - *

- * Validate that the stateful condition is {@code true}; otherwise throwing an - * exception. This method is useful when validating according to an arbitrary - * boolean expression, such as validating a primitive number or using your own - * custom validation expression. - *

- * - *
-	 * Validate.validState(field > 0);
-	 * Validate.validState(this.isOk());
-	 * 
- * - *

- * The message of the exception is "The validated state is false". - *

- * - * @param expression the boolean expression to check - * @throws IllegalStateException if expression is {@code false} - * @see #validState(boolean, String, Object...) - * - * @since 3.0 - */ - public static void validState(final boolean expression) { - if (!expression) { - throw new IllegalStateException(DEFAULT_VALID_STATE_EX_MESSAGE); - } - } - - /** - *

- * Validate that the stateful condition is {@code true}; otherwise throwing an - * exception with the specified message. This method is useful when validating - * according to an arbitrary boolean expression, such as validating a primitive - * number or using your own custom validation expression. - *

- * - *
-	 * Validate.validState(this.isOk(), "The state is not OK: %s", myObject);
-	 * 
- * - * @param expression the boolean expression to check - * @param message the {@link String#format(String, Object...)} exception - * message if invalid, not null - * @param values the optional values for the formatted exception message, - * null array not recommended - * @throws IllegalStateException if expression is {@code false} - * @see #validState(boolean) - * - * @since 3.0 - */ - public static void validState(final boolean expression, final String message, final Object... values) { - if (!expression) { - throw new IllegalStateException(HString.format(message, values)); - } - } - - // matchesPattern - // --------------------------------------------------------------------------------- - - /** - *

- * Validate that the specified argument character sequence matches the specified - * regular expression pattern; otherwise throwing an exception. - *

- * - *
-	 * Validate.matchesPattern("hi", "[a-z]*");
-	 * 
- * - *

- * The syntax of the pattern is the one used in the {@link Pattern} class. - *

- * - * @param input the character sequence to validate, not null - * @param pattern the regular expression pattern, not null - * @throws IllegalArgumentException if the character sequence does not match the - * pattern - * @see #matchesPattern(CharSequence, String, String, Object...) - * - * @since 3.0 - */ - public static void matchesPattern(final CharSequence input, final String pattern) { - // TODO when breaking BC, consider returning input - if (!Pattern.matches(pattern, input)) { - throw new IllegalArgumentException(HString.format(DEFAULT_MATCHES_PATTERN_EX, input, pattern)); - } - } - - /** - *

- * Validate that the specified argument character sequence matches the specified - * regular expression pattern; otherwise throwing an exception with the - * specified message. - *

- * - *
-	 * Validate.matchesPattern("hi", "[a-z]*", "%s does not match %s", "hi" "[a-z]*");
-	 * 
- * - *

- * The syntax of the pattern is the one used in the {@link Pattern} class. - *

- * - * @param input the character sequence to validate, not null - * @param pattern the regular expression pattern, not null - * @param message the {@link String#format(String, Object...)} exception message - * if invalid, not null - * @param values the optional values for the formatted exception message, null - * array not recommended - * @throws IllegalArgumentException if the character sequence does not match the - * pattern - * @see #matchesPattern(CharSequence, String) - * - * @since 3.0 - */ - public static void matchesPattern(final CharSequence input, final String pattern, final String message, - final Object... values) { - // TODO when breaking BC, consider returning input - if (!Pattern.matches(pattern, input)) { - throw new IllegalArgumentException(HString.format(message, values)); - } - } - - // notNaN - // --------------------------------------------------------------------------------- - - /** - *

- * Validates that the specified argument is not {@code NaN}; otherwise throwing - * an exception. - *

- * - *
-	 * Validate.notNaN(myDouble);
-	 * 
- * - *

- * The message of the exception is "The validated value is not a - * number". - *

- * - * @param value the value to validate - * @throws IllegalArgumentException if the value is not a number - * @see #notNaN(double, java.lang.String, java.lang.Object...) - * - * @since 3.5 - */ - public static void notNaN(final double value) { - notNaN(value, DEFAULT_NOT_NAN_EX_MESSAGE); - } - - /** - *

- * Validates that the specified argument is not {@code NaN}; otherwise throwing - * an exception with the specified message. - *

- * - *
-	 * Validate.notNaN(myDouble, "The value must be a number");
-	 * 
- * - * @param value the value to validate - * @param message the {@link String#format(String, Object...)} exception message - * if invalid, not null - * @param values the optional values for the formatted exception message - * @throws IllegalArgumentException if the value is not a number - * @see #notNaN(double) - * - * @since 3.5 - */ - public static void notNaN(final double value, final String message, final Object... values) { - if (Double.isNaN(value)) { - throw new IllegalArgumentException(HString.format(message, values)); - } - } - - // finite - // --------------------------------------------------------------------------------- - - /** - *

- * Validates that the specified argument is not infinite or {@code NaN}; - * otherwise throwing an exception. - *

- * - *
-	 * Validate.finite(myDouble);
-	 * 
- * - *

- * The message of the exception is "The value is invalid: %f". - *

- * - * @param value the value to validate - * @throws IllegalArgumentException if the value is infinite or {@code NaN} - * @see #finite(double, java.lang.String, java.lang.Object...) - * - * @since 3.5 - */ - public static void finite(final double value) { - finite(value, DEFAULT_FINITE_EX_MESSAGE, value); - } - - /** - *

- * Validates that the specified argument is not infinite or {@code NaN}; - * otherwise throwing an exception with the specified message. - *

- * - *
-	 * Validate.finite(myDouble, "The argument must contain a numeric value");
-	 * 
- * - * @param value the value to validate - * @param message the {@link String#format(String, Object...)} exception message - * if invalid, not null - * @param values the optional values for the formatted exception message - * @throws IllegalArgumentException if the value is infinite or {@code NaN} - * @see #finite(double) - * - * @since 3.5 - */ - public static void finite(final double value, final String message, final Object... values) { - if (Double.isNaN(value) || Double.isInfinite(value)) { - throw new IllegalArgumentException(HString.format(message, values)); - } - } - - // inclusiveBetween - // --------------------------------------------------------------------------------- - - /** - *

- * Validate that the specified argument object fall between the two inclusive - * values specified; otherwise, throws an exception. - *

- * - *
-	 * Validate.inclusiveBetween(0, 2, 1);
-	 * 
- * - * @param the type of the argument object - * @param start the inclusive start value, not null - * @param end the inclusive end value, not null - * @param value the object to validate, not null - * @throws IllegalArgumentException if the value falls outside the boundaries - * @see #inclusiveBetween(Object, Object, Comparable, String, Object...) - * - * @since 3.0 - */ - public static void inclusiveBetween(final T start, final T end, final Comparable value) { - // TODO when breaking BC, consider returning value - if (value.compareTo(start) < 0 || value.compareTo(end) > 0) { - throw new IllegalArgumentException(HString.format(DEFAULT_INCLUSIVE_BETWEEN_EX_MESSAGE, value, start, end)); - } - } - - /** - *

- * Validate that the specified argument object fall between the two inclusive - * values specified; otherwise, throws an exception with the specified message. - *

- * - *
-	 * Validate.inclusiveBetween(0, 2, 1, "Not in boundaries");
-	 * 
- * - * @param the type of the argument object - * @param start the inclusive start value, not null - * @param end the inclusive end value, not null - * @param value the object to validate, not null - * @param message the {@link String#format(String, Object...)} exception message - * if invalid, not null - * @param values the optional values for the formatted exception message, null - * array not recommended - * @throws IllegalArgumentException if the value falls outside the boundaries - * @see #inclusiveBetween(Object, Object, Comparable) - * - * @since 3.0 - */ - public static void inclusiveBetween(final T start, final T end, final Comparable value, final String message, - final Object... values) { - // TODO when breaking BC, consider returning value - if (value.compareTo(start) < 0 || value.compareTo(end) > 0) { - throw new IllegalArgumentException(HString.format(message, values)); - } - } - - /** - * Validate that the specified primitive value falls between the two inclusive - * values specified; otherwise, throws an exception. - * - *
-	 * Validate.inclusiveBetween(0, 2, 1);
-	 * 
- * - * @param start the inclusive start value - * @param end the inclusive end value - * @param value the value to validate - * @throws IllegalArgumentException if the value falls outside the boundaries - * (inclusive) - * - * @since 3.3 - */ - @SuppressWarnings("boxing") - public static void inclusiveBetween(final long start, final long end, final long value) { - // TODO when breaking BC, consider returning value - if (value < start || value > end) { - throw new IllegalArgumentException(HString.format(DEFAULT_INCLUSIVE_BETWEEN_EX_MESSAGE, value, start, end)); - } - } - - /** - * Validate that the specified primitive value falls between the two inclusive - * values specified; otherwise, throws an exception with the specified message. - * - *
-	 * Validate.inclusiveBetween(0, 2, 1, "Not in range");
-	 * 
- * - * @param start the inclusive start value - * @param end the inclusive end value - * @param value the value to validate - * @param message the exception message if invalid, not null - * - * @throws IllegalArgumentException if the value falls outside the boundaries - * - * @since 3.3 - */ - public static void inclusiveBetween(final long start, final long end, final long value, final String message) { - // TODO when breaking BC, consider returning value - if (value < start || value > end) { - throw new IllegalArgumentException(message); - } - } - - /** - * Validate that the specified primitive value falls between the two inclusive - * values specified; otherwise, throws an exception. - * - *
-	 * Validate.inclusiveBetween(0.1, 2.1, 1.1);
-	 * 
- * - * @param start the inclusive start value - * @param end the inclusive end value - * @param value the value to validate - * @throws IllegalArgumentException if the value falls outside the boundaries - * (inclusive) - * - * @since 3.3 - */ - @SuppressWarnings("boxing") - public static void inclusiveBetween(final double start, final double end, final double value) { - // TODO when breaking BC, consider returning value - if (value < start || value > end) { - throw new IllegalArgumentException(HString.format(DEFAULT_INCLUSIVE_BETWEEN_EX_MESSAGE, value, start, end)); - } - } - - /** - * Validate that the specified primitive value falls between the two inclusive - * values specified; otherwise, throws an exception with the specified message. - * - *
-	 * Validate.inclusiveBetween(0.1, 2.1, 1.1, "Not in range");
-	 * 
- * - * @param start the inclusive start value - * @param end the inclusive end value - * @param value the value to validate - * @param message the exception message if invalid, not null - * - * @throws IllegalArgumentException if the value falls outside the boundaries - * - * @since 3.3 - */ - public static void inclusiveBetween(final double start, final double end, final double value, - final String message) { - // TODO when breaking BC, consider returning value - if (value < start || value > end) { - throw new IllegalArgumentException(message); - } - } - - // exclusiveBetween - // --------------------------------------------------------------------------------- - - /** - *

- * Validate that the specified argument object fall between the two exclusive - * values specified; otherwise, throws an exception. - *

- * - *
-	 * Validate.exclusiveBetween(0, 2, 1);
-	 * 
- * - * @param the type of the argument object - * @param start the exclusive start value, not null - * @param end the exclusive end value, not null - * @param value the object to validate, not null - * @throws IllegalArgumentException if the value falls outside the boundaries - * @see #exclusiveBetween(Object, Object, Comparable, String, Object...) - * - * @since 3.0 - */ - public static void exclusiveBetween(final T start, final T end, final Comparable value) { - // TODO when breaking BC, consider returning value - if (value.compareTo(start) <= 0 || value.compareTo(end) >= 0) { - throw new IllegalArgumentException(HString.format(DEFAULT_EXCLUSIVE_BETWEEN_EX_MESSAGE, value, start, end)); - } - } - - /** - *

- * Validate that the specified argument object fall between the two exclusive - * values specified; otherwise, throws an exception with the specified message. - *

- * - *
-	 * Validate.exclusiveBetween(0, 2, 1, "Not in boundaries");
-	 * 
- * - * @param the type of the argument object - * @param start the exclusive start value, not null - * @param end the exclusive end value, not null - * @param value the object to validate, not null - * @param message the {@link String#format(String, Object...)} exception message - * if invalid, not null - * @param values the optional values for the formatted exception message, null - * array not recommended - * @throws IllegalArgumentException if the value falls outside the boundaries - * @see #exclusiveBetween(Object, Object, Comparable) - * - * @since 3.0 - */ - public static void exclusiveBetween(final T start, final T end, final Comparable value, final String message, - final Object... values) { - // TODO when breaking BC, consider returning value - if (value.compareTo(start) <= 0 || value.compareTo(end) >= 0) { - throw new IllegalArgumentException(HString.format(message, values)); - } - } - - /** - * Validate that the specified primitive value falls between the two exclusive - * values specified; otherwise, throws an exception. - * - *
-	 * Validate.exclusiveBetween(0, 2, 1);
-	 * 
- * - * @param start the exclusive start value - * @param end the exclusive end value - * @param value the value to validate - * @throws IllegalArgumentException if the value falls out of the boundaries - * - * @since 3.3 - */ - @SuppressWarnings("boxing") - public static void exclusiveBetween(final long start, final long end, final long value) { - // TODO when breaking BC, consider returning value - if (value <= start || value >= end) { - throw new IllegalArgumentException(HString.format(DEFAULT_EXCLUSIVE_BETWEEN_EX_MESSAGE, value, start, end)); - } - } - - /** - * Validate that the specified primitive value falls between the two exclusive - * values specified; otherwise, throws an exception with the specified message. - * - *
-	 * Validate.exclusiveBetween(0, 2, 1, "Not in range");
-	 * 
- * - * @param start the exclusive start value - * @param end the exclusive end value - * @param value the value to validate - * @param message the exception message if invalid, not null - * - * @throws IllegalArgumentException if the value falls outside the boundaries - * - * @since 3.3 - */ - public static void exclusiveBetween(final long start, final long end, final long value, final String message) { - // TODO when breaking BC, consider returning value - if (value <= start || value >= end) { - throw new IllegalArgumentException(message); - } - } - - /** - * Validate that the specified primitive value falls between the two exclusive - * values specified; otherwise, throws an exception. - * - *
-	 * Validate.exclusiveBetween(0.1, 2.1, 1.1);
-	 * 
- * - * @param start the exclusive start value - * @param end the exclusive end value - * @param value the value to validate - * @throws IllegalArgumentException if the value falls out of the boundaries - * - * @since 3.3 - */ - @SuppressWarnings("boxing") - public static void exclusiveBetween(final double start, final double end, final double value) { - // TODO when breaking BC, consider returning value - if (value <= start || value >= end) { - throw new IllegalArgumentException(HString.format(DEFAULT_EXCLUSIVE_BETWEEN_EX_MESSAGE, value, start, end)); - } - } - - /** - * Validate that the specified primitive value falls between the two exclusive - * values specified; otherwise, throws an exception with the specified message. - * - *
-	 * Validate.exclusiveBetween(0.1, 2.1, 1.1, "Not in range");
-	 * 
- * - * @param start the exclusive start value - * @param end the exclusive end value - * @param value the value to validate - * @param message the exception message if invalid, not null - * - * @throws IllegalArgumentException if the value falls outside the boundaries - * - * @since 3.3 - */ - public static void exclusiveBetween(final double start, final double end, final double value, - final String message) { - // TODO when breaking BC, consider returning value - if (value <= start || value >= end) { - throw new IllegalArgumentException(message); - } - } - - // isInstanceOf - // --------------------------------------------------------------------------------- - - /** - * Validates that the argument is an instance of the specified class, if not - * throws an exception. - * - *

- * This method is useful when validating according to an arbitrary class - *

- * - *
-	 * Validate.isInstanceOf(OkClass.class, object);
-	 * 
- * - *

- * The message of the exception is "Expected type: {type}, actual: - * {obj_type}" - *

- * - * @param type the class the object must be validated against, not null - * @param obj the object to check, null throws an exception - * @throws IllegalArgumentException if argument is not of specified class - * @see #isInstanceOf(Class, Object, String, Object...) - * - * @since 3.0 - */ - public static void isInstanceOf(final Class type, final Object obj) { - // TODO when breaking BC, consider returning obj - if (!type.isInstance(obj)) { - throw new IllegalArgumentException(HString.format(DEFAULT_IS_INSTANCE_OF_EX_MESSAGE, type.getName(), - obj == null ? "null" : obj.getClass().getName())); - } - } - - /** - *

- * Validate that the argument is an instance of the specified class; otherwise - * throwing an exception with the specified message. This method is useful when - * validating according to an arbitrary class - *

- * - *
-	 * Validate.isInstanceOf(OkClass.class, object, "Wrong class, object is of class %s", object.getClass().getName());
-	 * 
- * - * @param type the class the object must be validated against, not null - * @param obj the object to check, null throws an exception - * @param message the {@link String#format(String, Object...)} exception message - * if invalid, not null - * @param values the optional values for the formatted exception message, null - * array not recommended - * @throws IllegalArgumentException if argument is not of specified class - * @see #isInstanceOf(Class, Object) - * - * @since 3.0 - */ - public static void isInstanceOf(final Class type, final Object obj, final String message, - final Object... values) { - // TODO when breaking BC, consider returning obj - if (!type.isInstance(obj)) { - throw new IllegalArgumentException(HString.format(message, values)); - } - } - - // isAssignableFrom - // --------------------------------------------------------------------------------- - - /** - * Validates that the argument can be converted to the specified class, if not, - * throws an exception. - * - *

- * This method is useful when validating that there will be no casting errors. - *

- * - *
-	 * Validate.isAssignableFrom(SuperClass.class, object.getClass());
-	 * 
- * - *

- * The message format of the exception is "Cannot assign {type} to - * {superType}" - *

- * - * @param superType the class the class must be validated against, not null - * @param type the class to check, not null - * @throws IllegalArgumentException if type argument is not assignable to the - * specified superType - * @see #isAssignableFrom(Class, Class, String, Object...) - * - * @since 3.0 - */ - public static void isAssignableFrom(final Class superType, final Class type) { - // TODO when breaking BC, consider returning type - if (!superType.isAssignableFrom(type)) { - throw new IllegalArgumentException(HString.format(DEFAULT_IS_ASSIGNABLE_EX_MESSAGE, - type == null ? "null" : type.getName(), superType.getName())); - } - } - - /** - * Validates that the argument can be converted to the specified class, if not - * throws an exception. - * - *

- * This method is useful when validating if there will be no casting errors. - *

- * - *
-	 * Validate.isAssignableFrom(SuperClass.class, object.getClass());
-	 * 
- * - *

- * The message of the exception is "The validated object can not be - * converted to the" followed by the name of the class and - * "class" - *

- * - * @param superType the class the class must be validated against, not null - * @param type the class to check, not null - * @param message the {@link String#format(String, Object...)} exception - * message if invalid, not null - * @param values the optional values for the formatted exception message, - * null array not recommended - * @throws IllegalArgumentException if argument can not be converted to the - * specified class - * @see #isAssignableFrom(Class, Class) - */ - public static void isAssignableFrom(final Class superType, final Class type, final String message, - final Object... values) { - // TODO when breaking BC, consider returning type - if (!superType.isAssignableFrom(type)) { - throw new IllegalArgumentException(HString.format(message, values)); - } - } -} diff --git a/src/main/java/org/apache/commons/lang3/arch/Processor.java b/src/main/java/org/apache/commons/lang3/arch/Processor.java deleted file mode 100755 index 2a2fd203..00000000 --- a/src/main/java/org/apache/commons/lang3/arch/Processor.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.arch; - -/** - * The {@link Processor} represents a microprocessor and defines some properties - * like architecture and type of the microprocessor. - * - * @since 3.6 - */ -public class Processor { - - /** - * The {@link Arch} enum defines the architecture of a microprocessor. The - * architecture represents the bit value of the microprocessor. The following - * architectures are defined: - *
    - *
  • 32-bit
  • - *
  • 64-bit
  • - *
  • Unknown
  • - *
- */ - public enum Arch { - - /** - * A 32-bit processor architecture. - */ - BIT_32("32-bit"), - - /** - * A 64-bit processor architecture. - */ - BIT_64("64-bit"), - - /** - * An unknown-bit processor architecture. - */ - UNKNOWN("Unknown"); - - /** - * A label suitable for display. - * - * @since 3.10 - */ - private final String label; - - Arch(final String label) { - this.label = label; - } - - /** - * Gets the label suitable for display. - * - * @return the label. - */ - public String getLabel() { - return label; - } - } - - /** - * The {@link Type} enum defines types of a microprocessor. The following types - * are defined: - *
    - *
  • x86
  • - *
  • ia64
  • - *
  • PPC
  • - *
  • Unknown
  • - *
- */ - public enum Type { - - /** - * Intel x86 series of instruction set architectures. - */ - X86, - - /** - * Intel Itanium 64-bit architecture. - */ - IA_64, - - /** - * Apple–IBM–Motorola PowerPC architecture. - */ - PPC, - - /** - * Unknown architecture. - */ - UNKNOWN - } - - private final Arch arch; - private final Type type; - - /** - * Constructs a {@link Processor} object with the given parameters. - * - * @param arch The processor architecture. - * @param type The processor type. - */ - public Processor(final Arch arch, final Type type) { - this.arch = arch; - this.type = type; - } - - /** - * Returns the processor architecture as an {@link Arch} enum. The processor - * architecture defines, if the processor has a 32 or 64 bit architecture. - * - * @return A {@link Arch} enum. - */ - public Arch getArch() { - return arch; - } - - /** - * Returns the processor type as {@link Type} enum. The processor type defines, - * if the processor is for example a x86 or PPA. - * - * @return A {@link Type} enum. - */ - public Type getType() { - return type; - } - - /** - * Checks if {@link Processor} is 32 bit. - * - * @return {@code true}, if {@link Processor} is {@link Arch#BIT_32}, else - * {@code false}. - */ - public boolean is32Bit() { - return Arch.BIT_32 == arch; - } - - /** - * Checks if {@link Processor} is 64 bit. - * - * @return {@code true}, if {@link Processor} is {@link Arch#BIT_64}, else - * {@code false}. - */ - public boolean is64Bit() { - return Arch.BIT_64 == arch; - } - - /** - * Checks if {@link Processor} is type of x86. - * - * @return {@code true}, if {@link Processor} is {@link Type#X86}, else - * {@code false}. - */ - public boolean isX86() { - return Type.X86 == type; - } - - /** - * Checks if {@link Processor} is type of Intel Itanium. - * - * @return {@code true}. if {@link Processor} is {@link Type#IA_64}, else - * {@code false}. - */ - public boolean isIA64() { - return Type.IA_64 == type; - } - - /** - * Checks if {@link Processor} is type of Power PC. - * - * @return {@code true}. if {@link Processor} is {@link Type#PPC}, else - * {@code false}. - */ - public boolean isPPC() { - return Type.PPC == type; - } - -} diff --git a/src/main/java/org/apache/commons/lang3/arch/package-info.java b/src/main/java/org/apache/commons/lang3/arch/package-info.java deleted file mode 100755 index 76a2402c..00000000 --- a/src/main/java/org/apache/commons/lang3/arch/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Provides classes to work with the values of the os.arch system property. - * - * @since 3.6 - */ -package org.apache.commons.lang3.arch; diff --git a/src/main/java/org/apache/commons/lang3/builder/Builder.java b/src/main/java/org/apache/commons/lang3/builder/Builder.java deleted file mode 100755 index b80cf54d..00000000 --- a/src/main/java/org/apache/commons/lang3/builder/Builder.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.builder; - -/** - *

- * The Builder interface is designed to designate a class as a builder - * object in the Builder design pattern. Builders are capable of creating and - * configuring objects or results that normally take multiple steps to construct - * or are very complex to derive. - *

- * - *

- * The builder interface defines a single method, {@link #build()}, that classes - * must implement. The result of this method should be the final configured - * object or result after all building operations are performed. - *

- * - *

- * It is a recommended practice that the methods supplied to configure the - * object or result being built return a reference to {@code this} so that - * method calls can be chained together. - *

- * - *

- * Example Builder: - * - *

- * 
- * class FontBuilder implements Builder<Font> {
- *     private Font font;
- *
- *     public FontBuilder(String fontName) {
- *         this.font = new Font(fontName, Font.PLAIN, 12);
- *     }
- *
- *     public FontBuilder bold() {
- *         this.font = this.font.deriveFont(Font.BOLD);
- *         return this; // Reference returned so calls can be chained
- *     }
- *
- *     public FontBuilder size(float pointSize) {
- *         this.font = this.font.deriveFont(pointSize);
- *         return this; // Reference returned so calls can be chained
- *     }
- *
- *     // Other Font construction methods
- *
- *     public Font build() {
- *         return this.font;
- *     }
- * }
- * 
- * 
- * - * Example Builder Usage: - * - *
- * 
- * Font bold14ptSansSerifFont = new FontBuilder(Font.SANS_SERIF).bold()
- *                                                              .size(14.0f)
- *                                                              .build();
- * 
- * 
- * - * - * @param the type of object that the builder will construct or compute. - * - * @since 3.0 - */ -@FunctionalInterface -public interface Builder { - - /** - * Returns a reference to the object being constructed or result being - * calculated by the builder. - * - * @return the object constructed or result calculated by the builder. - */ - T build(); -} diff --git a/src/main/java/org/apache/commons/lang3/builder/ToStringBuilder.java b/src/main/java/org/apache/commons/lang3/builder/ToStringBuilder.java deleted file mode 100755 index 50245001..00000000 --- a/src/main/java/org/apache/commons/lang3/builder/ToStringBuilder.java +++ /dev/null @@ -1,1138 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.builder; - -import org.apache.commons.lang3.ObjectUtils; -import org.apache.commons.lang3.Validate; - -/** - *

- * Assists in implementing {@link Object#toString()} methods. - *

- * - *

- * This class enables a good and consistent {@code toString()} to be built for - * any class or object. This class aims to simplify the process by: - *

- *
    - *
  • allowing field names
  • - *
  • handling all types consistently
  • - *
  • handling nulls consistently
  • - *
  • outputting arrays and multi-dimensional arrays
  • - *
  • enabling the detail level to be controlled for Objects and - * Collections
  • - *
  • handling class hierarchies
  • - *
- * - *

- * To use this class write code as follows: - *

- * - *
- * public class Person {
- *   String name;
- *   int age;
- *   boolean smoker;
- *
- *   ...
- *
- *   public String toString() {
- *     return new ToStringBuilder(this).
- *       append("name", name).
- *       append("age", age).
- *       append("smoker", smoker).
- *       toString();
- *   }
- * }
- * 
- * - *

- * This will produce a toString of the format: - * {@code Person@7f54[name=Stephen,age=29,smoker=false]} - *

- * - *

- * To add the superclass {@code toString}, use {@link #appendSuper}. To append - * the {@code toString} from an object that is delegated to (or any other - * object), use {@link #appendToString}. - *

- * - *

- * Alternatively, there is a method that uses reflection to determine the fields - * to test. Because these fields are usually private, the method, - * {@code reflectionToString}, uses {@code AccessibleObject.setAccessible} to - * change the visibility of the fields. This will fail under a security manager, - * unless the appropriate permissions are set up correctly. It is also slower - * than testing explicitly. - *

- * - *

- * A typical invocation for this method would look like: - *

- * - *
- * public String toString() {
- * 	return ToStringBuilder.reflectionToString(this);
- * }
- * 
- * - *

- * You can also use the builder to debug 3rd party objects: - *

- * - *
- * System.out.println("An object: " + ToStringBuilder.reflectionToString(anObject));
- * 
- * - *

- * The exact format of the {@code toString} is determined by the - * {@link ToStringStyle} passed into the constructor. - *

- * - * @since 1.0 - */ -public class ToStringBuilder implements Builder { - - /** - * The default style of output to use, not null. - */ - private static volatile ToStringStyle defaultStyle = ToStringStyle.DEFAULT_STYLE; - - // ---------------------------------------------------------------------------- - - /** - *

- * Gets the default {@code ToStringStyle} to use. - *

- * - *

- * This method gets a singleton default value, typically for the whole JVM. - * Changing this default should generally only be done during application - * startup. It is recommended to pass a {@code ToStringStyle} to the constructor - * instead of using this global default. - *

- * - *

- * This method can be used from multiple threads. Internally, a {@code volatile} - * variable is used to provide the guarantee that the latest value set using - * {@link #setDefaultStyle} is the value returned. It is strongly recommended - * that the default style is only changed during application startup. - *

- * - *

- * One reason for changing the default could be to have a verbose style during - * development and a compact style in production. - *

- * - * @return the default {@code ToStringStyle}, never null - */ - public static ToStringStyle getDefaultStyle() { - return defaultStyle; - } - - /** - *

- * Sets the default {@code ToStringStyle} to use. - *

- * - *

- * This method sets a singleton default value, typically for the whole JVM. - * Changing this default should generally only be done during application - * startup. It is recommended to pass a {@code ToStringStyle} to the constructor - * instead of changing this global default. - *

- * - *

- * This method is not intended for use from multiple threads. Internally, a - * {@code volatile} variable is used to provide the guarantee that the latest - * value set is the value returned from {@link #getDefaultStyle}. - *

- * - * @param style the default {@code ToStringStyle} - * @throws IllegalArgumentException if the style is {@code null} - */ - public static void setDefaultStyle(final ToStringStyle style) { - defaultStyle = Validate.notNull(style, "style"); - } - - // ---------------------------------------------------------------------------- - - /** - * Current toString buffer, not null. - */ - private final StringBuffer buffer; - /** - * The object being output, may be null. - */ - private final Object object; - /** - * The style of output to use, not null. - */ - private final ToStringStyle style; - - /** - *

- * Constructs a builder for the specified object using the default output style. - *

- * - *

- * This default style is obtained from {@link #getDefaultStyle()}. - *

- * - * @param object the Object to build a {@code toString} for, not recommended to - * be null - */ - public ToStringBuilder(final Object object) { - this(object, null, null); - } - - /** - *

- * Constructs a builder for the specified object using the defined output style. - *

- * - *

- * If the style is {@code null}, the default style is used. - *

- * - * @param object the Object to build a {@code toString} for, not recommended to - * be null - * @param style the style of the {@code toString} to create, null uses the - * default style - */ - public ToStringBuilder(final Object object, final ToStringStyle style) { - this(object, style, null); - } - - /** - *

- * Constructs a builder for the specified object. - *

- * - *

- * If the style is {@code null}, the default style is used. - *

- * - *

- * If the buffer is {@code null}, a new one is created. - *

- * - * @param object the Object to build a {@code toString} for, not recommended to - * be null - * @param style the style of the {@code toString} to create, null uses the - * default style - * @param buffer the {@code StringBuffer} to populate, may be null - */ - public ToStringBuilder(final Object object, ToStringStyle style, StringBuffer buffer) { - if (style == null) { - style = getDefaultStyle(); - } - if (buffer == null) { - buffer = new StringBuffer(512); - } - this.buffer = buffer; - this.style = style; - this.object = object; - - style.appendStart(buffer, object); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code boolean} value. - *

- * - * @param value the value to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final boolean value) { - style.append(buffer, null, value); - return this; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code boolean} array. - *

- * - * @param array the array to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final boolean[] array) { - style.append(buffer, null, array, null); - return this; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code byte} value. - *

- * - * @param value the value to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final byte value) { - style.append(buffer, null, value); - return this; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code byte} array. - *

- * - * @param array the array to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final byte[] array) { - style.append(buffer, null, array, null); - return this; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code char} value. - *

- * - * @param value the value to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final char value) { - style.append(buffer, null, value); - return this; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code char} array. - *

- * - * @param array the array to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final char[] array) { - style.append(buffer, null, array, null); - return this; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code double} value. - *

- * - * @param value the value to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final double value) { - style.append(buffer, null, value); - return this; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code double} array. - *

- * - * @param array the array to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final double[] array) { - style.append(buffer, null, array, null); - return this; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code float} value. - *

- * - * @param value the value to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final float value) { - style.append(buffer, null, value); - return this; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code float} array. - *

- * - * @param array the array to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final float[] array) { - style.append(buffer, null, array, null); - return this; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} an {@code int} value. - *

- * - * @param value the value to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final int value) { - style.append(buffer, null, value); - return this; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} an {@code int} array. - *

- * - * @param array the array to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final int[] array) { - style.append(buffer, null, array, null); - return this; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code long} value. - *

- * - * @param value the value to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final long value) { - style.append(buffer, null, value); - return this; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code long} array. - *

- * - * @param array the array to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final long[] array) { - style.append(buffer, null, array, null); - return this; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} an {@code Object} value. - *

- * - * @param obj the value to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final Object obj) { - style.append(buffer, null, obj, null); - return this; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} an {@code Object} array. - *

- * - * @param array the array to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final Object[] array) { - style.append(buffer, null, array, null); - return this; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code short} value. - *

- * - * @param value the value to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final short value) { - style.append(buffer, null, value); - return this; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code short} array. - *

- * - * @param array the array to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final short[] array) { - style.append(buffer, null, array, null); - return this; - } - - /** - *

- * Append to the {@code toString} a {@code boolean} value. - *

- * - * @param fieldName the field name - * @param value the value to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final String fieldName, final boolean value) { - style.append(buffer, fieldName, value); - return this; - } - - /** - *

- * Append to the {@code toString} a {@code boolean} array. - *

- * - * @param fieldName the field name - * @param array the array to add to the {@code hashCode} - * @return this - */ - public ToStringBuilder append(final String fieldName, final boolean[] array) { - style.append(buffer, fieldName, array, null); - return this; - } - - /** - *

- * Append to the {@code toString} a {@code boolean} array. - *

- * - *

- * A boolean parameter controls the level of detail to show. Setting - * {@code true} will output the array in full. Setting {@code false} will output - * a summary, typically the size of the array. - *

- * - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @param fullDetail {@code true} for detail, {@code false} for summary info - * @return this - */ - public ToStringBuilder append(final String fieldName, final boolean[] array, final boolean fullDetail) { - style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); - return this; - } - - /** - *

- * Append to the {@code toString} an {@code byte} value. - *

- * - * @param fieldName the field name - * @param value the value to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final String fieldName, final byte value) { - style.append(buffer, fieldName, value); - return this; - } - - /** - *

- * Append to the {@code toString} a {@code byte} array. - *

- * - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final String fieldName, final byte[] array) { - style.append(buffer, fieldName, array, null); - return this; - } - - /** - *

- * Append to the {@code toString} a {@code byte} array. - *

- * - *

- * A boolean parameter controls the level of detail to show. Setting - * {@code true} will output the array in full. Setting {@code false} will output - * a summary, typically the size of the array. - * - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @param fullDetail {@code true} for detail, {@code false} for summary info - * @return this - */ - public ToStringBuilder append(final String fieldName, final byte[] array, final boolean fullDetail) { - style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); - return this; - } - - /** - *

- * Append to the {@code toString} a {@code char} value. - *

- * - * @param fieldName the field name - * @param value the value to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final String fieldName, final char value) { - style.append(buffer, fieldName, value); - return this; - } - - /** - *

- * Append to the {@code toString} a {@code char} array. - *

- * - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final String fieldName, final char[] array) { - style.append(buffer, fieldName, array, null); - return this; - } - - /** - *

- * Append to the {@code toString} a {@code char} array. - *

- * - *

- * A boolean parameter controls the level of detail to show. Setting - * {@code true} will output the array in full. Setting {@code false} will output - * a summary, typically the size of the array. - *

- * - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @param fullDetail {@code true} for detail, {@code false} for summary info - * @return this - */ - public ToStringBuilder append(final String fieldName, final char[] array, final boolean fullDetail) { - style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); - return this; - } - - /** - *

- * Append to the {@code toString} a {@code double} value. - *

- * - * @param fieldName the field name - * @param value the value to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final String fieldName, final double value) { - style.append(buffer, fieldName, value); - return this; - } - - /** - *

- * Append to the {@code toString} a {@code double} array. - *

- * - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final String fieldName, final double[] array) { - style.append(buffer, fieldName, array, null); - return this; - } - - /** - *

- * Append to the {@code toString} a {@code double} array. - *

- * - *

- * A boolean parameter controls the level of detail to show. Setting - * {@code true} will output the array in full. Setting {@code false} will output - * a summary, typically the size of the array. - *

- * - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @param fullDetail {@code true} for detail, {@code false} for summary info - * @return this - */ - public ToStringBuilder append(final String fieldName, final double[] array, final boolean fullDetail) { - style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); - return this; - } - - /** - *

- * Append to the {@code toString} an {@code float} value. - *

- * - * @param fieldName the field name - * @param value the value to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final String fieldName, final float value) { - style.append(buffer, fieldName, value); - return this; - } - - /** - *

- * Append to the {@code toString} a {@code float} array. - *

- * - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final String fieldName, final float[] array) { - style.append(buffer, fieldName, array, null); - return this; - } - - /** - *

- * Append to the {@code toString} a {@code float} array. - *

- * - *

- * A boolean parameter controls the level of detail to show. Setting - * {@code true} will output the array in full. Setting {@code false} will output - * a summary, typically the size of the array. - *

- * - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @param fullDetail {@code true} for detail, {@code false} for summary info - * @return this - */ - public ToStringBuilder append(final String fieldName, final float[] array, final boolean fullDetail) { - style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); - return this; - } - - /** - *

- * Append to the {@code toString} an {@code int} value. - *

- * - * @param fieldName the field name - * @param value the value to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final String fieldName, final int value) { - style.append(buffer, fieldName, value); - return this; - } - - /** - *

- * Append to the {@code toString} an {@code int} array. - *

- * - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final String fieldName, final int[] array) { - style.append(buffer, fieldName, array, null); - return this; - } - - /** - *

- * Append to the {@code toString} an {@code int} array. - *

- * - *

- * A boolean parameter controls the level of detail to show. Setting - * {@code true} will output the array in full. Setting {@code false} will output - * a summary, typically the size of the array. - *

- * - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @param fullDetail {@code true} for detail, {@code false} for summary info - * @return this - */ - public ToStringBuilder append(final String fieldName, final int[] array, final boolean fullDetail) { - style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); - return this; - } - - /** - *

- * Append to the {@code toString} a {@code long} value. - *

- * - * @param fieldName the field name - * @param value the value to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final String fieldName, final long value) { - style.append(buffer, fieldName, value); - return this; - } - - /** - *

- * Append to the {@code toString} a {@code long} array. - *

- * - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final String fieldName, final long[] array) { - style.append(buffer, fieldName, array, null); - return this; - } - - /** - *

- * Append to the {@code toString} a {@code long} array. - *

- * - *

- * A boolean parameter controls the level of detail to show. Setting - * {@code true} will output the array in full. Setting {@code false} will output - * a summary, typically the size of the array. - *

- * - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @param fullDetail {@code true} for detail, {@code false} for summary info - * @return this - */ - public ToStringBuilder append(final String fieldName, final long[] array, final boolean fullDetail) { - style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); - return this; - } - - /** - *

- * Append to the {@code toString} an {@code Object} value. - *

- * - * @param fieldName the field name - * @param obj the value to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final String fieldName, final Object obj) { - style.append(buffer, fieldName, obj, null); - return this; - } - - /** - *

- * Append to the {@code toString} an {@code Object} value. - *

- * - * @param fieldName the field name - * @param obj the value to add to the {@code toString} - * @param fullDetail {@code true} for detail, {@code false} for summary info - * @return this - */ - public ToStringBuilder append(final String fieldName, final Object obj, final boolean fullDetail) { - style.append(buffer, fieldName, obj, Boolean.valueOf(fullDetail)); - return this; - } - - /** - *

- * Append to the {@code toString} an {@code Object} array. - *

- * - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final String fieldName, final Object[] array) { - style.append(buffer, fieldName, array, null); - return this; - } - - /** - *

- * Append to the {@code toString} an {@code Object} array. - *

- * - *

- * A boolean parameter controls the level of detail to show. Setting - * {@code true} will output the array in full. Setting {@code false} will output - * a summary, typically the size of the array. - *

- * - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @param fullDetail {@code true} for detail, {@code false} for summary info - * @return this - */ - public ToStringBuilder append(final String fieldName, final Object[] array, final boolean fullDetail) { - style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); - return this; - } - - /** - *

- * Append to the {@code toString} an {@code short} value. - *

- * - * @param fieldName the field name - * @param value the value to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final String fieldName, final short value) { - style.append(buffer, fieldName, value); - return this; - } - - /** - *

- * Append to the {@code toString} a {@code short} array. - *

- * - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @return this - */ - public ToStringBuilder append(final String fieldName, final short[] array) { - style.append(buffer, fieldName, array, null); - return this; - } - - /** - *

- * Append to the {@code toString} a {@code short} array. - *

- * - *

- * A boolean parameter controls the level of detail to show. Setting - * {@code true} will output the array in full. Setting {@code false} will output - * a summary, typically the size of the array. - * - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @param fullDetail {@code true} for detail, {@code false} for summary info - * @return this - */ - public ToStringBuilder append(final String fieldName, final short[] array, final boolean fullDetail) { - style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); - return this; - } - - /** - *

- * Appends with the same format as the default Object toString() - * method. Appends the class name followed by - * {@link System#identityHashCode(java.lang.Object)}. - *

- * - * @param srcObject the {@code Object} whose class name and id to output - * @return this - * @since 2.0 - */ - public ToStringBuilder appendAsObjectToString(final Object srcObject) { - ObjectUtils.identityToString(this.getStringBuffer(), srcObject); - return this; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append the {@code toString} from the superclass. - *

- * - *

- * This method assumes that the superclass uses the same {@code ToStringStyle} - * as this one. - *

- * - *

- * If {@code superToString} is {@code null}, no change is made. - *

- * - * @param superToString the result of {@code super.toString()} - * @return this - * @since 2.0 - */ - public ToStringBuilder appendSuper(final String superToString) { - if (superToString != null) { - style.appendSuper(buffer, superToString); - } - return this; - } - - /** - *

- * Append the {@code toString} from another object. - *

- * - *

- * This method is useful where a class delegates most of the implementation of - * its properties to another class. You can then call {@code toString()} on the - * other class and pass the result into this method. - *

- * - *
-	 * private AnotherObject delegate;
-	 * private String fieldInThisClass;
-	 *
-	 * public String toString() {
-	 * 	return new ToStringBuilder(this).appendToString(delegate.toString()).append(fieldInThisClass).toString();
-	 * }
-	 * 
- * - *

- * This method assumes that the other object uses the same {@code ToStringStyle} - * as this one. - *

- * - *

- * If the {@code toString} is {@code null}, no change is made. - *

- * - * @param toString the result of {@code toString()} on another object - * @return this - * @since 2.0 - */ - public ToStringBuilder appendToString(final String toString) { - if (toString != null) { - style.appendToString(buffer, toString); - } - return this; - } - - /** - *

- * Returns the {@code Object} being output. - *

- * - * @return The object being output. - * @since 2.0 - */ - public Object getObject() { - return object; - } - - /** - *

- * Gets the {@code StringBuffer} being populated. - *

- * - * @return the {@code StringBuffer} being populated - */ - public StringBuffer getStringBuffer() { - return buffer; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Gets the {@code ToStringStyle} being used. - *

- * - * @return the {@code ToStringStyle} being used - * @since 2.0 - */ - public ToStringStyle getStyle() { - return style; - } - - /** - *

- * Returns the built {@code toString}. - *

- * - *

- * This method appends the end of data indicator, and can only be called once. - * Use {@link #getStringBuffer} to get the current string state. - *

- * - *

- * If the object is {@code null}, return the style's {@code nullText} - *

- * - * @return the String {@code toString} - */ - @Override - public String toString() { - if (this.getObject() == null) { - this.getStringBuffer().append(this.getStyle().getNullText()); - } else { - style.appendEnd(this.getStringBuffer(), this.getObject()); - } - return this.getStringBuffer().toString(); - } - - /** - * Returns the String that was build as an object representation. The default - * implementation utilizes the {@link #toString()} implementation. - * - * @return the String {@code toString} - * - * @see #toString() - * - * @since 3.0 - */ - @Override - public String build() { - return toString(); - } -} diff --git a/src/main/java/org/apache/commons/lang3/builder/ToStringExclude.java b/src/main/java/org/apache/commons/lang3/builder/ToStringExclude.java deleted file mode 100755 index 06074ece..00000000 --- a/src/main/java/org/apache/commons/lang3/builder/ToStringExclude.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.builder; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Use this annotation to exclude a field from being used by the - * {@link ReflectionToStringBuilder}. - * - * @since 3.5 - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.FIELD) -public @interface ToStringExclude { - // empty -} diff --git a/src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java b/src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java deleted file mode 100755 index 060dee25..00000000 --- a/src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java +++ /dev/null @@ -1,2910 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.builder; - -import java.io.Serializable; -import java.lang.reflect.Array; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; - -import org.apache.commons.lang3.ObjectUtils; -import org.apache.commons.lang3.StringUtils; - -/** - *

- * Controls {@code String} formatting for {@link ToStringBuilder}. The main - * public interface is always via {@code ToStringBuilder}. - *

- * - *

- * These classes are intended to be used as {@code Singletons}. There is no need - * to instantiate a new style each time. A program will generally use one of the - * predefined constants on this class. Alternatively, the - * {@link StandardToStringStyle} class can be used to set the individual - * settings. Thus most styles can be achieved without subclassing. - *

- * - *

- * If required, a subclass can override as many or as few of the methods as it - * requires. Each object type (from {@code boolean} to {@code long} to - * {@code Object} to {@code int[]}) has its own methods to output it. Most have - * two versions, detail and summary. - * - *

- * For example, the detail version of the array based methods will output the - * whole array, whereas the summary method will just output the array length. - *

- * - *

- * If you want to format the output of certain objects, such as dates, you must - * create a subclass and override a method. - *

- * - *
- * public class MyStyle extends ToStringStyle {
- * 	protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
- * 		if (value instanceof Date) {
- * 			value = new SimpleDateFormat("yyyy-MM-dd").format(value);
- * 		}
- * 		buffer.append(value);
- * 	}
- * }
- * 
- * - * @since 1.0 - */ -@SuppressWarnings("deprecation") // StringEscapeUtils -public abstract class ToStringStyle implements Serializable { - - /** - * Serialization version ID. - */ - private static final long serialVersionUID = -2587890625525655916L; - - /** - * The default toString style. Using the {@code Person} example from - * {@link ToStringBuilder}, the output would look like this: - * - *
-	 * Person@182f0db[name=John Doe,age=33,smoker=false]
-	 * 
- */ - public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle(); - - /** - * The multi line toString style. Using the {@code Person} example from - * {@link ToStringBuilder}, the output would look like this: - * - *
-	 * Person@182f0db[
-	 *   name=John Doe
-	 *   age=33
-	 *   smoker=false
-	 * ]
-	 * 
- */ - public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle(); - - /** - * The no field names toString style. Using the {@code Person} example from - * {@link ToStringBuilder}, the output would look like this: - * - *
-	 * Person@182f0db[John Doe,33,false]
-	 * 
- */ - public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle(); - - /** - * The short prefix toString style. Using the {@code Person} example from - * {@link ToStringBuilder}, the output would look like this: - * - *
-	 * Person[name=John Doe,age=33,smoker=false]
-	 * 
- * - * @since 2.1 - */ - public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle(); - - /** - * The simple toString style. Using the {@code Person} example from - * {@link ToStringBuilder}, the output would look like this: - * - *
-	 * John Doe,33,false
-	 * 
- */ - public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle(); - - /** - * The no class name toString style. Using the {@code Person} example from - * {@link ToStringBuilder}, the output would look like this: - * - *
-	 * [name=John Doe,age=33,smoker=false]
-	 * 
- * - * @since 3.4 - */ - public static final ToStringStyle NO_CLASS_NAME_STYLE = new NoClassNameToStringStyle(); - - /** - * The JSON toString style. Using the {@code Person} example from - * {@link ToStringBuilder}, the output would look like this: - * - *
-	 * {"name": "John Doe", "age": 33, "smoker": true}
-	 * 
- * - * Note: Since field names are mandatory in JSON, this - * ToStringStyle will throw an {@link UnsupportedOperationException} if no field - * name is passed in while appending. Furthermore This ToStringStyle will only - * generate valid JSON if referenced objects also produce JSON when calling - * {@code toString()} on them. - * - * @since 3.4 - * @see json.org - */ - public static final ToStringStyle JSON_STYLE = new JsonToStringStyle(); - - /** - *

- * A registry of objects used by {@code reflectionToString} methods to detect - * cyclical object references and avoid infinite loops. - *

- */ - private static final ThreadLocal> REGISTRY = new ThreadLocal<>(); - /* - * Note that objects of this class are generally shared between threads, so an - * instance variable would not be suitable here. - * - * In normal use the registry should always be left empty, because the caller - * should call toString() which will clean up. - * - * See LANG-792 - */ - - /** - *

- * Returns the registry of objects being traversed by the - * {@code reflectionToString} methods in the current thread. - *

- * - * @return Set the registry of objects being traversed - */ - static Map getRegistry() { - return REGISTRY.get(); - } - - /** - *

- * Returns {@code true} if the registry contains the given object. Used by the - * reflection methods to avoid infinite loops. - *

- * - * @param value The object to lookup in the registry. - * @return boolean {@code true} if the registry contains the given object. - */ - static boolean isRegistered(final Object value) { - final Map m = getRegistry(); - return m != null && m.containsKey(value); - } - - /** - *

- * Registers the given object. Used by the reflection methods to avoid infinite - * loops. - *

- * - * @param value The object to register. - */ - static void register(final Object value) { - if (value != null) { - final Map m = getRegistry(); - if (m == null) { - REGISTRY.set(new HashMap<>()); - } - getRegistry().put(value, null); - } - } - - /** - *

- * Unregisters the given object. - *

- * - *

- * Used by the reflection methods to avoid infinite loops. - *

- * - * @param value The object to unregister. - */ - static void unregister(final Object value) { - if (value != null) { - final Map m = getRegistry(); - if (m != null) { - m.remove(value); - if (m.isEmpty()) { - REGISTRY.remove(); - } - } - } - } - - /** - * Whether to use the field names, the default is {@code true}. - */ - private boolean useFieldNames = true; - - /** - * Whether to use the class name, the default is {@code true}. - */ - private boolean useClassName = true; - - /** - * Whether to use short class names, the default is {@code false}. - */ - private boolean useShortClassName; - - /** - * Whether to use the identity hash code, the default is {@code true}. - */ - private boolean useIdentityHashCode = true; - - /** - * The content start {@code '['}. - */ - private String contentStart = "["; - - /** - * The content end {@code ']'}. - */ - private String contentEnd = "]"; - - /** - * The field name value separator {@code '='}. - */ - private String fieldNameValueSeparator = "="; - - /** - * Whether the field separator should be added before any other fields. - */ - private boolean fieldSeparatorAtStart; - - /** - * Whether the field separator should be added after any other fields. - */ - private boolean fieldSeparatorAtEnd; - - /** - * The field separator {@code ','}. - */ - private String fieldSeparator = ","; - - /** - * The array start '{'. - */ - private String arrayStart = "{"; - - /** - * The array separator {@code ','}. - */ - private String arraySeparator = ","; - - /** - * The detail for array content. - */ - private boolean arrayContentDetail = true; - - /** - * The array end {@code '}'}. - */ - private String arrayEnd = "}"; - - /** - * The value to use when fullDetail is {@code null}, the default value is - * {@code true}. - */ - private boolean defaultFullDetail = true; - - /** - * The {@code null} text {@code '<null>'}. - */ - private String nullText = ""; - - /** - * The summary size text start {@code '<size'}. - */ - private String sizeStartText = " - * Constructor. - *

- */ - protected ToStringStyle() { - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} the superclass toString. - *

- *

- * NOTE: It assumes that the toString has been created from the same - * ToStringStyle. - *

- * - *

- * A {@code null} {@code superToString} is ignored. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param superToString the {@code super.toString()} - * @since 2.0 - */ - public void appendSuper(final StringBuffer buffer, final String superToString) { - appendToString(buffer, superToString); - } - - /** - *

- * Append to the {@code toString} another toString. - *

- *

- * NOTE: It assumes that the toString has been created from the same - * ToStringStyle. - *

- * - *

- * A {@code null} {@code toString} is ignored. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param toString the additional {@code toString} - * @since 2.0 - */ - public void appendToString(final StringBuffer buffer, final String toString) { - if (toString != null) { - final int pos1 = toString.indexOf(contentStart) + contentStart.length(); - final int pos2 = toString.lastIndexOf(contentEnd); - if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) { - if (fieldSeparatorAtStart) { - removeLastFieldSeparator(buffer); - } - buffer.append(toString, pos1, pos2); - appendFieldSeparator(buffer); - } - } - } - - /** - *

- * Append to the {@code toString} the start of data indicator. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param object the {@code Object} to build a {@code toString} for - */ - public void appendStart(final StringBuffer buffer, final Object object) { - if (object != null) { - appendClassName(buffer, object); - appendIdentityHashCode(buffer, object); - appendContentStart(buffer); - if (fieldSeparatorAtStart) { - appendFieldSeparator(buffer); - } - } - } - - /** - *

- * Append to the {@code toString} the end of data indicator. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param object the {@code Object} to build a {@code toString} for. - */ - public void appendEnd(final StringBuffer buffer, final Object object) { - if (!this.fieldSeparatorAtEnd) { - removeLastFieldSeparator(buffer); - } - appendContentEnd(buffer); - unregister(object); - } - - /** - *

- * Remove the last field separator from the buffer. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @since 2.0 - */ - protected void removeLastFieldSeparator(final StringBuffer buffer) { - if (StringUtils.endsWith(buffer, fieldSeparator)) { - buffer.setLength(buffer.length() - fieldSeparator.length()); - } - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} an {@code Object} value, printing the full - * {@code toString} of the {@code Object} passed in. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - * @param value the value to add to the {@code toString} - * @param fullDetail {@code true} for detail, {@code false} for summary info, - * {@code null} for style decides - */ - public void append(final StringBuffer buffer, final String fieldName, final Object value, - final Boolean fullDetail) { - appendFieldStart(buffer, fieldName); - - if (value == null) { - appendNullText(buffer, fieldName); - - } else { - appendInternal(buffer, fieldName, value, isFullDetail(fullDetail)); - } - - appendFieldEnd(buffer, fieldName); - } - - /** - *

- * Append to the {@code toString} an {@code Object}, correctly interpreting its - * type. - *

- * - *

- * This method performs the main lookup by Class type to correctly route arrays, - * {@code Collections}, {@code Maps} and {@code Objects} to the appropriate - * method. - *

- * - *

- * Either detail or summary views can be specified. - *

- * - *

- * If a cycle is detected, an object will be appended with the - * {@code Object.toString()} format. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param value the value to add to the {@code toString}, not {@code null} - * @param detail output detail or not - */ - protected void appendInternal(final StringBuffer buffer, final String fieldName, final Object value, - final boolean detail) { - if (isRegistered(value) - && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) { - appendCyclicObject(buffer, fieldName, value); - return; - } - - register(value); - - try { - if (value instanceof Collection) { - if (detail) { - appendDetail(buffer, fieldName, (Collection) value); - } else { - appendSummarySize(buffer, fieldName, ((Collection) value).size()); - } - - } else if (value instanceof Map) { - if (detail) { - appendDetail(buffer, fieldName, (Map) value); - } else { - appendSummarySize(buffer, fieldName, ((Map) value).size()); - } - - } else if (value instanceof long[]) { - if (detail) { - appendDetail(buffer, fieldName, (long[]) value); - } else { - appendSummary(buffer, fieldName, (long[]) value); - } - - } else if (value instanceof int[]) { - if (detail) { - appendDetail(buffer, fieldName, (int[]) value); - } else { - appendSummary(buffer, fieldName, (int[]) value); - } - - } else if (value instanceof short[]) { - if (detail) { - appendDetail(buffer, fieldName, (short[]) value); - } else { - appendSummary(buffer, fieldName, (short[]) value); - } - - } else if (value instanceof byte[]) { - if (detail) { - appendDetail(buffer, fieldName, (byte[]) value); - } else { - appendSummary(buffer, fieldName, (byte[]) value); - } - - } else if (value instanceof char[]) { - if (detail) { - appendDetail(buffer, fieldName, (char[]) value); - } else { - appendSummary(buffer, fieldName, (char[]) value); - } - - } else if (value instanceof double[]) { - if (detail) { - appendDetail(buffer, fieldName, (double[]) value); - } else { - appendSummary(buffer, fieldName, (double[]) value); - } - - } else if (value instanceof float[]) { - if (detail) { - appendDetail(buffer, fieldName, (float[]) value); - } else { - appendSummary(buffer, fieldName, (float[]) value); - } - - } else if (value instanceof boolean[]) { - if (detail) { - appendDetail(buffer, fieldName, (boolean[]) value); - } else { - appendSummary(buffer, fieldName, (boolean[]) value); - } - - } else if (value.getClass().isArray()) { - if (detail) { - appendDetail(buffer, fieldName, (Object[]) value); - } else { - appendSummary(buffer, fieldName, (Object[]) value); - } - - } else if (detail) { - appendDetail(buffer, fieldName, value); - } else { - appendSummary(buffer, fieldName, value); - } - } finally { - unregister(value); - } - } - - /** - *

- * Append to the {@code toString} an {@code Object} value that has been detected - * to participate in a cycle. This implementation will print the standard string - * value of the value. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param value the value to add to the {@code toString}, not {@code null} - * - * @since 2.2 - */ - protected void appendCyclicObject(final StringBuffer buffer, final String fieldName, final Object value) { - ObjectUtils.identityToString(buffer, value); - } - - /** - *

- * Append to the {@code toString} an {@code Object} value, printing the full - * detail of the {@code Object}. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param value the value to add to the {@code toString}, not {@code null} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) { - buffer.append(value); - } - - /** - *

- * Append to the {@code toString} a {@code Collection}. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param coll the {@code Collection} to add to the {@code toString}, not - * {@code null} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection coll) { - buffer.append(coll); - } - - /** - *

- * Append to the {@code toString} a {@code Map}. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param map the {@code Map} to add to the {@code toString}, not - * {@code null} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map map) { - buffer.append(map); - } - - /** - *

- * Append to the {@code toString} an {@code Object} value, printing a summary of - * the {@code Object}. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param value the value to add to the {@code toString}, not {@code null} - */ - protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object value) { - buffer.append(summaryObjectStartText); - buffer.append(getShortClassName(value.getClass())); - buffer.append(summaryObjectEndText); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code long} value. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - * @param value the value to add to the {@code toString} - */ - public void append(final StringBuffer buffer, final String fieldName, final long value) { - appendFieldStart(buffer, fieldName); - appendDetail(buffer, fieldName, value); - appendFieldEnd(buffer, fieldName); - } - - /** - *

- * Append to the {@code toString} a {@code long} value. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param value the value to add to the {@code toString} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final long value) { - buffer.append(value); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} an {@code int} value. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - * @param value the value to add to the {@code toString} - */ - public void append(final StringBuffer buffer, final String fieldName, final int value) { - appendFieldStart(buffer, fieldName); - appendDetail(buffer, fieldName, value); - appendFieldEnd(buffer, fieldName); - } - - /** - *

- * Append to the {@code toString} an {@code int} value. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param value the value to add to the {@code toString} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final int value) { - buffer.append(value); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code short} value. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - * @param value the value to add to the {@code toString} - */ - public void append(final StringBuffer buffer, final String fieldName, final short value) { - appendFieldStart(buffer, fieldName); - appendDetail(buffer, fieldName, value); - appendFieldEnd(buffer, fieldName); - } - - /** - *

- * Append to the {@code toString} a {@code short} value. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param value the value to add to the {@code toString} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final short value) { - buffer.append(value); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code byte} value. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - * @param value the value to add to the {@code toString} - */ - public void append(final StringBuffer buffer, final String fieldName, final byte value) { - appendFieldStart(buffer, fieldName); - appendDetail(buffer, fieldName, value); - appendFieldEnd(buffer, fieldName); - } - - /** - *

- * Append to the {@code toString} a {@code byte} value. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param value the value to add to the {@code toString} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte value) { - buffer.append(value); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code char} value. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - * @param value the value to add to the {@code toString} - */ - public void append(final StringBuffer buffer, final String fieldName, final char value) { - appendFieldStart(buffer, fieldName); - appendDetail(buffer, fieldName, value); - appendFieldEnd(buffer, fieldName); - } - - /** - *

- * Append to the {@code toString} a {@code char} value. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param value the value to add to the {@code toString} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) { - buffer.append(value); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code double} value. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - * @param value the value to add to the {@code toString} - */ - public void append(final StringBuffer buffer, final String fieldName, final double value) { - appendFieldStart(buffer, fieldName); - appendDetail(buffer, fieldName, value); - appendFieldEnd(buffer, fieldName); - } - - /** - *

- * Append to the {@code toString} a {@code double} value. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param value the value to add to the {@code toString} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final double value) { - buffer.append(value); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code float} value. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - * @param value the value to add to the {@code toString} - */ - public void append(final StringBuffer buffer, final String fieldName, final float value) { - appendFieldStart(buffer, fieldName); - appendDetail(buffer, fieldName, value); - appendFieldEnd(buffer, fieldName); - } - - /** - *

- * Append to the {@code toString} a {@code float} value. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param value the value to add to the {@code toString} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final float value) { - buffer.append(value); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code boolean} value. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - * @param value the value to add to the {@code toString} - */ - public void append(final StringBuffer buffer, final String fieldName, final boolean value) { - appendFieldStart(buffer, fieldName); - appendDetail(buffer, fieldName, value); - appendFieldEnd(buffer, fieldName); - } - - /** - *

- * Append to the {@code toString} a {@code boolean} value. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param value the value to add to the {@code toString} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean value) { - buffer.append(value); - } - - /** - *

- * Append to the {@code toString} an {@code Object} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - * @param array the array to add to the toString - * @param fullDetail {@code true} for detail, {@code false} for summary info, - * {@code null} for style decides - */ - public void append(final StringBuffer buffer, final String fieldName, final Object[] array, - final Boolean fullDetail) { - appendFieldStart(buffer, fieldName); - - if (array == null) { - appendNullText(buffer, fieldName); - - } else if (isFullDetail(fullDetail)) { - appendDetail(buffer, fieldName, array); - - } else { - appendSummary(buffer, fieldName, array); - } - - appendFieldEnd(buffer, fieldName); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} the detail of an {@code Object} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) { - buffer.append(arrayStart); - for (int i = 0; i < array.length; i++) { - final Object item = array[i]; - appendDetail(buffer, fieldName, i, item); - } - buffer.append(arrayEnd); - } - - /** - *

- * Append to the {@code toString} the detail of an {@code Object} array item. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param i the array item index to add - * @param item the array item to add - * @since 3.11 - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final int i, final Object item) { - if (i > 0) { - buffer.append(arraySeparator); - } - if (item == null) { - appendNullText(buffer, fieldName); - } else { - appendInternal(buffer, fieldName, item, arrayContentDetail); - } - } - - /** - *

- * Append to the {@code toString} the detail of an array type. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - * @since 2.0 - */ - protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) { - buffer.append(arrayStart); - final int length = Array.getLength(array); - for (int i = 0; i < length; i++) { - final Object item = Array.get(array, i); - appendDetail(buffer, fieldName, i, item); - } - buffer.append(arrayEnd); - } - - /** - *

- * Append to the {@code toString} a summary of an {@code Object} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - */ - protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object[] array) { - appendSummarySize(buffer, fieldName, array.length); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code long} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @param fullDetail {@code true} for detail, {@code false} for summary info, - * {@code null} for style decides - */ - public void append(final StringBuffer buffer, final String fieldName, final long[] array, - final Boolean fullDetail) { - appendFieldStart(buffer, fieldName); - - if (array == null) { - appendNullText(buffer, fieldName); - - } else if (isFullDetail(fullDetail)) { - appendDetail(buffer, fieldName, array); - - } else { - appendSummary(buffer, fieldName, array); - } - - appendFieldEnd(buffer, fieldName); - } - - /** - *

- * Append to the {@code toString} the detail of a {@code long} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) { - buffer.append(arrayStart); - for (int i = 0; i < array.length; i++) { - if (i > 0) { - buffer.append(arraySeparator); - } - appendDetail(buffer, fieldName, array[i]); - } - buffer.append(arrayEnd); - } - - /** - *

- * Append to the {@code toString} a summary of a {@code long} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - */ - protected void appendSummary(final StringBuffer buffer, final String fieldName, final long[] array) { - appendSummarySize(buffer, fieldName, array.length); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} an {@code int} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @param fullDetail {@code true} for detail, {@code false} for summary info, - * {@code null} for style decides - */ - public void append(final StringBuffer buffer, final String fieldName, final int[] array, final Boolean fullDetail) { - appendFieldStart(buffer, fieldName); - - if (array == null) { - appendNullText(buffer, fieldName); - - } else if (isFullDetail(fullDetail)) { - appendDetail(buffer, fieldName, array); - - } else { - appendSummary(buffer, fieldName, array); - } - - appendFieldEnd(buffer, fieldName); - } - - /** - *

- * Append to the {@code toString} the detail of an {@code int} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) { - buffer.append(arrayStart); - for (int i = 0; i < array.length; i++) { - if (i > 0) { - buffer.append(arraySeparator); - } - appendDetail(buffer, fieldName, array[i]); - } - buffer.append(arrayEnd); - } - - /** - *

- * Append to the {@code toString} a summary of an {@code int} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - */ - protected void appendSummary(final StringBuffer buffer, final String fieldName, final int[] array) { - appendSummarySize(buffer, fieldName, array.length); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code short} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @param fullDetail {@code true} for detail, {@code false} for summary info, - * {@code null} for style decides - */ - public void append(final StringBuffer buffer, final String fieldName, final short[] array, - final Boolean fullDetail) { - appendFieldStart(buffer, fieldName); - - if (array == null) { - appendNullText(buffer, fieldName); - - } else if (isFullDetail(fullDetail)) { - appendDetail(buffer, fieldName, array); - - } else { - appendSummary(buffer, fieldName, array); - } - - appendFieldEnd(buffer, fieldName); - } - - /** - *

- * Append to the {@code toString} the detail of a {@code short} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) { - buffer.append(arrayStart); - for (int i = 0; i < array.length; i++) { - if (i > 0) { - buffer.append(arraySeparator); - } - appendDetail(buffer, fieldName, array[i]); - } - buffer.append(arrayEnd); - } - - /** - *

- * Append to the {@code toString} a summary of a {@code short} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - */ - protected void appendSummary(final StringBuffer buffer, final String fieldName, final short[] array) { - appendSummarySize(buffer, fieldName, array.length); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code byte} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @param fullDetail {@code true} for detail, {@code false} for summary info, - * {@code null} for style decides - */ - public void append(final StringBuffer buffer, final String fieldName, final byte[] array, - final Boolean fullDetail) { - appendFieldStart(buffer, fieldName); - - if (array == null) { - appendNullText(buffer, fieldName); - - } else if (isFullDetail(fullDetail)) { - appendDetail(buffer, fieldName, array); - - } else { - appendSummary(buffer, fieldName, array); - } - - appendFieldEnd(buffer, fieldName); - } - - /** - *

- * Append to the {@code toString} the detail of a {@code byte} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) { - buffer.append(arrayStart); - for (int i = 0; i < array.length; i++) { - if (i > 0) { - buffer.append(arraySeparator); - } - appendDetail(buffer, fieldName, array[i]); - } - buffer.append(arrayEnd); - } - - /** - *

- * Append to the {@code toString} a summary of a {@code byte} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - */ - protected void appendSummary(final StringBuffer buffer, final String fieldName, final byte[] array) { - appendSummarySize(buffer, fieldName, array.length); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code char} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - * @param array the array to add to the {@code toString} - * @param fullDetail {@code true} for detail, {@code false} for summary info, - * {@code null} for style decides - */ - public void append(final StringBuffer buffer, final String fieldName, final char[] array, - final Boolean fullDetail) { - appendFieldStart(buffer, fieldName); - - if (array == null) { - appendNullText(buffer, fieldName); - - } else if (isFullDetail(fullDetail)) { - appendDetail(buffer, fieldName, array); - - } else { - appendSummary(buffer, fieldName, array); - } - - appendFieldEnd(buffer, fieldName); - } - - /** - *

- * Append to the {@code toString} the detail of a {@code char} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) { - buffer.append(arrayStart); - for (int i = 0; i < array.length; i++) { - if (i > 0) { - buffer.append(arraySeparator); - } - appendDetail(buffer, fieldName, array[i]); - } - buffer.append(arrayEnd); - } - - /** - *

- * Append to the {@code toString} a summary of a {@code char} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - */ - protected void appendSummary(final StringBuffer buffer, final String fieldName, final char[] array) { - appendSummarySize(buffer, fieldName, array.length); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code double} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - * @param array the array to add to the toString - * @param fullDetail {@code true} for detail, {@code false} for summary info, - * {@code null} for style decides - */ - public void append(final StringBuffer buffer, final String fieldName, final double[] array, - final Boolean fullDetail) { - appendFieldStart(buffer, fieldName); - - if (array == null) { - appendNullText(buffer, fieldName); - - } else if (isFullDetail(fullDetail)) { - appendDetail(buffer, fieldName, array); - - } else { - appendSummary(buffer, fieldName, array); - } - - appendFieldEnd(buffer, fieldName); - } - - /** - *

- * Append to the {@code toString} the detail of a {@code double} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) { - buffer.append(arrayStart); - for (int i = 0; i < array.length; i++) { - if (i > 0) { - buffer.append(arraySeparator); - } - appendDetail(buffer, fieldName, array[i]); - } - buffer.append(arrayEnd); - } - - /** - *

- * Append to the {@code toString} a summary of a {@code double} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - */ - protected void appendSummary(final StringBuffer buffer, final String fieldName, final double[] array) { - appendSummarySize(buffer, fieldName, array.length); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code float} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - * @param array the array to add to the toString - * @param fullDetail {@code true} for detail, {@code false} for summary info, - * {@code null} for style decides - */ - public void append(final StringBuffer buffer, final String fieldName, final float[] array, - final Boolean fullDetail) { - appendFieldStart(buffer, fieldName); - - if (array == null) { - appendNullText(buffer, fieldName); - - } else if (isFullDetail(fullDetail)) { - appendDetail(buffer, fieldName, array); - - } else { - appendSummary(buffer, fieldName, array); - } - - appendFieldEnd(buffer, fieldName); - } - - /** - *

- * Append to the {@code toString} the detail of a {@code float} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) { - buffer.append(arrayStart); - for (int i = 0; i < array.length; i++) { - if (i > 0) { - buffer.append(arraySeparator); - } - appendDetail(buffer, fieldName, array[i]); - } - buffer.append(arrayEnd); - } - - /** - *

- * Append to the {@code toString} a summary of a {@code float} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - */ - protected void appendSummary(final StringBuffer buffer, final String fieldName, final float[] array) { - appendSummarySize(buffer, fieldName, array.length); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} a {@code boolean} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - * @param array the array to add to the toString - * @param fullDetail {@code true} for detail, {@code false} for summary info, - * {@code null} for style decides - */ - public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, - final Boolean fullDetail) { - appendFieldStart(buffer, fieldName); - - if (array == null) { - appendNullText(buffer, fieldName); - - } else if (isFullDetail(fullDetail)) { - appendDetail(buffer, fieldName, array); - - } else { - appendSummary(buffer, fieldName, array); - } - - appendFieldEnd(buffer, fieldName); - } - - /** - *

- * Append to the {@code toString} the detail of a {@code boolean} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - */ - protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) { - buffer.append(arrayStart); - for (int i = 0; i < array.length; i++) { - if (i > 0) { - buffer.append(arraySeparator); - } - appendDetail(buffer, fieldName, array[i]); - } - buffer.append(arrayEnd); - } - - /** - *

- * Append to the {@code toString} a summary of a {@code boolean} array. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param array the array to add to the {@code toString}, not {@code null} - */ - protected void appendSummary(final StringBuffer buffer, final String fieldName, final boolean[] array) { - appendSummarySize(buffer, fieldName, array.length); - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Append to the {@code toString} the class name. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param object the {@code Object} whose name to output - */ - protected void appendClassName(final StringBuffer buffer, final Object object) { - if (useClassName && object != null) { - register(object); - if (useShortClassName) { - buffer.append(getShortClassName(object.getClass())); - } else { - buffer.append(object.getClass().getName()); - } - } - } - - /** - *

- * Append the {@link System#identityHashCode(java.lang.Object)}. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param object the {@code Object} whose id to output - */ - protected void appendIdentityHashCode(final StringBuffer buffer, final Object object) { - if (this.isUseIdentityHashCode() && object != null) { - register(object); - buffer.append('@'); - buffer.append(Integer.toHexString(System.identityHashCode(object))); - } - } - - /** - *

- * Append to the {@code toString} the content start. - *

- * - * @param buffer the {@code StringBuffer} to populate - */ - protected void appendContentStart(final StringBuffer buffer) { - buffer.append(contentStart); - } - - /** - *

- * Append to the {@code toString} the content end. - *

- * - * @param buffer the {@code StringBuffer} to populate - */ - protected void appendContentEnd(final StringBuffer buffer) { - buffer.append(contentEnd); - } - - /** - *

- * Append to the {@code toString} an indicator for {@code null}. - *

- * - *

- * The default indicator is {@code '<null>'}. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - */ - protected void appendNullText(final StringBuffer buffer, final String fieldName) { - buffer.append(nullText); - } - - /** - *

- * Append to the {@code toString} the field separator. - *

- * - * @param buffer the {@code StringBuffer} to populate - */ - protected void appendFieldSeparator(final StringBuffer buffer) { - buffer.append(fieldSeparator); - } - - /** - *

- * Append to the {@code toString} the field start. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name - */ - protected void appendFieldStart(final StringBuffer buffer, final String fieldName) { - if (useFieldNames && fieldName != null) { - buffer.append(fieldName); - buffer.append(fieldNameValueSeparator); - } - } - - /** - *

- * Append to the {@code toString} the field end. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - */ - protected void appendFieldEnd(final StringBuffer buffer, final String fieldName) { - appendFieldSeparator(buffer); - } - - /** - *

- * Append to the {@code toString} a size summary. - *

- * - *

- * The size summary is used to summarize the contents of {@code Collections}, - * {@code Maps} and arrays. - *

- * - *

- * The output consists of a prefix, the passed in size and a suffix. - *

- * - *

- * The default format is {@code '<size=n>'}. - *

- * - * @param buffer the {@code StringBuffer} to populate - * @param fieldName the field name, typically not used as already appended - * @param size the size to append - */ - protected void appendSummarySize(final StringBuffer buffer, final String fieldName, final int size) { - buffer.append(sizeStartText); - buffer.append(size); - buffer.append(sizeEndText); - } - - /** - *

- * Is this field to be output in full detail. - *

- * - *

- * This method converts a detail request into a detail level. The calling code - * may request full detail ({@code true}), but a subclass might ignore that and - * always return {@code false}. The calling code may pass in {@code null} - * indicating that it doesn't care about the detail level. In this case the - * default detail level is used. - *

- * - * @param fullDetailRequest the detail level requested - * @return whether full detail is to be shown - */ - protected boolean isFullDetail(final Boolean fullDetailRequest) { - if (fullDetailRequest == null) { - return defaultFullDetail; - } - return fullDetailRequest.booleanValue(); - } - - /** - *

- * Gets the short class name for a class. - *

- * - *

- * The short class name is the classname excluding the package name. - *

- * - * @param cls the {@code Class} to get the short name of - * @return the short name - */ - protected String getShortClassName(final Class cls) { - return cls.getSimpleName(); - } - - // Setters and getters for the customizable parts of the style - // These methods are not expected to be overridden, except to make public - // (They are not public so that immutable subclasses can be written) - // --------------------------------------------------------------------- - - /** - *

- * Gets whether to use the class name. - *

- * - * @return the current useClassName flag - */ - protected boolean isUseClassName() { - return useClassName; - } - - /** - *

- * Sets whether to use the class name. - *

- * - * @param useClassName the new useClassName flag - */ - protected void setUseClassName(final boolean useClassName) { - this.useClassName = useClassName; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets whether to output short or long class names. - *

- * - * @return the current useShortClassName flag - * @since 2.0 - */ - protected boolean isUseShortClassName() { - return useShortClassName; - } - - /** - *

- * Sets whether to output short or long class names. - *

- * - * @param useShortClassName the new useShortClassName flag - * @since 2.0 - */ - protected void setUseShortClassName(final boolean useShortClassName) { - this.useShortClassName = useShortClassName; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets whether to use the identity hash code. - *

- * - * @return the current useIdentityHashCode flag - */ - protected boolean isUseIdentityHashCode() { - return useIdentityHashCode; - } - - /** - *

- * Sets whether to use the identity hash code. - *

- * - * @param useIdentityHashCode the new useIdentityHashCode flag - */ - protected void setUseIdentityHashCode(final boolean useIdentityHashCode) { - this.useIdentityHashCode = useIdentityHashCode; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets whether to use the field names passed in. - *

- * - * @return the current useFieldNames flag - */ - protected boolean isUseFieldNames() { - return useFieldNames; - } - - /** - *

- * Sets whether to use the field names passed in. - *

- * - * @param useFieldNames the new useFieldNames flag - */ - protected void setUseFieldNames(final boolean useFieldNames) { - this.useFieldNames = useFieldNames; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets whether to use full detail when the caller doesn't specify. - *

- * - * @return the current defaultFullDetail flag - */ - protected boolean isDefaultFullDetail() { - return defaultFullDetail; - } - - /** - *

- * Sets whether to use full detail when the caller doesn't specify. - *

- * - * @param defaultFullDetail the new defaultFullDetail flag - */ - protected void setDefaultFullDetail(final boolean defaultFullDetail) { - this.defaultFullDetail = defaultFullDetail; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets whether to output array content detail. - *

- * - * @return the current array content detail setting - */ - protected boolean isArrayContentDetail() { - return arrayContentDetail; - } - - /** - *

- * Sets whether to output array content detail. - *

- * - * @param arrayContentDetail the new arrayContentDetail flag - */ - protected void setArrayContentDetail(final boolean arrayContentDetail) { - this.arrayContentDetail = arrayContentDetail; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets the array start text. - *

- * - * @return the current array start text - */ - protected String getArrayStart() { - return arrayStart; - } - - /** - *

- * Sets the array start text. - *

- * - *

- * {@code null} is accepted, but will be converted to an empty String. - *

- * - * @param arrayStart the new array start text - */ - protected void setArrayStart(String arrayStart) { - if (arrayStart == null) { - arrayStart = StringUtils.EMPTY; - } - this.arrayStart = arrayStart; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets the array end text. - *

- * - * @return the current array end text - */ - protected String getArrayEnd() { - return arrayEnd; - } - - /** - *

- * Sets the array end text. - *

- * - *

- * {@code null} is accepted, but will be converted to an empty String. - *

- * - * @param arrayEnd the new array end text - */ - protected void setArrayEnd(String arrayEnd) { - if (arrayEnd == null) { - arrayEnd = StringUtils.EMPTY; - } - this.arrayEnd = arrayEnd; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets the array separator text. - *

- * - * @return the current array separator text - */ - protected String getArraySeparator() { - return arraySeparator; - } - - /** - *

- * Sets the array separator text. - *

- * - *

- * {@code null} is accepted, but will be converted to an empty String. - *

- * - * @param arraySeparator the new array separator text - */ - protected void setArraySeparator(String arraySeparator) { - if (arraySeparator == null) { - arraySeparator = StringUtils.EMPTY; - } - this.arraySeparator = arraySeparator; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets the content start text. - *

- * - * @return the current content start text - */ - protected String getContentStart() { - return contentStart; - } - - /** - *

- * Sets the content start text. - *

- * - *

- * {@code null} is accepted, but will be converted to an empty String. - *

- * - * @param contentStart the new content start text - */ - protected void setContentStart(String contentStart) { - if (contentStart == null) { - contentStart = StringUtils.EMPTY; - } - this.contentStart = contentStart; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets the content end text. - *

- * - * @return the current content end text - */ - protected String getContentEnd() { - return contentEnd; - } - - /** - *

- * Sets the content end text. - *

- * - *

- * {@code null} is accepted, but will be converted to an empty String. - *

- * - * @param contentEnd the new content end text - */ - protected void setContentEnd(String contentEnd) { - if (contentEnd == null) { - contentEnd = StringUtils.EMPTY; - } - this.contentEnd = contentEnd; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets the field name value separator text. - *

- * - * @return the current field name value separator text - */ - protected String getFieldNameValueSeparator() { - return fieldNameValueSeparator; - } - - /** - *

- * Sets the field name value separator text. - *

- * - *

- * {@code null} is accepted, but will be converted to an empty String. - *

- * - * @param fieldNameValueSeparator the new field name value separator text - */ - protected void setFieldNameValueSeparator(String fieldNameValueSeparator) { - if (fieldNameValueSeparator == null) { - fieldNameValueSeparator = StringUtils.EMPTY; - } - this.fieldNameValueSeparator = fieldNameValueSeparator; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets the field separator text. - *

- * - * @return the current field separator text - */ - protected String getFieldSeparator() { - return fieldSeparator; - } - - /** - *

- * Sets the field separator text. - *

- * - *

- * {@code null} is accepted, but will be converted to an empty String. - *

- * - * @param fieldSeparator the new field separator text - */ - protected void setFieldSeparator(String fieldSeparator) { - if (fieldSeparator == null) { - fieldSeparator = StringUtils.EMPTY; - } - this.fieldSeparator = fieldSeparator; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets whether the field separator should be added at the start of each buffer. - *

- * - * @return the fieldSeparatorAtStart flag - * @since 2.0 - */ - protected boolean isFieldSeparatorAtStart() { - return fieldSeparatorAtStart; - } - - /** - *

- * Sets whether the field separator should be added at the start of each buffer. - *

- * - * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag - * @since 2.0 - */ - protected void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) { - this.fieldSeparatorAtStart = fieldSeparatorAtStart; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets whether the field separator should be added at the end of each buffer. - *

- * - * @return fieldSeparatorAtEnd flag - * @since 2.0 - */ - protected boolean isFieldSeparatorAtEnd() { - return fieldSeparatorAtEnd; - } - - /** - *

- * Sets whether the field separator should be added at the end of each buffer. - *

- * - * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag - * @since 2.0 - */ - protected void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) { - this.fieldSeparatorAtEnd = fieldSeparatorAtEnd; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets the text to output when {@code null} found. - *

- * - * @return the current text to output when null found - */ - protected String getNullText() { - return nullText; - } - - /** - *

- * Sets the text to output when {@code null} found. - *

- * - *

- * {@code null} is accepted, but will be converted to an empty String. - *

- * - * @param nullText the new text to output when null found - */ - protected void setNullText(String nullText) { - if (nullText == null) { - nullText = StringUtils.EMPTY; - } - this.nullText = nullText; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets the start text to output when a {@code Collection}, {@code Map} or array - * size is output. - *

- * - *

- * This is output before the size value. - *

- * - * @return the current start of size text - */ - protected String getSizeStartText() { - return sizeStartText; - } - - /** - *

- * Sets the start text to output when a {@code Collection}, {@code Map} or array - * size is output. - *

- * - *

- * This is output before the size value. - *

- * - *

- * {@code null} is accepted, but will be converted to an empty String. - *

- * - * @param sizeStartText the new start of size text - */ - protected void setSizeStartText(String sizeStartText) { - if (sizeStartText == null) { - sizeStartText = StringUtils.EMPTY; - } - this.sizeStartText = sizeStartText; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets the end text to output when a {@code Collection}, {@code Map} or array - * size is output. - *

- * - *

- * This is output after the size value. - *

- * - * @return the current end of size text - */ - protected String getSizeEndText() { - return sizeEndText; - } - - /** - *

- * Sets the end text to output when a {@code Collection}, {@code Map} or array - * size is output. - *

- * - *

- * This is output after the size value. - *

- * - *

- * {@code null} is accepted, but will be converted to an empty String. - *

- * - * @param sizeEndText the new end of size text - */ - protected void setSizeEndText(String sizeEndText) { - if (sizeEndText == null) { - sizeEndText = StringUtils.EMPTY; - } - this.sizeEndText = sizeEndText; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets the start text to output when an {@code Object} is output in summary - * mode. - *

- * - *

- * This is output before the size value. - *

- * - * @return the current start of summary text - */ - protected String getSummaryObjectStartText() { - return summaryObjectStartText; - } - - /** - *

- * Sets the start text to output when an {@code Object} is output in summary - * mode. - *

- * - *

- * This is output before the size value. - *

- * - *

- * {@code null} is accepted, but will be converted to an empty String. - *

- * - * @param summaryObjectStartText the new start of summary text - */ - protected void setSummaryObjectStartText(String summaryObjectStartText) { - if (summaryObjectStartText == null) { - summaryObjectStartText = StringUtils.EMPTY; - } - this.summaryObjectStartText = summaryObjectStartText; - } - - // --------------------------------------------------------------------- - - /** - *

- * Gets the end text to output when an {@code Object} is output in summary mode. - *

- * - *

- * This is output after the size value. - *

- * - * @return the current end of summary text - */ - protected String getSummaryObjectEndText() { - return summaryObjectEndText; - } - - /** - *

- * Sets the end text to output when an {@code Object} is output in summary mode. - *

- * - *

- * This is output after the size value. - *

- * - *

- * {@code null} is accepted, but will be converted to an empty String. - *

- * - * @param summaryObjectEndText the new end of summary text - */ - protected void setSummaryObjectEndText(String summaryObjectEndText) { - if (summaryObjectEndText == null) { - summaryObjectEndText = StringUtils.EMPTY; - } - this.summaryObjectEndText = summaryObjectEndText; - } - - // ---------------------------------------------------------------------------- - - /** - *

- * Default {@code ToStringStyle}. - *

- * - *

- * This is an inner class rather than using {@code StandardToStringStyle} to - * ensure its immutability. - *

- */ - private static final class DefaultToStringStyle extends ToStringStyle { - - /** - * Required for serialization support. - * - * @see java.io.Serializable - */ - private static final long serialVersionUID = 1L; - - /** - *

- * Constructor. - *

- * - *

- * Use the static constant rather than instantiating. - *

- */ - DefaultToStringStyle() { - } - - /** - *

- * Ensure {@code Singleton} after serialization. - *

- * - * @return the singleton - */ - private Object readResolve() { - return DEFAULT_STYLE; - } - - } - - // ---------------------------------------------------------------------------- - - /** - *

- * {@code ToStringStyle} that does not print out the field names. - *

- * - *

- * This is an inner class rather than using {@code StandardToStringStyle} to - * ensure its immutability. - */ - private static final class NoFieldNameToStringStyle extends ToStringStyle { - - private static final long serialVersionUID = 1L; - - /** - *

- * Constructor. - *

- * - *

- * Use the static constant rather than instantiating. - *

- */ - NoFieldNameToStringStyle() { - this.setUseFieldNames(false); - } - - /** - *

- * Ensure {@code Singleton} after serialization. - *

- * - * @return the singleton - */ - private Object readResolve() { - return NO_FIELD_NAMES_STYLE; - } - - } - - // ---------------------------------------------------------------------------- - - /** - *

- * {@code ToStringStyle} that prints out the short class name and no identity - * hashcode. - *

- * - *

- * This is an inner class rather than using {@code StandardToStringStyle} to - * ensure its immutability. - *

- */ - private static final class ShortPrefixToStringStyle extends ToStringStyle { - - private static final long serialVersionUID = 1L; - - /** - *

- * Constructor. - *

- * - *

- * Use the static constant rather than instantiating. - *

- */ - ShortPrefixToStringStyle() { - this.setUseShortClassName(true); - this.setUseIdentityHashCode(false); - } - - /** - *

- * Ensure Singleton after serialization. - *

- * - * @return the singleton - */ - private Object readResolve() { - return SHORT_PREFIX_STYLE; - } - - } - - // ---------------------------------------------------------------------------- - - /** - *

- * {@code ToStringStyle} that does not print out the classname, identity - * hashcode, content start or field name. - *

- * - *

- * This is an inner class rather than using {@code StandardToStringStyle} to - * ensure its immutability. - *

- */ - private static final class SimpleToStringStyle extends ToStringStyle { - - private static final long serialVersionUID = 1L; - - /** - *

- * Constructor. - *

- * - *

- * Use the static constant rather than instantiating. - *

- */ - SimpleToStringStyle() { - this.setUseClassName(false); - this.setUseIdentityHashCode(false); - this.setUseFieldNames(false); - this.setContentStart(StringUtils.EMPTY); - this.setContentEnd(StringUtils.EMPTY); - } - - /** - *

- * Ensure Singleton after serialization. - *

- * - * @return the singleton - */ - private Object readResolve() { - return SIMPLE_STYLE; - } - - } - - // ---------------------------------------------------------------------------- - - /** - *

- * {@code ToStringStyle} that outputs on multiple lines. - *

- * - *

- * This is an inner class rather than using {@code StandardToStringStyle} to - * ensure its immutability. - *

- */ - private static final class MultiLineToStringStyle extends ToStringStyle { - - private static final long serialVersionUID = 1L; - - /** - *

- * Constructor. - *

- * - *

- * Use the static constant rather than instantiating. - *

- */ - MultiLineToStringStyle() { - this.setContentStart("["); - this.setFieldSeparator(System.lineSeparator() + " "); - this.setFieldSeparatorAtStart(true); - this.setContentEnd(System.lineSeparator() + "]"); - } - - /** - *

- * Ensure {@code Singleton} after serialization. - *

- * - * @return the singleton - */ - private Object readResolve() { - return MULTI_LINE_STYLE; - } - - } - - // ---------------------------------------------------------------------------- - - /** - *

- * {@code ToStringStyle} that does not print out the classname and identity hash - * code but prints content start and field names. - *

- * - *

- * This is an inner class rather than using {@code StandardToStringStyle} to - * ensure its immutability. - *

- */ - private static final class NoClassNameToStringStyle extends ToStringStyle { - - private static final long serialVersionUID = 1L; - - /** - *

- * Constructor. - *

- * - *

- * Use the static constant rather than instantiating. - *

- */ - NoClassNameToStringStyle() { - this.setUseClassName(false); - this.setUseIdentityHashCode(false); - } - - /** - *

- * Ensure {@code Singleton} after serialization. - *

- * - * @return the singleton - */ - private Object readResolve() { - return NO_CLASS_NAME_STYLE; - } - - } - - // ---------------------------------------------------------------------------- - - /** - *

- * {@code ToStringStyle} that outputs with JSON format. - *

- * - *

- * This is an inner class rather than using {@code StandardToStringStyle} to - * ensure its immutability. - *

- * - * @since 3.4 - * @see json.org - */ - private static final class JsonToStringStyle extends ToStringStyle { - - private static final long serialVersionUID = 1L; - - private static final String FIELD_NAME_QUOTE = "\""; - - /** - *

- * Constructor. - *

- * - *

- * Use the static constant rather than instantiating. - *

- */ - JsonToStringStyle() { - this.setUseClassName(false); - this.setUseIdentityHashCode(false); - - this.setContentStart("{"); - this.setContentEnd("}"); - - this.setArrayStart("["); - this.setArrayEnd("]"); - - this.setFieldSeparator(","); - this.setFieldNameValueSeparator(":"); - - this.setNullText("null"); - - this.setSummaryObjectStartText("\"<"); - this.setSummaryObjectEndText(">\""); - - this.setSizeStartText("\"\""); - } - - @Override - public void append(final StringBuffer buffer, final String fieldName, final Object[] array, - final Boolean fullDetail) { - - if (fieldName == null) { - throw new UnsupportedOperationException("Field names are mandatory when using JsonToStringStyle"); - } - if (!isFullDetail(fullDetail)) { - throw new UnsupportedOperationException("FullDetail must be true when using JsonToStringStyle"); - } - - super.append(buffer, fieldName, array, fullDetail); - } - - @Override - public void append(final StringBuffer buffer, final String fieldName, final long[] array, - final Boolean fullDetail) { - - if (fieldName == null) { - throw new UnsupportedOperationException("Field names are mandatory when using JsonToStringStyle"); - } - if (!isFullDetail(fullDetail)) { - throw new UnsupportedOperationException("FullDetail must be true when using JsonToStringStyle"); - } - - super.append(buffer, fieldName, array, fullDetail); - } - - @Override - public void append(final StringBuffer buffer, final String fieldName, final int[] array, - final Boolean fullDetail) { - - if (fieldName == null) { - throw new UnsupportedOperationException("Field names are mandatory when using JsonToStringStyle"); - } - if (!isFullDetail(fullDetail)) { - throw new UnsupportedOperationException("FullDetail must be true when using JsonToStringStyle"); - } - - super.append(buffer, fieldName, array, fullDetail); - } - - @Override - public void append(final StringBuffer buffer, final String fieldName, final short[] array, - final Boolean fullDetail) { - - if (fieldName == null) { - throw new UnsupportedOperationException("Field names are mandatory when using JsonToStringStyle"); - } - if (!isFullDetail(fullDetail)) { - throw new UnsupportedOperationException("FullDetail must be true when using JsonToStringStyle"); - } - - super.append(buffer, fieldName, array, fullDetail); - } - - @Override - public void append(final StringBuffer buffer, final String fieldName, final byte[] array, - final Boolean fullDetail) { - - if (fieldName == null) { - throw new UnsupportedOperationException("Field names are mandatory when using JsonToStringStyle"); - } - if (!isFullDetail(fullDetail)) { - throw new UnsupportedOperationException("FullDetail must be true when using JsonToStringStyle"); - } - - super.append(buffer, fieldName, array, fullDetail); - } - - @Override - public void append(final StringBuffer buffer, final String fieldName, final char[] array, - final Boolean fullDetail) { - - if (fieldName == null) { - throw new UnsupportedOperationException("Field names are mandatory when using JsonToStringStyle"); - } - if (!isFullDetail(fullDetail)) { - throw new UnsupportedOperationException("FullDetail must be true when using JsonToStringStyle"); - } - - super.append(buffer, fieldName, array, fullDetail); - } - - @Override - public void append(final StringBuffer buffer, final String fieldName, final double[] array, - final Boolean fullDetail) { - - if (fieldName == null) { - throw new UnsupportedOperationException("Field names are mandatory when using JsonToStringStyle"); - } - if (!isFullDetail(fullDetail)) { - throw new UnsupportedOperationException("FullDetail must be true when using JsonToStringStyle"); - } - - super.append(buffer, fieldName, array, fullDetail); - } - - @Override - public void append(final StringBuffer buffer, final String fieldName, final float[] array, - final Boolean fullDetail) { - - if (fieldName == null) { - throw new UnsupportedOperationException("Field names are mandatory when using JsonToStringStyle"); - } - if (!isFullDetail(fullDetail)) { - throw new UnsupportedOperationException("FullDetail must be true when using JsonToStringStyle"); - } - - super.append(buffer, fieldName, array, fullDetail); - } - - @Override - public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, - final Boolean fullDetail) { - - if (fieldName == null) { - throw new UnsupportedOperationException("Field names are mandatory when using JsonToStringStyle"); - } - if (!isFullDetail(fullDetail)) { - throw new UnsupportedOperationException("FullDetail must be true when using JsonToStringStyle"); - } - - super.append(buffer, fieldName, array, fullDetail); - } - - @Override - public void append(final StringBuffer buffer, final String fieldName, final Object value, - final Boolean fullDetail) { - - if (fieldName == null) { - throw new UnsupportedOperationException("Field names are mandatory when using JsonToStringStyle"); - } - if (!isFullDetail(fullDetail)) { - throw new UnsupportedOperationException("FullDetail must be true when using JsonToStringStyle"); - } - - super.append(buffer, fieldName, value, fullDetail); - } - - @Override - protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) { - appendValueAsString(buffer, String.valueOf(value)); - } - - @Override - protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) { - - if (value == null) { - appendNullText(buffer, fieldName); - return; - } - - if (value instanceof String || value instanceof Character) { - appendValueAsString(buffer, value.toString()); - return; - } - - if (value instanceof Number || value instanceof Boolean) { - buffer.append(value); - return; - } - - final String valueAsString = value.toString(); - if (isJsonObject(valueAsString) || isJsonArray(valueAsString)) { - buffer.append(value); - return; - } - - appendDetail(buffer, fieldName, valueAsString); - } - - @Override - protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection coll) { - if (coll != null && !coll.isEmpty()) { - buffer.append(getArrayStart()); - int i = 0; - for (final Object item : coll) { - appendDetail(buffer, fieldName, i++, item); - } - buffer.append(getArrayEnd()); - return; - } - - buffer.append(coll); - } - - @Override - protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map map) { - if (map != null && !map.isEmpty()) { - buffer.append(getContentStart()); - - boolean firstItem = true; - for (final Entry entry : map.entrySet()) { - final String keyStr = Objects.toString(entry.getKey(), null); - if (keyStr != null) { - if (firstItem) { - firstItem = false; - } else { - appendFieldEnd(buffer, keyStr); - } - appendFieldStart(buffer, keyStr); - final Object value = entry.getValue(); - if (value == null) { - appendNullText(buffer, keyStr); - } else { - appendInternal(buffer, keyStr, value, true); - } - } - } - - buffer.append(getContentEnd()); - return; - } - - buffer.append(map); - } - - private boolean isJsonArray(final String valueAsString) { - return valueAsString.startsWith(getArrayStart()) && valueAsString.endsWith(getArrayEnd()); - } - - private boolean isJsonObject(final String valueAsString) { - return valueAsString.startsWith(getContentStart()) && valueAsString.endsWith(getContentEnd()); - } - - /** - * Appends the given String enclosed in double-quotes to the given StringBuffer. - * - * @param buffer the StringBuffer to append the value to. - * @param value the value to append. - */ - private void appendValueAsString(final StringBuffer buffer, final String value) { - // buffer.append('"').append(StringEscapeUtils.escapeJson(value)).append('"'); - throw new UnsupportedOperationException("Not supported in TeaVM"); - } - - @Override - protected void appendFieldStart(final StringBuffer buffer, final String fieldName) { - throw new UnsupportedOperationException("Not supported in TeaVM"); - /* - * if (fieldName == null) { throw new UnsupportedOperationException( - * "Field names are mandatory when using JsonToStringStyle"); } - * - * super.appendFieldStart(buffer, FIELD_NAME_QUOTE + - * StringEscapeUtils.escapeJson(fieldName) + FIELD_NAME_QUOTE); - */ - } - - /** - *

- * Ensure {@code Singleton} after serialization. - *

- * - * @return the singleton - */ - private Object readResolve() { - return JSON_STYLE; - } - - } -} diff --git a/src/main/java/org/apache/commons/lang3/builder/ToStringSummary.java b/src/main/java/org/apache/commons/lang3/builder/ToStringSummary.java deleted file mode 100755 index 811d7916..00000000 --- a/src/main/java/org/apache/commons/lang3/builder/ToStringSummary.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.builder; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Use this annotation on the fields to get the summary instead of the detailed - * information when using {@link ReflectionToStringBuilder}. - * - *

- * Notice that not all {@link ToStringStyle} implementations support the - * appendSummary method. - *

- * - * @since 3.8 - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.FIELD) -public @interface ToStringSummary { - // empty -} diff --git a/src/main/java/org/apache/commons/lang3/compare/ComparableUtils.java b/src/main/java/org/apache/commons/lang3/compare/ComparableUtils.java deleted file mode 100755 index 12a2afc9..00000000 --- a/src/main/java/org/apache/commons/lang3/compare/ComparableUtils.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.compare; - -import java.util.function.Predicate; - -/** - *

- * Utility library to provide helper methods for translating - * {@link Comparable#compareTo} result into a boolean. - *

- * - *

- * Example: - * {@code boolean x = is(myComparable).lessThanOrEqualTo(otherComparable)} - *

- * - *

- * #ThreadSafe# - *

- * - * @since 3.10 - */ -public class ComparableUtils { - - /** - * Provides access to the available methods - * - * @param the type of objects that this object may be compared against. - */ - public static class ComparableCheckBuilder> { - - private final A a; - - private ComparableCheckBuilder(final A a) { - this.a = a; - } - - /** - * Checks if {@code [b <= a <= c]} or {@code [b >= a >= c]} where the {@code a} - * is object passed to {@link #is}. - * - * @param b the object to compare to the base object - * @param c the object to compare to the base object - * @return true if the base object is between b and c - */ - public boolean between(final A b, final A c) { - return betweenOrdered(b, c) || betweenOrdered(c, b); - } - - /** - * Checks if {@code (b < a < c)} or {@code (b > a > c)} where the {@code a} is - * object passed to {@link #is}. - * - * @param b the object to compare to the base object - * @param c the object to compare to the base object - * @return true if the base object is between b and c and not equal to those - */ - public boolean betweenExclusive(final A b, final A c) { - return betweenOrderedExclusive(b, c) || betweenOrderedExclusive(c, b); - } - - private boolean betweenOrdered(final A b, final A c) { - return greaterThanOrEqualTo(b) && lessThanOrEqualTo(c); - } - - private boolean betweenOrderedExclusive(final A b, final A c) { - return greaterThan(b) && lessThan(c); - } - - /** - * Checks if the object passed to {@link #is} is equal to {@code b} - * - * @param b the object to compare to the base object - * @return true if the value returned by {@link Comparable#compareTo} is equal - * to {@code 0} - */ - public boolean equalTo(final A b) { - return a.compareTo(b) == 0; - } - - /** - * Checks if the object passed to {@link #is} is greater than {@code b} - * - * @param b the object to compare to the base object - * @return true if the value returned by {@link Comparable#compareTo} is greater - * than {@code 0} - */ - public boolean greaterThan(final A b) { - return a.compareTo(b) > 0; - } - - /** - * Checks if the object passed to {@link #is} is greater than or equal to - * {@code b} - * - * @param b the object to compare to the base object - * @return true if the value returned by {@link Comparable#compareTo} is greater - * than or equal to {@code 0} - */ - public boolean greaterThanOrEqualTo(final A b) { - return a.compareTo(b) >= 0; - } - - /** - * Checks if the object passed to {@link #is} is less than {@code b} - * - * @param b the object to compare to the base object - * @return true if the value returned by {@link Comparable#compareTo} is less - * than {@code 0} - */ - public boolean lessThan(final A b) { - return a.compareTo(b) < 0; - } - - /** - * Checks if the object passed to {@link #is} is less than or equal to {@code b} - * - * @param b the object to compare to the base object - * @return true if the value returned by {@link Comparable#compareTo} is less - * than or equal to {@code 0} - */ - public boolean lessThanOrEqualTo(final A b) { - return a.compareTo(b) <= 0; - } - } - - /** - * Checks if {@code [b <= a <= c]} or {@code [b >= a >= c]} where the {@code a} - * is the tested object. - * - * @param b the object to compare to the tested object - * @param c the object to compare to the tested object - * @param type of the test object - * @return a predicate for true if the tested object is between b and c - */ - public static > Predicate between(final A b, final A c) { - return a -> is(a).between(b, c); - } - - /** - * Checks if {@code (b < a < c)} or {@code (b > a > c)} where the {@code a} is - * the tested object. - * - * @param b the object to compare to the tested object - * @param c the object to compare to the tested object - * @param type of the test object - * @return a predicate for true if the tested object is between b and c and not - * equal to those - */ - public static > Predicate betweenExclusive(final A b, final A c) { - return a -> is(a).betweenExclusive(b, c); - } - - /** - * Checks if the tested object is greater than or equal to {@code b} - * - * @param b the object to compare to the tested object - * @param type of the test object - * @return a predicate for true if the value returned by - * {@link Comparable#compareTo} is greater than or equal to {@code 0} - */ - public static > Predicate ge(final A b) { - return a -> is(a).greaterThanOrEqualTo(b); - } - - /** - * Checks if the tested object is greater than {@code b} - * - * @param b the object to compare to the tested object - * @param type of the test object - * @return a predicate for true if the value returned by - * {@link Comparable#compareTo} is greater than {@code 0} - */ - public static > Predicate gt(final A b) { - return a -> is(a).greaterThan(b); - } - - /** - * Provides access to the available methods - * - * @param a base object in the further comparison - * @param type of the base object - * @return a builder object with further methods - */ - public static > ComparableCheckBuilder is(final A a) { - return new ComparableCheckBuilder<>(a); - } - - /** - * Checks if the tested object is less than or equal to {@code b} - * - * @param b the object to compare to the tested object - * @param type of the test object - * @return a predicate for true if the value returned by - * {@link Comparable#compareTo} is less than or equal to {@code 0} - */ - public static > Predicate le(final A b) { - return a -> is(a).lessThanOrEqualTo(b); - } - - /** - * Checks if the tested object is less than {@code b} - * - * @param b the object to compare to the tested object - * @param type of the test object - * @return a predicate for true if the value returned by - * {@link Comparable#compareTo} is less than {@code 0} - */ - public static > Predicate lt(final A b) { - return a -> is(a).lessThan(b); - } - - private ComparableUtils() { - } -} diff --git a/src/main/java/org/apache/commons/lang3/compare/ObjectToStringComparator.java b/src/main/java/org/apache/commons/lang3/compare/ObjectToStringComparator.java deleted file mode 100755 index 348c6265..00000000 --- a/src/main/java/org/apache/commons/lang3/compare/ObjectToStringComparator.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.compare; - -import java.io.Serializable; -import java.util.Comparator; - -/** - * Compares Object's {@link Object#toString()} values. - * - * This class is stateless. - * - * @since 3.10 - */ -public final class ObjectToStringComparator implements Comparator, Serializable { - - /** - * Singleton instance. - * - * This class is stateless. - */ - public static final ObjectToStringComparator INSTANCE = new ObjectToStringComparator(); - - /** - * For {@link Serializable}. - */ - private static final long serialVersionUID = 1L; - - @Override - public int compare(final Object o1, final Object o2) { - if (o1 == null && o2 == null) { - return 0; - } - if (o1 == null) { - return 1; - } - if (o2 == null) { - return -1; - } - final String string1 = o1.toString(); - final String string2 = o2.toString(); - // No guarantee that toString() returns a non-null value, despite what Spotbugs - // thinks. - if (string1 == null && string2 == null) { - return 0; - } - if (string1 == null) { - return 1; - } - if (string2 == null) { - return -1; - } - return string1.compareTo(string2); - } -} diff --git a/src/main/java/org/apache/commons/lang3/compare/package-info.java b/src/main/java/org/apache/commons/lang3/compare/package-info.java deleted file mode 100755 index 30eeca44..00000000 --- a/src/main/java/org/apache/commons/lang3/compare/package-info.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Provides classes to work with the {@link java.lang.Comparable} and - * {@link java.util.Comparator} interfaces. - * - * @since 3.10 - */ -package org.apache.commons.lang3.compare; diff --git a/src/main/java/org/apache/commons/lang3/exception/CloneFailedException.java b/src/main/java/org/apache/commons/lang3/exception/CloneFailedException.java deleted file mode 100755 index bcb63a7a..00000000 --- a/src/main/java/org/apache/commons/lang3/exception/CloneFailedException.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.exception; - -/** - * Exception thrown when a clone cannot be created. In contrast to - * {@link CloneNotSupportedException} this is a {@link RuntimeException}. - * - * @since 3.0 - */ -public class CloneFailedException extends RuntimeException { - // ~ Static fields/initializers --------------------------------------------- - - private static final long serialVersionUID = 20091223L; - - // ~ Constructors ----------------------------------------------------------- - - /** - * Constructs a CloneFailedException. - * - * @param message description of the exception - */ - public CloneFailedException(final String message) { - super(message); - } - - /** - * Constructs a CloneFailedException. - * - * @param cause cause of the exception - */ - public CloneFailedException(final Throwable cause) { - super(cause); - } - - /** - * Constructs a CloneFailedException. - * - * @param message description of the exception - * @param cause cause of the exception - */ - public CloneFailedException(final String message, final Throwable cause) { - super(message, cause); - } -} diff --git a/src/main/java/org/apache/commons/lang3/exception/package-info.java b/src/main/java/org/apache/commons/lang3/exception/package-info.java deleted file mode 100755 index e9ab2939..00000000 --- a/src/main/java/org/apache/commons/lang3/exception/package-info.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - *

- * Provides functionality for Exceptions. - *

- *

- * Contains the concept of an exception with context i.e. such an exception will - * contain a map with keys and values. This provides an easy way to pass - * valuable state information at exception time in useful form to a calling - * process. - *

- *

- * Lastly, {@link org.apache.commons.lang3.exception.ExceptionUtils} also - * contains {@code Throwable} manipulation and examination routines. - *

- * - * @since 1.0 - */ -package org.apache.commons.lang3.exception; diff --git a/src/main/java/org/apache/commons/lang3/function/Failable.java b/src/main/java/org/apache/commons/lang3/function/Failable.java deleted file mode 100755 index e2329710..00000000 --- a/src/main/java/org/apache/commons/lang3/function/Failable.java +++ /dev/null @@ -1,608 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.lang.reflect.UndeclaredThrowableException; -import java.util.Collection; -import java.util.Objects; -import java.util.concurrent.Callable; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.BiPredicate; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.stream.Stream; - -import org.apache.commons.lang3.stream.Streams.FailableStream; - -/** - * This class provides utility functions, and classes for working with the - * {@code java.util.function} package, or more generally, with Java 8 lambdas. - * More specifically, it attempts to address the fact that lambdas are supposed - * not to throw Exceptions, at least not checked Exceptions, AKA instances of - * {@link Exception}. This enforces the use of constructs like: - * - *
- * Consumer<java.lang.reflect.Method-> consumer = m -> {
- *     try {
- *         m.invoke(o, args);
- *     } catch (Throwable t) {
- *         throw Failable.rethrow(t);
- *     }
- * };
- * 
- * - *

- * By replacing a {@link java.util.function.Consumer Consumer<O>} with a - * {@link FailableConsumer FailableConsumer<O,? extends Throwable>}, this - * can be written like follows: - *

- * - *
- * Functions.accept((m) -> m.invoke(o, args));
- * 
- * - *

- * Obviously, the second version is much more concise and the spirit of Lambda - * expressions is met better than the second version. - *

- * - * @since 3.11 - */ -public class Failable { - - /** - * Consumes a consumer and rethrows any exception as a {@link RuntimeException}. - * - * @param consumer the consumer to consume - * @param object1 the first object to consume by {@code consumer} - * @param object2 the second object to consume by {@code consumer} - * @param the type of the first argument the consumer accepts - * @param the type of the second argument the consumer accepts - * @param the type of checked exception the consumer may throw - */ - public static void accept(final FailableBiConsumer consumer, final T object1, - final U object2) { - run(() -> consumer.accept(object1, object2)); - } - - /** - * Consumes a consumer and rethrows any exception as a {@link RuntimeException}. - * - * @param consumer the consumer to consume - * @param object the object to consume by {@code consumer} - * @param the type the consumer accepts - * @param the type of checked exception the consumer may throw - */ - public static void accept(final FailableConsumer consumer, final T object) { - run(() -> consumer.accept(object)); - } - - /** - * Consumes a consumer and rethrows any exception as a {@link RuntimeException}. - * - * @param consumer the consumer to consume - * @param value the value to consume by {@code consumer} - * @param the type of checked exception the consumer may throw - */ - public static void accept(final FailableDoubleConsumer consumer, final double value) { - run(() -> consumer.accept(value)); - } - - /** - * Consumes a consumer and rethrows any exception as a {@link RuntimeException}. - * - * @param consumer the consumer to consume - * @param value the value to consume by {@code consumer} - * @param the type of checked exception the consumer may throw - */ - public static void accept(final FailableIntConsumer consumer, final int value) { - run(() -> consumer.accept(value)); - } - - /** - * Consumes a consumer and rethrows any exception as a {@link RuntimeException}. - * - * @param consumer the consumer to consume - * @param value the value to consume by {@code consumer} - * @param the type of checked exception the consumer may throw - */ - public static void accept(final FailableLongConsumer consumer, final long value) { - run(() -> consumer.accept(value)); - } - - /** - * Applies a function and rethrows any exception as a {@link RuntimeException}. - * - * @param function the function to apply - * @param input1 the first input to apply {@code function} on - * @param input2 the second input to apply {@code function} on - * @param the type of the first argument the function accepts - * @param the type of the second argument the function accepts - * @param the return type of the function - * @param the type of checked exception the function may throw - * @return the value returned from the function - */ - public static R apply(final FailableBiFunction function, final T input1, - final U input2) { - return get(() -> function.apply(input1, input2)); - } - - /** - * Applies a function and rethrows any exception as a {@link RuntimeException}. - * - * @param function the function to apply - * @param input the input to apply {@code function} on - * @param the type of the argument the function accepts - * @param the return type of the function - * @param the type of checked exception the function may throw - * @return the value returned from the function - */ - public static R apply(final FailableFunction function, final T input) { - return get(() -> function.apply(input)); - } - - /** - * Applies a function and rethrows any exception as a {@link RuntimeException}. - * - * @param function the function to apply - * @param left the first input to apply {@code function} on - * @param right the second input to apply {@code function} on - * @param the type of checked exception the function may throw - * @return the value returned from the function - */ - public static double applyAsDouble(final FailableDoubleBinaryOperator function, - final double left, final double right) { - return getAsDouble(() -> function.applyAsDouble(left, right)); - } - - /** - * Converts the given {@link FailableBiConsumer} into a standard - * {@link BiConsumer}. - * - * @param the type of the first argument of the consumers - * @param the type of the second argument of the consumers - * @param consumer a failable {@code BiConsumer} - * @return a standard {@code BiConsumer} - */ - public static BiConsumer asBiConsumer(final FailableBiConsumer consumer) { - return (input1, input2) -> accept(consumer, input1, input2); - } - - /** - * Converts the given {@link FailableBiFunction} into a standard - * {@link BiFunction}. - * - * @param the type of the first argument of the input of the functions - * @param the type of the second argument of the input of the functions - * @param the type of the output of the functions - * @param function a {@code FailableBiFunction} - * @return a standard {@code BiFunction} - */ - public static BiFunction asBiFunction(final FailableBiFunction function) { - return (input1, input2) -> apply(function, input1, input2); - } - - /** - * Converts the given {@link FailableBiPredicate} into a standard - * {@link BiPredicate}. - * - * @param the type of the first argument used by the predicates - * @param the type of the second argument used by the predicates - * @param predicate a {@code FailableBiPredicate} - * @return a standard {@code BiPredicate} - */ - public static BiPredicate asBiPredicate(final FailableBiPredicate predicate) { - return (input1, input2) -> test(predicate, input1, input2); - } - - /** - * Converts the given {@link FailableCallable} into a standard {@link Callable}. - * - * @param the type used by the callables - * @param callable a {@code FailableCallable} - * @return a standard {@code Callable} - */ - public static Callable asCallable(final FailableCallable callable) { - return () -> call(callable); - } - - /** - * Converts the given {@link FailableConsumer} into a standard {@link Consumer}. - * - * @param the type used by the consumers - * @param consumer a {@code FailableConsumer} - * @return a standard {@code Consumer} - */ - public static Consumer asConsumer(final FailableConsumer consumer) { - return input -> accept(consumer, input); - } - - /** - * Converts the given {@link FailableFunction} into a standard {@link Function}. - * - * @param the type of the input of the functions - * @param the type of the output of the functions - * @param function a {code FailableFunction} - * @return a standard {@code Function} - */ - public static Function asFunction(final FailableFunction function) { - return input -> apply(function, input); - } - - /** - * Converts the given {@link FailablePredicate} into a standard - * {@link Predicate}. - * - * @param the type used by the predicates - * @param predicate a {@code FailablePredicate} - * @return a standard {@code Predicate} - */ - public static Predicate asPredicate(final FailablePredicate predicate) { - return input -> test(predicate, input); - } - - /** - * Converts the given {@link FailableRunnable} into a standard {@link Runnable}. - * - * @param runnable a {@code FailableRunnable} - * @return a standard {@code Runnable} - */ - public static Runnable asRunnable(final FailableRunnable runnable) { - return () -> run(runnable); - } - - /** - * Converts the given {@link FailableSupplier} into a standard {@link Supplier}. - * - * @param the type supplied by the suppliers - * @param supplier a {@code FailableSupplier} - * @return a standard {@code Supplier} - */ - public static Supplier asSupplier(final FailableSupplier supplier) { - return () -> get(supplier); - } - - /** - * Calls a callable and rethrows any exception as a {@link RuntimeException}. - * - * @param callable the callable to call - * @param the return type of the callable - * @param the type of checked exception the callable may throw - * @return the value returned from the callable - */ - public static V call(final FailableCallable callable) { - return get(callable::call); - } - - /** - * Invokes a supplier, and returns the result. - * - * @param supplier The supplier to invoke. - * @param The suppliers output type. - * @param The type of checked exception, which the supplier can throw. - * @return The object, which has been created by the supplier - */ - public static T get(final FailableSupplier supplier) { - try { - return supplier.get(); - } catch (final Throwable t) { - throw rethrow(t); - } - } - - /** - * Invokes a boolean supplier, and returns the result. - * - * @param supplier The boolean supplier to invoke. - * @param The type of checked exception, which the supplier can throw. - * @return The boolean, which has been created by the supplier - */ - public static boolean getAsBoolean(final FailableBooleanSupplier supplier) { - try { - return supplier.getAsBoolean(); - } catch (final Throwable t) { - throw rethrow(t); - } - } - - /** - * Invokes a double supplier, and returns the result. - * - * @param supplier The double supplier to invoke. - * @param The type of checked exception, which the supplier can throw. - * @return The double, which has been created by the supplier - */ - public static double getAsDouble(final FailableDoubleSupplier supplier) { - try { - return supplier.getAsDouble(); - } catch (final Throwable t) { - throw rethrow(t); - } - } - - /** - * Invokes an int supplier, and returns the result. - * - * @param supplier The int supplier to invoke. - * @param The type of checked exception, which the supplier can throw. - * @return The int, which has been created by the supplier - */ - public static int getAsInt(final FailableIntSupplier supplier) { - try { - return supplier.getAsInt(); - } catch (final Throwable t) { - throw rethrow(t); - } - } - - /** - * Invokes a long supplier, and returns the result. - * - * @param supplier The long supplier to invoke. - * @param The type of checked exception, which the supplier can throw. - * @return The long, which has been created by the supplier - */ - public static long getAsLong(final FailableLongSupplier supplier) { - try { - return supplier.getAsLong(); - } catch (final Throwable t) { - throw rethrow(t); - } - } - - /** - * Invokes a short supplier, and returns the result. - * - * @param supplier The short supplier to invoke. - * @param The type of checked exception, which the supplier can throw. - * @return The short, which has been created by the supplier - */ - public static short getAsShort(final FailableShortSupplier supplier) { - try { - return supplier.getAsShort(); - } catch (final Throwable t) { - throw rethrow(t); - } - } - - /** - *

- * Rethrows a {@link Throwable} as an unchecked exception. If the argument is - * already unchecked, namely a {@code RuntimeException} or {@code Error} then - * the argument will be rethrown without modification. If the exception is - * {@code IOException} then it will be wrapped into a - * {@code UncheckedIOException}. In every other cases the exception will be - * wrapped into a {@code - * UndeclaredThrowableException} - *

- * - *

- * Note that there is a declared return type for this method, even though it - * never returns. The reason for that is to support the usual pattern: - *

- * - *
-	 * throw rethrow(myUncheckedException);
-	 * 
- * - *

- * instead of just calling the method. This pattern may help the Java compiler - * to recognize that at that point an exception will be thrown and the code flow - * analysis will not demand otherwise mandatory commands that could follow the - * method call, like a {@code return} statement from a value returning method. - *

- * - * @param throwable The throwable to rethrow ossibly wrapped into an unchecked - * exception - * @return Never returns anything, this method never terminates normally. - */ - public static RuntimeException rethrow(final Throwable throwable) { - Objects.requireNonNull(throwable, "throwable"); - if (throwable instanceof RuntimeException) { - throw (RuntimeException) throwable; - } else if (throwable instanceof Error) { - throw (Error) throwable; - } else if (throwable instanceof IOException) { - throw new UncheckedIOException((IOException) throwable); - } else { - throw new UndeclaredThrowableException(throwable); - } - } - - /** - * Runs a runnable and rethrows any exception as a {@link RuntimeException}. - * - * @param runnable The runnable to run - * @param the type of checked exception the runnable may throw - */ - public static void run(final FailableRunnable runnable) { - try { - runnable.run(); - } catch (final Throwable t) { - throw rethrow(t); - } - } - - /** - * Converts the given collection into a {@link FailableStream}. The - * {@link FailableStream} consists of the collections elements. Shortcut for - * - *
-	 * Functions.stream(collection.stream());
-	 * 
- * - * @param collection The collection, which is being converted into a - * {@link FailableStream}. - * @param The collections element type. (In turn, the result streams - * element type.) - * @return The created {@link FailableStream}. - */ - public static FailableStream stream(final Collection collection) { - return new FailableStream<>(collection.stream()); - } - - /** - * Converts the given stream into a {@link FailableStream}. The - * {@link FailableStream} consists of the same elements, than the input stream. - * However, failable lambdas, like {@link FailablePredicate}, - * {@link FailableFunction}, and {@link FailableConsumer} may be applied, rather - * than {@link Predicate}, {@link Function}, {@link Consumer}, etc. - * - * @param stream The stream, which is being converted into a - * {@link FailableStream}. - * @param The streams element type. - * @return The created {@link FailableStream}. - */ - public static FailableStream stream(final Stream stream) { - return new FailableStream<>(stream); - } - - /** - * Tests a predicate and rethrows any exception as a {@link RuntimeException}. - * - * @param predicate the predicate to test - * @param object1 the first input to test by {@code predicate} - * @param object2 the second input to test by {@code predicate} - * @param the type of the first argument the predicate tests - * @param the type of the second argument the predicate tests - * @param the type of checked exception the predicate may throw - * @return the boolean value returned by the predicate - */ - public static boolean test(final FailableBiPredicate predicate, - final T object1, final U object2) { - return getAsBoolean(() -> predicate.test(object1, object2)); - } - - /** - * Tests a predicate and rethrows any exception as a {@link RuntimeException}. - * - * @param predicate the predicate to test - * @param object the input to test by {@code predicate} - * @param the type of argument the predicate tests - * @param the type of checked exception the predicate may throw - * @return the boolean value returned by the predicate - */ - public static boolean test(final FailablePredicate predicate, final T object) { - return getAsBoolean(() -> predicate.test(object)); - } - - /** - * A simple try-with-resources implementation, that can be used, if your objects - * do not implement the {@link AutoCloseable} interface. The method executes the - * {@code action}. The method guarantees, that all the - * {@code resources} are being executed, in the given order, afterwards, and - * regardless of success, or failure. If either the original action, or any of - * the resource action fails, then the first failure (AKA - * {@link Throwable} is rethrown. Example use: - * - *
-	 * final FileInputStream fis = new FileInputStream("my.file");
-	 * Functions.tryWithResources(useInputStream(fis), null, () -> fis.close());
-	 * 
- * - * @param action The action to execute. This object will always - * be invoked. - * @param errorHandler An optional error handler, which will be invoked finally, - * if any error occurred. The error handler will receive the - * first error, AKA {@link Throwable}. - * @param resources The resource actions to execute. All resource - * actions will be invoked, in the given order. A resource - * action is an instance of {@link FailableRunnable}, which - * will be executed. - * @see #tryWithResources(FailableRunnable, FailableRunnable...) - */ - @SafeVarargs - public static void tryWithResources(final FailableRunnable action, - final FailableConsumer errorHandler, - final FailableRunnable... resources) { - final FailableConsumer actualErrorHandler; - if (errorHandler == null) { - actualErrorHandler = Failable::rethrow; - } else { - actualErrorHandler = errorHandler; - } - if (resources != null) { - for (final FailableRunnable failableRunnable : resources) { - Objects.requireNonNull(failableRunnable, "runnable"); - } - } - Throwable th = null; - try { - action.run(); - } catch (final Throwable t) { - th = t; - } - if (resources != null) { - for (final FailableRunnable runnable : resources) { - try { - runnable.run(); - } catch (final Throwable t) { - if (th == null) { - th = t; - } - } - } - } - if (th != null) { - try { - actualErrorHandler.accept(th); - } catch (final Throwable t) { - throw rethrow(t); - } - } - } - - /** - * A simple try-with-resources implementation, that can be used, if your objects - * do not implement the {@link AutoCloseable} interface. The method executes the - * {@code action}. The method guarantees, that all the - * {@code resources} are being executed, in the given order, afterwards, and - * regardless of success, or failure. If either the original action, or any of - * the resource action fails, then the first failure (AKA - * {@link Throwable} is rethrown. Example use: - * - *
-	 * final FileInputStream fis = new FileInputStream("my.file");
-	 * Functions.tryWithResources(useInputStream(fis), () -> fis.close());
-	 * 
- * - * @param action The action to execute. This object will always be - * invoked. - * @param resources The resource actions to execute. All resource - * actions will be invoked, in the given order. A resource - * action is an instance of {@link FailableRunnable}, which - * will be executed. - * @see #tryWithResources(FailableRunnable, FailableConsumer, - * FailableRunnable...) - */ - @SafeVarargs - public static void tryWithResources(final FailableRunnable action, - final FailableRunnable... resources) { - tryWithResources(action, null, resources); - } - - private Failable() { - // empty - } - -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableBiConsumer.java b/src/main/java/org/apache/commons/lang3/function/FailableBiConsumer.java deleted file mode 100755 index 4469793e..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableBiConsumer.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.Objects; -import java.util.function.BiConsumer; - -/** - * A functional interface like {@link BiConsumer} that declares a - * {@code Throwable}. - * - * @param Consumed type 1. - * @param Consumed type 2. - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableBiConsumer { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableBiConsumer NOP = (t, u) -> { - /* NOP */}; - - /** - * Returns The NOP singleton. - * - * @param Consumed type 1. - * @param Consumed type 2. - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableBiConsumer nop() { - return NOP; - } - - /** - * Accepts the consumer. - * - * @param t the first parameter for the consumable to accept - * @param u the second parameter for the consumable to accept - * @throws E Thrown when the consumer fails. - */ - void accept(T t, U u) throws E; - - /** - * Returns a composed {@code FailableBiConsumer} like - * {@link BiConsumer#andThen(BiConsumer)}. - * - * @param after the operation to perform after this one. - * @return a composed {@code FailableBiConsumer} like - * {@link BiConsumer#andThen(BiConsumer)}. - * @throws NullPointerException when {@code after} is null. - */ - default FailableBiConsumer andThen(final FailableBiConsumer after) { - Objects.requireNonNull(after); - return (t, u) -> { - accept(t, u); - after.accept(t, u); - }; - } -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableBiFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableBiFunction.java deleted file mode 100755 index f7d34ca1..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableBiFunction.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.Objects; -import java.util.function.BiFunction; -import java.util.function.Function; - -/** - * A functional interface like {@link BiFunction} that declares a - * {@code Throwable}. - * - * @param Input type 1. - * @param Input type 2. - * @param Return type. - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableBiFunction { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableBiFunction NOP = (t, u) -> null; - - /** - * Returns The NOP singleton. - * - * @param Consumed type 1. - * @param Consumed type 2. - * @param Return type. - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableBiFunction nop() { - return NOP; - } - - /** - * Returns a composed {@code FailableBiFunction} that like - * {@link BiFunction#andThen(Function)}. - * - * @param the output type of the {@code after} function, and of the - * composed function. - * @param after the operation to perform after this one. - * @return a composed {@code FailableBiFunction} that like - * {@link BiFunction#andThen(Function)}. - * @throws NullPointerException when {@code after} is null. - */ - default FailableBiFunction andThen(final FailableFunction after) { - Objects.requireNonNull(after); - return (final T t, final U u) -> after.apply(apply(t, u)); - } - - /** - * Applies this function. - * - * @param input1 the first input for the function - * @param input2 the second input for the function - * @return the result of the function - * @throws E Thrown when the function fails. - */ - R apply(T input1, U input2) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableBiPredicate.java b/src/main/java/org/apache/commons/lang3/function/FailableBiPredicate.java deleted file mode 100755 index 9b4d7e1e..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableBiPredicate.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.Objects; -import java.util.function.BiPredicate; - -/** - * A functional interface like {@link BiPredicate} that declares a - * {@code Throwable}. - * - * @param Predicate type 1. - * @param Predicate type 2. - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableBiPredicate { - - /** FALSE singleton */ - @SuppressWarnings("rawtypes") - FailableBiPredicate FALSE = (t, u) -> false; - - /** TRUE singleton */ - @SuppressWarnings("rawtypes") - FailableBiPredicate TRUE = (t, u) -> true; - - /** - * Returns The FALSE singleton. - * - * @param Consumed type 1. - * @param Consumed type 2. - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableBiPredicate falsePredicate() { - return FALSE; - } - - /** - * Returns The FALSE TRUE. - * - * @param Consumed type 1. - * @param Consumed type 2. - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableBiPredicate truePredicate() { - return TRUE; - } - - /** - * Returns a composed {@code FailableBiPredicate} like - * {@link BiPredicate#and(BiPredicate)}. - * - * @param other a predicate that will be logically-ANDed with this predicate. - * @return a composed {@code FailableBiPredicate} like - * {@link BiPredicate#and(BiPredicate)}. - * @throws NullPointerException if other is null - */ - default FailableBiPredicate and(final FailableBiPredicate other) { - Objects.requireNonNull(other); - return (final T t, final U u) -> test(t, u) && other.test(t, u); - } - - /** - * Returns a predicate that negates this predicate. - * - * @return a predicate that negates this predicate. - */ - default FailableBiPredicate negate() { - return (final T t, final U u) -> !test(t, u); - } - - /** - * Returns a composed {@code FailableBiPredicate} like - * {@link BiPredicate#and(BiPredicate)}. - * - * @param other a predicate that will be logically-ORed with this predicate. - * @return a composed {@code FailableBiPredicate} like - * {@link BiPredicate#and(BiPredicate)}. - * @throws NullPointerException if other is null - */ - default FailableBiPredicate or(final FailableBiPredicate other) { - Objects.requireNonNull(other); - return (final T t, final U u) -> test(t, u) || other.test(t, u); - } - - /** - * Tests the predicate. - * - * @param object1 the first object to test the predicate on - * @param object2 the second object to test the predicate on - * @return the predicate's evaluation - * @throws E Thrown when this predicate fails. - */ - boolean test(T object1, U object2) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableBooleanSupplier.java b/src/main/java/org/apache/commons/lang3/function/FailableBooleanSupplier.java deleted file mode 100755 index 1e957e02..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableBooleanSupplier.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.BooleanSupplier; - -/** - * A functional interface like {@link BooleanSupplier} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableBooleanSupplier { - - /** - * Supplies a boolean. - * - * @return a result - * @throws E if the supplier fails - */ - boolean getAsBoolean() throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableCallable.java b/src/main/java/org/apache/commons/lang3/function/FailableCallable.java deleted file mode 100755 index 6e710fc7..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableCallable.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -/** - * A functional interface like {@link java.util.concurrent.Callable} that - * declares a {@code Throwable}. - * - * @param Return type. - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableCallable { - - /** - * Calls the callable. - * - * @return The value returned from the callable - * @throws E if the callable fails - */ - R call() throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableConsumer.java b/src/main/java/org/apache/commons/lang3/function/FailableConsumer.java deleted file mode 100755 index d887331c..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableConsumer.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.Objects; -import java.util.function.Consumer; - -/** - * A functional interface like {@link Consumer} that declares a - * {@code Throwable}. - * - * @param Consumed type 1. - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableConsumer { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableConsumer NOP = t -> { - /* NOP */}; - - /** - * Returns The NOP singleton. - * - * @param Consumed type 1. - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableConsumer nop() { - return NOP; - } - - /** - * Accepts the consumer. - * - * @param object the parameter for the consumable to accept - * @throws E Thrown when the consumer fails. - */ - void accept(T object) throws E; - - /** - * Returns a composed {@code Consumer} like {@link Consumer#andThen(Consumer)}. - * - * @param after the operation to perform after this operation - * @return a composed {@code Consumer} like {@link Consumer#andThen(Consumer)}. - * @throws NullPointerException when {@code after} is null - */ - default FailableConsumer andThen(final FailableConsumer after) { - Objects.requireNonNull(after); - return (final T t) -> { - accept(t); - after.accept(t); - }; - } -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableDoubleBinaryOperator.java b/src/main/java/org/apache/commons/lang3/function/FailableDoubleBinaryOperator.java deleted file mode 100755 index 3a170527..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableDoubleBinaryOperator.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.DoubleBinaryOperator; - -/** - * A functional interface like {@link DoubleBinaryOperator} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableDoubleBinaryOperator { - - /** - * Applies this operator to the given operands. - * - * @param left the first operand - * @param right the second operand - * @return the operator result - * @throws E if the operation fails - */ - double applyAsDouble(double left, double right) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableDoubleConsumer.java b/src/main/java/org/apache/commons/lang3/function/FailableDoubleConsumer.java deleted file mode 100755 index 5c98df60..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableDoubleConsumer.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.Objects; -import java.util.function.DoubleConsumer; - -/** - * A functional interface like {@link DoubleConsumer} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableDoubleConsumer { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableDoubleConsumer NOP = t -> { - /* NOP */}; - - /** - * Returns The NOP singleton. - * - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableDoubleConsumer nop() { - return NOP; - } - - /** - * Accepts the consumer. - * - * @param value the parameter for the consumable to accept - * @throws E Thrown when the consumer fails. - */ - void accept(double value) throws E; - - /** - * Returns a composed {@code FailableDoubleConsumer} like - * {@link DoubleConsumer#andThen(DoubleConsumer)}. - * - * @param after the operation to perform after this one. - * @return a composed {@code FailableDoubleConsumer} like - * {@link DoubleConsumer#andThen(DoubleConsumer)}. - * @throws NullPointerException when {@code after} is null. - */ - default FailableDoubleConsumer andThen(final FailableDoubleConsumer after) { - Objects.requireNonNull(after); - return (final double t) -> { - accept(t); - after.accept(t); - }; - } -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableDoubleFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableDoubleFunction.java deleted file mode 100755 index 109f6b5e..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableDoubleFunction.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.DoubleFunction; - -/** - * A functional interface like {@link DoubleFunction} that declares a - * {@code Throwable}. - * - * @param Return type. - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableDoubleFunction { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableDoubleFunction NOP = t -> null; - - /** - * Returns The NOP singleton. - * - * @param Return type. - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableDoubleFunction nop() { - return NOP; - } - - /** - * Applies this function. - * - * @param input the input for the function - * @return the result of the function - * @throws E Thrown when the function fails. - */ - R apply(double input) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableDoublePredicate.java b/src/main/java/org/apache/commons/lang3/function/FailableDoublePredicate.java deleted file mode 100755 index 342af354..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableDoublePredicate.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.Objects; -import java.util.function.DoublePredicate; - -/** - * A functional interface like {@link DoublePredicate} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableDoublePredicate { - - /** FALSE singleton */ - @SuppressWarnings("rawtypes") - FailableDoublePredicate FALSE = t -> false; - - /** TRUE singleton */ - @SuppressWarnings("rawtypes") - FailableDoublePredicate TRUE = t -> true; - - /** - * Returns The FALSE singleton. - * - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableDoublePredicate falsePredicate() { - return FALSE; - } - - /** - * Returns The FALSE TRUE. - * - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableDoublePredicate truePredicate() { - return TRUE; - } - - /** - * Returns a composed {@code FailableDoublePredicate} like - * {@link DoublePredicate#and(DoublePredicate)}. - * - * @param other a predicate that will be logically-ANDed with this predicate. - * @return a composed {@code FailableDoublePredicate} like - * {@link DoublePredicate#and(DoublePredicate)}. - * @throws NullPointerException if other is null - */ - default FailableDoublePredicate and(final FailableDoublePredicate other) { - Objects.requireNonNull(other); - return t -> test(t) && other.test(t); - } - - /** - * Returns a predicate that negates this predicate. - * - * @return a predicate that negates this predicate. - */ - default FailableDoublePredicate negate() { - return t -> !test(t); - } - - /** - * Returns a composed {@code FailableDoublePredicate} like - * {@link DoublePredicate#and(DoublePredicate)}. - * - * @param other a predicate that will be logically-ORed with this predicate. - * @return a composed {@code FailableDoublePredicate} like - * {@link DoublePredicate#and(DoublePredicate)}. - * @throws NullPointerException if other is null - */ - default FailableDoublePredicate or(final FailableDoublePredicate other) { - Objects.requireNonNull(other); - return t -> test(t) || other.test(t); - } - - /** - * Tests the predicate. - * - * @param value the parameter for the predicate to accept. - * @return {@code true} if the input argument matches the predicate, - * {@code false} otherwise. - * @throws E Thrown when the consumer fails. - */ - boolean test(double value) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableDoubleSupplier.java b/src/main/java/org/apache/commons/lang3/function/FailableDoubleSupplier.java deleted file mode 100755 index 070ed6d8..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableDoubleSupplier.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.DoubleSupplier; - -/** - * A functional interface like {@link DoubleSupplier} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableDoubleSupplier { - - /** - * Supplies a double. - * - * @return a result - * @throws E if the supplier fails - */ - double getAsDouble() throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableDoubleToIntFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableDoubleToIntFunction.java deleted file mode 100755 index d40fc22f..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableDoubleToIntFunction.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.DoubleToIntFunction; - -/** - * A functional interface like {@link DoubleToIntFunction} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableDoubleToIntFunction { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableDoubleToIntFunction NOP = t -> 0; - - /** - * Returns The NOP singleton. - * - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableDoubleToIntFunction nop() { - return NOP; - } - - /** - * Applies this function to the given argument. - * - * @param value the function argument - * @return the function result - * @throws E Thrown when the function fails. - */ - int applyAsInt(double value) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableDoubleToLongFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableDoubleToLongFunction.java deleted file mode 100755 index bf2a4708..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableDoubleToLongFunction.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.DoubleToLongFunction; - -/** - * A functional interface like {@link DoubleToLongFunction} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableDoubleToLongFunction { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableDoubleToLongFunction NOP = t -> 0; - - /** - * Returns The NOP singleton. - * - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableDoubleToLongFunction nop() { - return NOP; - } - - /** - * Applies this function to the given argument. - * - * @param value the function argument - * @return the function result - * @throws E if the operation fails - */ - int applyAsLong(double value) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableDoubleUnaryOperator.java b/src/main/java/org/apache/commons/lang3/function/FailableDoubleUnaryOperator.java deleted file mode 100755 index 59c62a84..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableDoubleUnaryOperator.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.Objects; -import java.util.function.DoubleUnaryOperator; - -/** - * A functional interface like {@link DoubleUnaryOperator} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -public interface FailableDoubleUnaryOperator { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableDoubleUnaryOperator NOP = t -> 0d; - - /** - * Returns a unary operator that always returns its input argument. - * - * @param Thrown exception. - * @return a unary operator that always returns its input argument - */ - static FailableDoubleUnaryOperator identity() { - return t -> t; - } - - /** - * Returns The NOP singleton. - * - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableDoubleUnaryOperator nop() { - return NOP; - } - - /** - * Returns a composed {@code FailableDoubleUnaryOperator} like - * {@link DoubleUnaryOperator#andThen(DoubleUnaryOperator)}. - * - * @param after the operator to apply after this one. - * @return a composed {@code FailableDoubleUnaryOperator} like - * {@link DoubleUnaryOperator#andThen(DoubleUnaryOperator)}. - * @throws NullPointerException if after is null. - * @see #compose(FailableDoubleUnaryOperator) - */ - default FailableDoubleUnaryOperator andThen(final FailableDoubleUnaryOperator after) { - Objects.requireNonNull(after); - return (final double t) -> after.applyAsDouble(applyAsDouble(t)); - } - - /** - * Applies this operator to the given operand. - * - * @param operand the operand - * @return the operator result - * @throws E Thrown when a consumer fails. - */ - double applyAsDouble(double operand) throws E; - - /** - * Returns a composed {@code FailableDoubleUnaryOperator} like - * {@link DoubleUnaryOperator#compose(DoubleUnaryOperator)}. - * - * @param before the operator to apply before this one. - * @return a composed {@code FailableDoubleUnaryOperator} like - * {@link DoubleUnaryOperator#compose(DoubleUnaryOperator)}. - * @throws NullPointerException if before is null. - * @see #andThen(FailableDoubleUnaryOperator) - */ - default FailableDoubleUnaryOperator compose(final FailableDoubleUnaryOperator before) { - Objects.requireNonNull(before); - return (final double v) -> applyAsDouble(before.applyAsDouble(v)); - } -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableFunction.java deleted file mode 100755 index 61f913cc..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableFunction.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.Objects; -import java.util.function.Function; - -/** - * A functional interface like {@link Function} that declares a - * {@code Throwable}. - * - * @param Input type 1. - * @param Return type. - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableFunction { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableFunction NOP = t -> null; - - /** - * Returns a function that always returns its input argument. - * - * @param the type of the input and output objects to the function - * @param Thrown exception. - * @return a function that always returns its input argument - */ - static FailableFunction identity() { - return t -> t; - } - - /** - * Returns The NOP singleton. - * - * @param Consumed type 1. - * @param Return type. - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableFunction nop() { - return NOP; - } - - /** - * Returns a composed {@code FailableFunction} like - * {@link Function#andThen(Function)}. - * - * @param the output type of the {@code after} function, and of the composed - * function. - * @return a composed {@code FailableFunction} like - * {@link Function#andThen(Function)}. - * @param after the operation to perform after this one. - * @throws NullPointerException when {@code after} is null. - */ - default FailableFunction andThen(final FailableFunction after) { - Objects.requireNonNull(after); - return (final T t) -> after.apply(apply(t)); - } - - /** - * Applies this function. - * - * @param input the input for the function - * @return the result of the function - * @throws E Thrown when the function fails. - */ - R apply(T input) throws E; - - /** - * Returns a composed {@code FailableFunction} like - * {@link Function#compose(Function)}. - * - * @param the input type to the {@code before} function, and to the - * composed function. - * @param before the operator to apply before this one. - * @return a a composed {@code FailableFunction} like - * {@link Function#compose(Function)}. - * @throws NullPointerException if before is null. - * @see #andThen(FailableFunction) - */ - default FailableFunction compose(final FailableFunction before) { - Objects.requireNonNull(before); - return (final V v) -> apply(before.apply(v)); - } -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableIntBinaryOperator.java b/src/main/java/org/apache/commons/lang3/function/FailableIntBinaryOperator.java deleted file mode 100755 index 23d8c453..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableIntBinaryOperator.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.IntBinaryOperator; - -/** - * A functional interface like {@link IntBinaryOperator} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableIntBinaryOperator { - - /** - * Applies this operator to the given operands. - * - * @param left the first operand - * @param right the second operand - * @return the operator result - * @throws E if the operation fails - */ - int applyAsInt(int left, int right) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableIntConsumer.java b/src/main/java/org/apache/commons/lang3/function/FailableIntConsumer.java deleted file mode 100755 index 8574af58..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableIntConsumer.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.Objects; -import java.util.function.IntConsumer; - -/** - * A functional interface like {@link IntConsumer} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableIntConsumer { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableIntConsumer NOP = t -> { - /* NOP */}; - - /** - * Returns The NOP singleton. - * - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableIntConsumer nop() { - return NOP; - } - - /** - * Accepts the consumer. - * - * @param value the parameter for the consumable to accept - * @throws E Thrown when the consumer fails. - */ - void accept(int value) throws E; - - /** - * Returns a composed {@code FailableIntConsumer} like - * {@link IntConsumer#andThen(IntConsumer)}. - * - * @param after the operation to perform after this one. - * @return a composed {@code FailableLongConsumer} like - * {@link IntConsumer#andThen(IntConsumer)}. - * @throws NullPointerException if {@code after} is null - */ - default FailableIntConsumer andThen(final FailableIntConsumer after) { - Objects.requireNonNull(after); - return (final int t) -> { - accept(t); - after.accept(t); - }; - } - -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableIntFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableIntFunction.java deleted file mode 100755 index d0a15475..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableIntFunction.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.IntFunction; - -/** - * A functional interface like {@link IntFunction} that declares a - * {@code Throwable}. - * - * @param Return type. - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableIntFunction { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableIntFunction NOP = t -> null; - - /** - * Returns The NOP singleton. - * - * @param Return type. - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableIntFunction nop() { - return NOP; - } - - /** - * Applies this function. - * - * @param input the input for the function - * @return the result of the function - * @throws E Thrown when the function fails. - */ - R apply(int input) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableIntPredicate.java b/src/main/java/org/apache/commons/lang3/function/FailableIntPredicate.java deleted file mode 100755 index 04fb2fc6..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableIntPredicate.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.Objects; -import java.util.function.IntPredicate; - -/** - * A functional interface like {@link IntPredicate} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableIntPredicate { - - /** FALSE singleton */ - @SuppressWarnings("rawtypes") - FailableIntPredicate FALSE = t -> false; - - /** TRUE singleton */ - @SuppressWarnings("rawtypes") - FailableIntPredicate TRUE = t -> true; - - /** - * Returns The FALSE singleton. - * - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableIntPredicate falsePredicate() { - return FALSE; - } - - /** - * Returns The FALSE TRUE. - * - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableIntPredicate truePredicate() { - return TRUE; - } - - /** - * Returns a composed {@code FailableIntPredicate} like - * {@link IntPredicate#and(IntPredicate)}. - * - * @param other a predicate that will be logically-ANDed with this predicate. - * @return a composed {@code FailableIntPredicate} like - * {@link IntPredicate#and(IntPredicate)}. - * @throws NullPointerException if other is null - */ - default FailableIntPredicate and(final FailableIntPredicate other) { - Objects.requireNonNull(other); - return t -> test(t) && other.test(t); - } - - /** - * Returns a predicate that negates this predicate. - * - * @return a predicate that negates this predicate. - */ - default FailableIntPredicate negate() { - return t -> !test(t); - } - - /** - * Returns a composed {@code FailableIntPredicate} like - * {@link IntPredicate#and(IntPredicate)}. - * - * @param other a predicate that will be logically-ORed with this predicate. - * @return a composed {@code FailableIntPredicate} like - * {@link IntPredicate#and(IntPredicate)}. - * @throws NullPointerException if other is null - */ - default FailableIntPredicate or(final FailableIntPredicate other) { - Objects.requireNonNull(other); - return t -> test(t) || other.test(t); - } - - /** - * Tests the predicate. - * - * @param value the parameter for the predicate to accept. - * @return {@code true} if the input argument matches the predicate, - * {@code false} otherwise. - * @throws E Thrown when the consumer fails. - */ - boolean test(int value) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableIntSupplier.java b/src/main/java/org/apache/commons/lang3/function/FailableIntSupplier.java deleted file mode 100755 index 12731f59..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableIntSupplier.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.IntSupplier; - -/** - * A functional interface like {@link IntSupplier} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableIntSupplier { - - /** - * Supplies an int. - * - * @return a result - * @throws E if the supplier fails - */ - int getAsInt() throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableIntToDoubleFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableIntToDoubleFunction.java deleted file mode 100755 index 08004d1f..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableIntToDoubleFunction.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.IntToDoubleFunction; - -/** - * A functional interface like {@link IntToDoubleFunction} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableIntToDoubleFunction { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableIntToDoubleFunction NOP = t -> 0d; - - /** - * Returns The NOP singleton. - * - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableIntToDoubleFunction nop() { - return NOP; - } - - /** - * Applies this function to the given argument. - * - * @param value the function argument - * @return the function result - * @throws E Thrown when the function fails. - */ - double applyAsDouble(int value) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableIntToLongFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableIntToLongFunction.java deleted file mode 100755 index f499dc45..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableIntToLongFunction.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.IntToLongFunction; - -/** - * A functional interface like {@link IntToLongFunction} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableIntToLongFunction { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableIntToLongFunction NOP = t -> 0L; - - /** - * Returns The NOP singleton. - * - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableIntToLongFunction nop() { - return NOP; - } - - /** - * Applies this function to the given argument. - * - * @param value the function argument - * @return the function result - * @throws E Thrown when the function fails. - */ - long applyAsLong(int value) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableIntUnaryOperator.java b/src/main/java/org/apache/commons/lang3/function/FailableIntUnaryOperator.java deleted file mode 100755 index 878a4763..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableIntUnaryOperator.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.Objects; -import java.util.function.IntUnaryOperator; - -/** - * A functional interface like {@link IntUnaryOperator} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -public interface FailableIntUnaryOperator { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableIntUnaryOperator NOP = t -> 0; - - /** - * Returns a unary operator that always returns its input argument. - * - * @param Thrown exception. - * @return a unary operator that always returns its input argument - */ - static FailableIntUnaryOperator identity() { - return t -> t; - } - - /** - * Returns The NOP singleton. - * - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableIntUnaryOperator nop() { - return NOP; - } - - /** - * Returns a composed {@code FailableDoubleUnaryOperator} like - * {@link IntUnaryOperator#andThen(IntUnaryOperator)}. - * - * @param after the operator to apply after this one. - * @return a composed {@code FailableIntUnaryOperator} like - * {@link IntUnaryOperator#andThen(IntUnaryOperator)}. - * @throws NullPointerException if after is null. - * @see #compose(FailableIntUnaryOperator) - */ - default FailableIntUnaryOperator andThen(final FailableIntUnaryOperator after) { - Objects.requireNonNull(after); - return (final int t) -> after.applyAsInt(applyAsInt(t)); - } - - /** - * Applies this operator to the given operand. - * - * @param operand the operand - * @return the operator result - * @throws E Thrown when a consumer fails. - */ - int applyAsInt(int operand) throws E; - - /** - * Returns a composed {@code FailableIntUnaryOperator} like - * {@link IntUnaryOperator#compose(IntUnaryOperator)}. - * - * @param before the operator to apply before this one. - * @return a composed {@code FailableIntUnaryOperator} like - * {@link IntUnaryOperator#compose(IntUnaryOperator)}. - * @throws NullPointerException if before is null. - * @see #andThen(FailableIntUnaryOperator) - */ - default FailableIntUnaryOperator compose(final FailableIntUnaryOperator before) { - Objects.requireNonNull(before); - return (final int v) -> applyAsInt(before.applyAsInt(v)); - } -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableLongBinaryOperator.java b/src/main/java/org/apache/commons/lang3/function/FailableLongBinaryOperator.java deleted file mode 100755 index b3775f08..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableLongBinaryOperator.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.LongBinaryOperator; - -/** - * A functional interface like {@link LongBinaryOperator} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableLongBinaryOperator { - - /** - * Applies this operator to the given operands. - * - * @param left the first operand - * @param right the second operand - * @return the operator result - * @throws E if the operation fails - */ - long applyAsLong(long left, long right) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableLongConsumer.java b/src/main/java/org/apache/commons/lang3/function/FailableLongConsumer.java deleted file mode 100755 index 780f3521..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableLongConsumer.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.Objects; -import java.util.function.LongConsumer; - -/** - * A functional interface like {@link LongConsumer} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableLongConsumer { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableLongConsumer NOP = t -> { - /* NOP */}; - - /** - * Returns The NOP singleton. - * - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableLongConsumer nop() { - return NOP; - } - - /** - * Accepts the consumer. - * - * @param object the parameter for the consumable to accept - * @throws E Thrown when the consumer fails. - */ - void accept(long object) throws E; - - /** - * Returns a composed {@code FailableLongConsumer} like - * {@link LongConsumer#andThen(LongConsumer)}. - * - * @param after the operation to perform after this one. - * @return a composed {@code FailableLongConsumer} like - * {@link LongConsumer#andThen(LongConsumer)}. - * @throws NullPointerException if {@code after} is null - */ - default FailableLongConsumer andThen(final FailableLongConsumer after) { - Objects.requireNonNull(after); - return (final long t) -> { - accept(t); - after.accept(t); - }; - } -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableLongFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableLongFunction.java deleted file mode 100755 index ec8e20c7..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableLongFunction.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.LongFunction; - -/** - * A functional interface like {@link LongFunction} that declares a - * {@code Throwable}. - * - * @param Return type. - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableLongFunction { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableLongFunction NOP = t -> null; - - /** - * Returns The NOP singleton. - * - * @param Return type. - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableLongFunction nop() { - return NOP; - } - - /** - * Applies this function. - * - * @param input the input for the function - * @return the result of the function - * @throws E Thrown when the function fails. - */ - R apply(long input) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableLongPredicate.java b/src/main/java/org/apache/commons/lang3/function/FailableLongPredicate.java deleted file mode 100755 index cc54ff81..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableLongPredicate.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.Objects; -import java.util.function.LongPredicate; - -/** - * A functional interface like {@link LongPredicate} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableLongPredicate { - - /** FALSE singleton */ - @SuppressWarnings("rawtypes") - FailableLongPredicate FALSE = t -> false; - - /** TRUE singleton */ - @SuppressWarnings("rawtypes") - FailableLongPredicate TRUE = t -> true; - - /** - * Returns The FALSE singleton. - * - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableLongPredicate falsePredicate() { - return FALSE; - } - - /** - * Returns The FALSE TRUE. - * - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableLongPredicate truePredicate() { - return TRUE; - } - - /** - * Returns a composed {@code FailableLongPredicate} like - * {@link LongPredicate#and(LongPredicate)}. - * - * @param other a predicate that will be logically-ANDed with this predicate. - * @return a composed {@code FailableLongPredicate} like - * {@link LongPredicate#and(LongPredicate)}. - * @throws NullPointerException if other is null - */ - default FailableLongPredicate and(final FailableLongPredicate other) { - Objects.requireNonNull(other); - return t -> test(t) && other.test(t); - } - - /** - * Returns a predicate that negates this predicate. - * - * @return a predicate that negates this predicate. - */ - default FailableLongPredicate negate() { - return t -> !test(t); - } - - /** - * Returns a composed {@code FailableLongPredicate} like - * {@link LongPredicate#and(LongPredicate)}. - * - * @param other a predicate that will be logically-ORed with this predicate. - * @return a composed {@code FailableLongPredicate} like - * {@link LongPredicate#and(LongPredicate)}. - * @throws NullPointerException if other is null - */ - default FailableLongPredicate or(final FailableLongPredicate other) { - Objects.requireNonNull(other); - return t -> test(t) || other.test(t); - } - - /** - * Tests the predicate. - * - * @param value the parameter for the predicate to accept. - * @return {@code true} if the input argument matches the predicate, - * {@code false} otherwise. - * @throws E Thrown when the consumer fails. - */ - boolean test(long value) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableLongSupplier.java b/src/main/java/org/apache/commons/lang3/function/FailableLongSupplier.java deleted file mode 100755 index 76be6c01..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableLongSupplier.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.LongSupplier; - -/** - * A functional interface like {@link LongSupplier} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableLongSupplier { - - /** - * Supplies a long. - * - * @return a result - * @throws E if the supplier fails - */ - long getAsLong() throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableLongToDoubleFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableLongToDoubleFunction.java deleted file mode 100755 index 8bcc34bf..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableLongToDoubleFunction.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.LongToDoubleFunction; - -/** - * A functional interface like {@link LongToDoubleFunction} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableLongToDoubleFunction { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableLongToDoubleFunction NOP = t -> 0d; - - /** - * Returns The NOP singleton. - * - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableLongToDoubleFunction nop() { - return NOP; - } - - /** - * Applies this function to the given argument. - * - * @param value the function argument - * @return the function result - * @throws E Thrown when the function fails. - */ - double applyAsDouble(long value) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableLongToIntFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableLongToIntFunction.java deleted file mode 100755 index 225a2f4d..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableLongToIntFunction.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.LongToIntFunction; - -/** - * A functional interface like {@link LongToIntFunction} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableLongToIntFunction { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableLongToIntFunction NOP = t -> 0; - - /** - * Returns The NOP singleton. - * - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableLongToIntFunction nop() { - return NOP; - } - - /** - * Applies this function to the given argument. - * - * @param value the function argument - * @return the function result - * @throws E Thrown when the function fails. - */ - int applyAsInt(long value) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableLongUnaryOperator.java b/src/main/java/org/apache/commons/lang3/function/FailableLongUnaryOperator.java deleted file mode 100755 index 243ee6cf..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableLongUnaryOperator.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.Objects; -import java.util.function.LongUnaryOperator; - -/** - * A functional interface like {@link LongUnaryOperator} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -public interface FailableLongUnaryOperator { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableLongUnaryOperator NOP = t -> 0L; - - /** - * Returns a unary operator that always returns its input argument. - * - * @param Thrown exception. - * @return a unary operator that always returns its input argument - */ - static FailableLongUnaryOperator identity() { - return t -> t; - } - - /** - * Returns The NOP singleton. - * - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableLongUnaryOperator nop() { - return NOP; - } - - /** - * Returns a composed {@code FailableDoubleUnaryOperator} like - * {@link LongUnaryOperator#andThen(LongUnaryOperator)}. - * - * @param after the operator to apply after this one. - * @return a composed {@code FailableLongUnaryOperator} like - * {@link LongUnaryOperator#andThen(LongUnaryOperator)}. - * @throws NullPointerException if after is null. - * @see #compose(FailableLongUnaryOperator) - */ - default FailableLongUnaryOperator andThen(final FailableLongUnaryOperator after) { - Objects.requireNonNull(after); - return (final long t) -> after.applyAsLong(applyAsLong(t)); - } - - /** - * Applies this operator to the given operand. - * - * @param operand the operand - * @return the operator result - * @throws E Thrown when a consumer fails. - */ - long applyAsLong(long operand) throws E; - - /** - * Returns a composed {@code FailableLongUnaryOperator} like - * {@link LongUnaryOperator#compose(LongUnaryOperator)}. - * - * @param before the operator to apply before this one. - * @return a composed {@code FailableLongUnaryOperator} like - * {@link LongUnaryOperator#compose(LongUnaryOperator)}. - * @throws NullPointerException if before is null. - * @see #andThen(FailableLongUnaryOperator) - */ - default FailableLongUnaryOperator compose(final FailableLongUnaryOperator before) { - Objects.requireNonNull(before); - return (final long v) -> applyAsLong(before.applyAsLong(v)); - } -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableObjDoubleConsumer.java b/src/main/java/org/apache/commons/lang3/function/FailableObjDoubleConsumer.java deleted file mode 100755 index 375a440f..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableObjDoubleConsumer.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.ObjDoubleConsumer; - -/** - * A functional interface like {@link ObjDoubleConsumer} that declares a - * {@code Throwable}. - * - * @param the type of the object argument to the operation. - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableObjDoubleConsumer { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableObjDoubleConsumer NOP = (t, u) -> { - /* NOP */}; - - /** - * Returns The NOP singleton. - * - * @param the type of the object argument to the operation. - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableObjDoubleConsumer nop() { - return NOP; - } - - /** - * Accepts the consumer. - * - * @param object the object parameter for the consumable to accept. - * @param value the double parameter for the consumable to accept. - * @throws E Thrown when the consumer fails. - */ - void accept(T object, double value) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableObjIntConsumer.java b/src/main/java/org/apache/commons/lang3/function/FailableObjIntConsumer.java deleted file mode 100755 index ba865695..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableObjIntConsumer.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.ObjIntConsumer; - -/** - * A functional interface like {@link ObjIntConsumer} that declares a - * {@code Throwable}. - * - * @param the type of the object argument to the operation. - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableObjIntConsumer { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableObjIntConsumer NOP = (t, u) -> { - /* NOP */}; - - /** - * Returns The NOP singleton. - * - * @param the type of the object argument to the operation. - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableObjIntConsumer nop() { - return NOP; - } - - /** - * Accepts the consumer. - * - * @param object the object parameter for the consumable to accept. - * @param value the int parameter for the consumable to accept. - * @throws E Thrown when the consumer fails. - */ - void accept(T object, int value) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableObjLongConsumer.java b/src/main/java/org/apache/commons/lang3/function/FailableObjLongConsumer.java deleted file mode 100755 index d29b692d..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableObjLongConsumer.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.ObjLongConsumer; - -/** - * A functional interface like {@link ObjLongConsumer} that declares a - * {@code Throwable}. - * - * @param the type of the object argument to the operation. - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableObjLongConsumer { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableObjLongConsumer NOP = (t, u) -> { - /* NOP */}; - - /** - * Returns The NOP singleton. - * - * @param the type of the object argument to the operation. - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableObjLongConsumer nop() { - return NOP; - } - - /** - * Accepts the consumer. - * - * @param object the object parameter for the consumable to accept. - * @param value the long parameter for the consumable to accept. - * @throws E Thrown when the consumer fails. - */ - void accept(T object, long value) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailablePredicate.java b/src/main/java/org/apache/commons/lang3/function/FailablePredicate.java deleted file mode 100755 index a6d70ffd..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailablePredicate.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.Objects; -import java.util.function.Predicate; - -/** - * A functional interface like {@link Predicate} that declares a - * {@code Throwable}. - * - * @param Predicate type. - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailablePredicate { - - /** FALSE singleton */ - @SuppressWarnings("rawtypes") - FailablePredicate FALSE = t -> false; - - /** TRUE singleton */ - @SuppressWarnings("rawtypes") - FailablePredicate TRUE = t -> true; - - /** - * Returns The FALSE singleton. - * - * @param Predicate type. - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailablePredicate falsePredicate() { - return FALSE; - } - - /** - * Returns The FALSE TRUE. - * - * @param Predicate type. - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailablePredicate truePredicate() { - return TRUE; - } - - /** - * Returns a composed {@code FailablePredicate} like - * {@link Predicate#and(Predicate)}. - * - * @param other a predicate that will be logically-ANDed with this predicate. - * @return a composed {@code FailablePredicate} like - * {@link Predicate#and(Predicate)}. - * @throws NullPointerException if other is null - */ - default FailablePredicate and(final FailablePredicate other) { - Objects.requireNonNull(other); - return t -> test(t) && other.test(t); - } - - /** - * Returns a predicate that negates this predicate. - * - * @return a predicate that negates this predicate. - */ - default FailablePredicate negate() { - return t -> !test(t); - } - - /** - * Returns a composed {@code FailablePredicate} like - * {@link Predicate#and(Predicate)}. - * - * @param other a predicate that will be logically-ORed with this predicate. - * @return a composed {@code FailablePredicate} like - * {@link Predicate#and(Predicate)}. - * @throws NullPointerException if other is null - */ - default FailablePredicate or(final FailablePredicate other) { - Objects.requireNonNull(other); - return t -> test(t) || other.test(t); - } - - /** - * Tests the predicate. - * - * @param object the object to test the predicate on - * @return the predicate's evaluation - * @throws E if the predicate fails - */ - boolean test(T object) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableRunnable.java b/src/main/java/org/apache/commons/lang3/function/FailableRunnable.java deleted file mode 100755 index 55a2736d..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableRunnable.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -/** - * A functional interface like {@link Runnable} that declares a - * {@code Throwable}. - * - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableRunnable { - - /** - * Runs the function. - * - * @throws E Thrown when the function fails. - */ - void run() throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableShortSupplier.java b/src/main/java/org/apache/commons/lang3/function/FailableShortSupplier.java deleted file mode 100755 index 6a336f0f..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableShortSupplier.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.IntSupplier; - -/** - * A functional interface like {@link IntSupplier} but for {@code short} that - * declares a {@code Throwable}. - * - * @param Thrown exception. - * @since 3.12.0 - */ -@FunctionalInterface -public interface FailableShortSupplier { - - /** - * Supplies an int. - * - * @return a result - * @throws E if the supplier fails - */ - short getAsShort() throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableSupplier.java b/src/main/java/org/apache/commons/lang3/function/FailableSupplier.java deleted file mode 100755 index e46b4127..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableSupplier.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.Supplier; - -/** - * A functional interface like {@link Supplier} that declares a - * {@code Throwable}. - * - * @param Return type. - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableSupplier { - - /** - * Supplies an object - * - * @return a result - * @throws E if the supplier fails - */ - R get() throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableToDoubleBiFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableToDoubleBiFunction.java deleted file mode 100755 index 18e9cb30..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableToDoubleBiFunction.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.ToDoubleBiFunction; - -/** - * A functional interface like {@link ToDoubleBiFunction} that declares a - * {@code Throwable}. - * - * @param the type of the first argument to the function - * @param the type of the second argument to the function - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableToDoubleBiFunction { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableToDoubleBiFunction NOP = (t, u) -> 0d; - - /** - * Returns The NOP singleton. - * - * @param the type of the first argument to the function - * @param the type of the second argument to the function - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableToDoubleBiFunction nop() { - return NOP; - } - - /** - * Applies this function to the given arguments. - * - * @param t the first function argument - * @param u the second function argument - * @return the function result - * @throws E Thrown when the function fails. - */ - double applyAsDouble(T t, U u) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableToDoubleFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableToDoubleFunction.java deleted file mode 100755 index a400e268..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableToDoubleFunction.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.ToDoubleFunction; - -/** - * A functional interface like {@link ToDoubleFunction} that declares a - * {@code Throwable}. - * - * @param the type of the argument to the function - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableToDoubleFunction { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableToDoubleFunction NOP = t -> 0d; - - /** - * Returns The NOP singleton. - * - * @param the type of the argument to the function - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableToDoubleFunction nop() { - return NOP; - } - - /** - * Applies this function to the given arguments. - * - * @param t the first function argument - * @return the function result - * @throws E Thrown when the function fails. - */ - double applyAsDouble(T t) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableToIntBiFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableToIntBiFunction.java deleted file mode 100755 index 806426d3..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableToIntBiFunction.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.ToIntBiFunction; - -/** - * A functional interface like {@link ToIntBiFunction} that declares a - * {@code Throwable}. - * - * @param the type of the first argument to the function - * @param the type of the second argument to the function - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableToIntBiFunction { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableToIntBiFunction NOP = (t, u) -> 0; - - /** - * Returns The NOP singleton. - * - * @param the type of the first argument to the function - * @param the type of the second argument to the function - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableToIntBiFunction nop() { - return NOP; - } - - /** - * Applies this function to the given arguments. - * - * @param t the first function argument - * @param u the second function argument - * @return the function result - * @throws E Thrown when the function fails. - */ - int applyAsInt(T t, U u) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableToIntFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableToIntFunction.java deleted file mode 100755 index d3972747..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableToIntFunction.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.ToIntFunction; - -/** - * A functional interface like {@link ToIntFunction} that declares a - * {@code Throwable}. - * - * @param the type of the argument to the function - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableToIntFunction { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableToIntFunction NOP = t -> 0; - - /** - * Returns The NOP singleton. - * - * @param the type of the argument to the function - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableToIntFunction nop() { - return NOP; - } - - /** - * Applies this function to the given arguments. - * - * @param t the first function argument - * @return the function result - * @throws E Thrown when the function fails. - */ - int applyAsInt(T t) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableToLongBiFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableToLongBiFunction.java deleted file mode 100755 index fe8a15a1..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableToLongBiFunction.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.ToLongBiFunction; - -/** - * A functional interface like {@link ToLongBiFunction} that declares a - * {@code Throwable}. - * - * @param the type of the first argument to the function - * @param the type of the second argument to the function - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableToLongBiFunction { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableToLongBiFunction NOP = (t, u) -> 0; - - /** - * Returns The NOP singleton. - * - * @param the type of the first argument to the function - * @param the type of the second argument to the function - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableToLongBiFunction nop() { - return NOP; - } - - /** - * Applies this function to the given arguments. - * - * @param t the first function argument - * @param u the second function argument - * @return the function result - * @throws E Thrown when the function fails. - */ - long applyAsLong(T t, U u) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/FailableToLongFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableToLongFunction.java deleted file mode 100755 index 420f9839..00000000 --- a/src/main/java/org/apache/commons/lang3/function/FailableToLongFunction.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.ToLongFunction; - -/** - * A functional interface like {@link ToLongFunction} that declares a - * {@code Throwable}. - * - * @param the type of the first argument to the function - * @param Thrown exception. - * @since 3.11 - */ -@FunctionalInterface -public interface FailableToLongFunction { - - /** NOP singleton */ - @SuppressWarnings("rawtypes") - FailableToLongFunction NOP = t -> 0L; - - /** - * Returns The NOP singleton. - * - * @param the type of the argument to the function - * @param Thrown exception. - * @return The NOP singleton. - */ - static FailableToLongFunction nop() { - return NOP; - } - - /** - * Applies this function to the given arguments. - * - * @param t the first function argument - * @return the function result - * @throws E Thrown when the function fails. - */ - long applyAsLong(T t) throws E; -} diff --git a/src/main/java/org/apache/commons/lang3/function/ToBooleanBiFunction.java b/src/main/java/org/apache/commons/lang3/function/ToBooleanBiFunction.java deleted file mode 100755 index 222e651c..00000000 --- a/src/main/java/org/apache/commons/lang3/function/ToBooleanBiFunction.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.function; - -import java.util.function.BiFunction; - -/** - * A function that accepts two arguments and produces a boolean result. This is - * the {@code boolean}-producing primitive specialization for - * {@link BiFunction}. - * - * @param the type of the first argument to the function. - * @param the type of the second argument to the function. - * - * @see BiFunction - * @since 3.12.0 - */ -@FunctionalInterface -public interface ToBooleanBiFunction { - - /** - * Applies this function to the given arguments. - * - * @param t the first function argument. - * @param u the second function argument. - * @return the function result. - */ - boolean applyAsBoolean(T t, U u); -} diff --git a/src/main/java/org/apache/commons/lang3/function/TriFunction.java b/src/main/java/org/apache/commons/lang3/function/TriFunction.java deleted file mode 100755 index 7ae09ba3..00000000 --- a/src/main/java/org/apache/commons/lang3/function/TriFunction.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.function; - -import java.util.Objects; -import java.util.function.Function; - -/** - * Represents a function that accepts three arguments and produces a result. - * This is the three-arity specialization of {@link Function}. - * - *

- * This is a functional interface whose - * functional method is {@link #apply(Object, Object, Object)}. - *

- * - * @param the type of the first argument to the function - * @param the type of the second argument to the function - * @param the type of the third argument to the function - * @param the type of the result of the function - * - * @see Function - * @since 3.12.0 - */ -@FunctionalInterface -public interface TriFunction { - - /** - * Applies this function to the given arguments. - * - * @param t the first function argument - * @param u the second function argument - * @param v the third function argument - * @return the function result - */ - R apply(T t, U u, V v); - - /** - * Returns a composed function that first applies this function to its input, - * and then applies the {@code after} function to the result. If evaluation of - * either function throws an exception, it is relayed to the caller of the - * composed function. - * - * @param the type of output of the {@code after} function, and of the - * composed function - * @param after the function to apply after this function is applied - * @return a composed function that first applies this function and then applies - * the {@code after} function - * @throws NullPointerException if after is null - */ - default TriFunction andThen(final Function after) { - Objects.requireNonNull(after); - return (final T t, final U u, final V v) -> after.apply(apply(t, u, v)); - } -} diff --git a/src/main/java/org/apache/commons/lang3/function/package-info.java b/src/main/java/org/apache/commons/lang3/function/package-info.java deleted file mode 100755 index 1b8902e9..00000000 --- a/src/main/java/org/apache/commons/lang3/function/package-info.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Provides functional interfaces to complement those in - * {@code java.lang.function} and utilities for working with Java 8 lambdas. - * - *

- * Contains failable functional interfaces that address the fact that lambdas - * are supposed not to throw Exceptions, at least not checked Exceptions, A.K.A. - * instances of {@link java.lang.Exception}. A failable functional interface - * declares a type of Exception that may be raised if the function fails. - *

- * - * @since 3.11 - */ -package org.apache.commons.lang3.function; diff --git a/src/main/java/org/apache/commons/lang3/math/Fraction.java b/src/main/java/org/apache/commons/lang3/math/Fraction.java deleted file mode 100755 index 37805f4f..00000000 --- a/src/main/java/org/apache/commons/lang3/math/Fraction.java +++ /dev/null @@ -1,1025 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.math; - -import java.math.BigInteger; - -import org.apache.commons.lang3.Validate; - -/** - *

- * {@code Fraction} is a {@code Number} implementation that stores fractions - * accurately. - *

- * - *

- * This class is immutable, and interoperable with most methods that accept a - * {@code Number}. - *

- * - *

- * Note that this class is intended for common use cases, it is int based - * and thus suffers from various overflow issues. For a BigInteger based - * equivalent, please see the Commons Math BigFraction class. - *

- * - * @since 2.0 - */ -public final class Fraction extends Number implements Comparable { - - /** - * Required for serialization support. Lang version 2.0. - * - * @see java.io.Serializable - */ - private static final long serialVersionUID = 65382027393090L; - - /** - * {@code Fraction} representation of 0. - */ - public static final Fraction ZERO = new Fraction(0, 1); - /** - * {@code Fraction} representation of 1. - */ - public static final Fraction ONE = new Fraction(1, 1); - /** - * {@code Fraction} representation of 1/2. - */ - public static final Fraction ONE_HALF = new Fraction(1, 2); - /** - * {@code Fraction} representation of 1/3. - */ - public static final Fraction ONE_THIRD = new Fraction(1, 3); - /** - * {@code Fraction} representation of 2/3. - */ - public static final Fraction TWO_THIRDS = new Fraction(2, 3); - /** - * {@code Fraction} representation of 1/4. - */ - public static final Fraction ONE_QUARTER = new Fraction(1, 4); - /** - * {@code Fraction} representation of 2/4. - */ - public static final Fraction TWO_QUARTERS = new Fraction(2, 4); - /** - * {@code Fraction} representation of 3/4. - */ - public static final Fraction THREE_QUARTERS = new Fraction(3, 4); - /** - * {@code Fraction} representation of 1/5. - */ - public static final Fraction ONE_FIFTH = new Fraction(1, 5); - /** - * {@code Fraction} representation of 2/5. - */ - public static final Fraction TWO_FIFTHS = new Fraction(2, 5); - /** - * {@code Fraction} representation of 3/5. - */ - public static final Fraction THREE_FIFTHS = new Fraction(3, 5); - /** - * {@code Fraction} representation of 4/5. - */ - public static final Fraction FOUR_FIFTHS = new Fraction(4, 5); - - /** - * The numerator number part of the fraction (the three in three sevenths). - */ - private final int numerator; - /** - * The denominator number part of the fraction (the seven in three sevenths). - */ - private final int denominator; - - /** - * Cached output hashCode (class is immutable). - */ - private transient int hashCode; - /** - * Cached output toString (class is immutable). - */ - private transient String toString; - /** - * Cached output toProperString (class is immutable). - */ - private transient String toProperString; - - /** - *

- * Constructs a {@code Fraction} instance with the 2 parts of a fraction Y/Z. - *

- * - * @param numerator the numerator, for example the three in 'three sevenths' - * @param denominator the denominator, for example the seven in 'three sevenths' - */ - private Fraction(final int numerator, final int denominator) { - this.numerator = numerator; - this.denominator = denominator; - } - - /** - *

- * Creates a {@code Fraction} instance with the 2 parts of a fraction Y/Z. - *

- * - *

- * Any negative signs are resolved to be on the numerator. - *

- * - * @param numerator the numerator, for example the three in 'three sevenths' - * @param denominator the denominator, for example the seven in 'three sevenths' - * @return a new fraction instance - * @throws ArithmeticException if the denominator is {@code zero} or the - * denominator is {@code negative} and the numerator - * is {@code Integer#MIN_VALUE} - */ - public static Fraction getFraction(int numerator, int denominator) { - if (denominator == 0) { - throw new ArithmeticException("The denominator must not be zero"); - } - if (denominator < 0) { - if (numerator == Integer.MIN_VALUE || denominator == Integer.MIN_VALUE) { - throw new ArithmeticException("overflow: can't negate"); - } - numerator = -numerator; - denominator = -denominator; - } - return new Fraction(numerator, denominator); - } - - /** - *

- * Creates a {@code Fraction} instance with the 3 parts of a fraction X Y/Z. - *

- * - *

- * The negative sign must be passed in on the whole number part. - *

- * - * @param whole the whole number, for example the one in 'one and three - * sevenths' - * @param numerator the numerator, for example the three in 'one and three - * sevenths' - * @param denominator the denominator, for example the seven in 'one and three - * sevenths' - * @return a new fraction instance - * @throws ArithmeticException if the denominator is {@code zero} - * @throws ArithmeticException if the denominator is negative - * @throws ArithmeticException if the numerator is negative - * @throws ArithmeticException if the resulting numerator exceeds - * {@code Integer.MAX_VALUE} - */ - public static Fraction getFraction(final int whole, final int numerator, final int denominator) { - if (denominator == 0) { - throw new ArithmeticException("The denominator must not be zero"); - } - if (denominator < 0) { - throw new ArithmeticException("The denominator must not be negative"); - } - if (numerator < 0) { - throw new ArithmeticException("The numerator must not be negative"); - } - final long numeratorValue; - if (whole < 0) { - numeratorValue = whole * (long) denominator - numerator; - } else { - numeratorValue = whole * (long) denominator + numerator; - } - if (numeratorValue < Integer.MIN_VALUE || numeratorValue > Integer.MAX_VALUE) { - throw new ArithmeticException("Numerator too large to represent as an Integer."); - } - return new Fraction((int) numeratorValue, denominator); - } - - /** - *

- * Creates a reduced {@code Fraction} instance with the 2 parts of a fraction - * Y/Z. - *

- * - *

- * For example, if the input parameters represent 2/4, then the created fraction - * will be 1/2. - *

- * - *

- * Any negative signs are resolved to be on the numerator. - *

- * - * @param numerator the numerator, for example the three in 'three sevenths' - * @param denominator the denominator, for example the seven in 'three sevenths' - * @return a new fraction instance, with the numerator and denominator reduced - * @throws ArithmeticException if the denominator is {@code zero} - */ - public static Fraction getReducedFraction(int numerator, int denominator) { - if (denominator == 0) { - throw new ArithmeticException("The denominator must not be zero"); - } - if (numerator == 0) { - return ZERO; // normalize zero. - } - // allow 2^k/-2^31 as a valid fraction (where k>0) - if (denominator == Integer.MIN_VALUE && (numerator & 1) == 0) { - numerator /= 2; - denominator /= 2; - } - if (denominator < 0) { - if (numerator == Integer.MIN_VALUE || denominator == Integer.MIN_VALUE) { - throw new ArithmeticException("overflow: can't negate"); - } - numerator = -numerator; - denominator = -denominator; - } - // simplify fraction. - final int gcd = greatestCommonDivisor(numerator, denominator); - numerator /= gcd; - denominator /= gcd; - return new Fraction(numerator, denominator); - } - - /** - *

- * Creates a {@code Fraction} instance from a {@code double} value. - *

- * - *

- * This method uses the - * continued - * fraction algorithm, computing a maximum of 25 convergents and bounding - * the denominator by 10,000. - *

- * - * @param value the double value to convert - * @return a new fraction instance that is close to the value - * @throws ArithmeticException if {@code |value| > Integer.MAX_VALUE} or - * {@code value = NaN} - * @throws ArithmeticException if the calculated denominator is {@code zero} - * @throws ArithmeticException if the algorithm does not converge - */ - public static Fraction getFraction(double value) { - final int sign = value < 0 ? -1 : 1; - value = Math.abs(value); - if (value > Integer.MAX_VALUE || Double.isNaN(value)) { - throw new ArithmeticException("The value must not be greater than Integer.MAX_VALUE or NaN"); - } - final int wholeNumber = (int) value; - value -= wholeNumber; - - int numer0 = 0; // the pre-previous - int denom0 = 1; // the pre-previous - int numer1 = 1; // the previous - int denom1 = 0; // the previous - int numer2 = 0; // the current, setup in calculation - int denom2 = 0; // the current, setup in calculation - int a1 = (int) value; - int a2 = 0; - double x1 = 1; - double x2 = 0; - double y1 = value - a1; - double y2 = 0; - double delta1, delta2 = Double.MAX_VALUE; - double fraction; - int i = 1; - do { - delta1 = delta2; - a2 = (int) (x1 / y1); - x2 = y1; - y2 = x1 - a2 * y1; - numer2 = a1 * numer1 + numer0; - denom2 = a1 * denom1 + denom0; - fraction = (double) numer2 / (double) denom2; - delta2 = Math.abs(value - fraction); - a1 = a2; - x1 = x2; - y1 = y2; - numer0 = numer1; - denom0 = denom1; - numer1 = numer2; - denom1 = denom2; - i++; - } while (delta1 > delta2 && denom2 <= 10000 && denom2 > 0 && i < 25); - if (i == 25) { - throw new ArithmeticException("Unable to convert double to fraction"); - } - return getReducedFraction((numer0 + wholeNumber * denom0) * sign, denom0); - } - - /** - *

- * Creates a Fraction from a {@code String}. - *

- * - *

- * The formats accepted are: - *

- * - *
    - *
  1. {@code double} String containing a dot
  2. - *
  3. 'X Y/Z'
  4. - *
  5. 'Y/Z'
  6. - *
  7. 'X' (a simple whole number)
  8. - *
- *

- * and a . - *

- * - * @param str the string to parse, must not be {@code null} - * @return the new {@code Fraction} instance - * @throws NullPointerException if the string is {@code null} - * @throws NumberFormatException if the number format is invalid - */ - public static Fraction getFraction(String str) { - Validate.notNull(str, "str"); - // parse double format - int pos = str.indexOf('.'); - if (pos >= 0) { - return getFraction(Double.parseDouble(str)); - } - - // parse X Y/Z format - pos = str.indexOf(' '); - if (pos > 0) { - final int whole = Integer.parseInt(str.substring(0, pos)); - str = str.substring(pos + 1); - pos = str.indexOf('/'); - if (pos < 0) { - throw new NumberFormatException("The fraction could not be parsed as the format X Y/Z"); - } - final int numer = Integer.parseInt(str.substring(0, pos)); - final int denom = Integer.parseInt(str.substring(pos + 1)); - return getFraction(whole, numer, denom); - } - - // parse Y/Z format - pos = str.indexOf('/'); - if (pos < 0) { - // simple whole number - return getFraction(Integer.parseInt(str), 1); - } - final int numer = Integer.parseInt(str.substring(0, pos)); - final int denom = Integer.parseInt(str.substring(pos + 1)); - return getFraction(numer, denom); - } - - // Accessors - // ------------------------------------------------------------------- - - /** - *

- * Gets the numerator part of the fraction. - *

- * - *

- * This method may return a value greater than the denominator, an improper - * fraction, such as the seven in 7/4. - *

- * - * @return the numerator fraction part - */ - public int getNumerator() { - return numerator; - } - - /** - *

- * Gets the denominator part of the fraction. - *

- * - * @return the denominator fraction part - */ - public int getDenominator() { - return denominator; - } - - /** - *

- * Gets the proper numerator, always positive. - *

- * - *

- * An improper fraction 7/4 can be resolved into a proper one, 1 3/4. This - * method returns the 3 from the proper fraction. - *

- * - *

- * If the fraction is negative such as -7/4, it can be resolved into -1 3/4, so - * this method returns the positive proper numerator, 3. - *

- * - * @return the numerator fraction part of a proper fraction, always positive - */ - public int getProperNumerator() { - return Math.abs(numerator % denominator); - } - - /** - *

- * Gets the proper whole part of the fraction. - *

- * - *

- * An improper fraction 7/4 can be resolved into a proper one, 1 3/4. This - * method returns the 1 from the proper fraction. - *

- * - *

- * If the fraction is negative such as -7/4, it can be resolved into -1 3/4, so - * this method returns the positive whole part -1. - *

- * - * @return the whole fraction part of a proper fraction, that includes the sign - */ - public int getProperWhole() { - return numerator / denominator; - } - - // Number methods - // ------------------------------------------------------------------- - - /** - *

- * Gets the fraction as an {@code int}. This returns the whole number part of - * the fraction. - *

- * - * @return the whole number fraction part - */ - @Override - public int intValue() { - return numerator / denominator; - } - - /** - *

- * Gets the fraction as a {@code long}. This returns the whole number part of - * the fraction. - *

- * - * @return the whole number fraction part - */ - @Override - public long longValue() { - return (long) numerator / denominator; - } - - /** - *

- * Gets the fraction as a {@code float}. This calculates the fraction as the - * numerator divided by denominator. - *

- * - * @return the fraction as a {@code float} - */ - @Override - public float floatValue() { - return (float) numerator / (float) denominator; - } - - /** - *

- * Gets the fraction as a {@code double}. This calculates the fraction as the - * numerator divided by denominator. - *

- * - * @return the fraction as a {@code double} - */ - @Override - public double doubleValue() { - return (double) numerator / (double) denominator; - } - - // Calculations - // ------------------------------------------------------------------- - - /** - *

- * Reduce the fraction to the smallest values for the numerator and denominator, - * returning the result. - *

- * - *

- * For example, if this fraction represents 2/4, then the result will be 1/2. - *

- * - * @return a new reduced fraction instance, or this if no simplification - * possible - */ - public Fraction reduce() { - if (numerator == 0) { - return equals(ZERO) ? this : ZERO; - } - final int gcd = greatestCommonDivisor(Math.abs(numerator), denominator); - if (gcd == 1) { - return this; - } - return getFraction(numerator / gcd, denominator / gcd); - } - - /** - *

- * Gets a fraction that is the inverse (1/fraction) of this one. - *

- * - *

- * The returned fraction is not reduced. - *

- * - * @return a new fraction instance with the numerator and denominator inverted. - * @throws ArithmeticException if the fraction represents zero. - */ - public Fraction invert() { - if (numerator == 0) { - throw new ArithmeticException("Unable to invert zero."); - } - if (numerator == Integer.MIN_VALUE) { - throw new ArithmeticException("overflow: can't negate numerator"); - } - if (numerator < 0) { - return new Fraction(-denominator, -numerator); - } - return new Fraction(denominator, numerator); - } - - /** - *

- * Gets a fraction that is the negative (-fraction) of this one. - *

- * - *

- * The returned fraction is not reduced. - *

- * - * @return a new fraction instance with the opposite signed numerator - */ - public Fraction negate() { - // the positive range is one smaller than the negative range of an int. - if (numerator == Integer.MIN_VALUE) { - throw new ArithmeticException("overflow: too large to negate"); - } - return new Fraction(-numerator, denominator); - } - - /** - *

- * Gets a fraction that is the positive equivalent of this one. - *

- *

- * More precisely: {@code (fraction >= 0 ? this : -fraction)} - *

- * - *

- * The returned fraction is not reduced. - *

- * - * @return {@code this} if it is positive, or a new positive fraction instance - * with the opposite signed numerator - */ - public Fraction abs() { - if (numerator >= 0) { - return this; - } - return negate(); - } - - /** - *

- * Gets a fraction that is raised to the passed in power. - *

- * - *

- * The returned fraction is in reduced form. - *

- * - * @param power the power to raise the fraction to - * @return {@code this} if the power is one, {@code ONE} if the power is zero - * (even if the fraction equals ZERO) or a new fraction instance raised - * to the appropriate power - * @throws ArithmeticException if the resulting numerator or denominator exceeds - * {@code Integer.MAX_VALUE} - */ - public Fraction pow(final int power) { - if (power == 1) { - return this; - } else if (power == 0) { - return ONE; - } else if (power < 0) { - if (power == Integer.MIN_VALUE) { // MIN_VALUE can't be negated. - return this.invert().pow(2).pow(-(power / 2)); - } - return this.invert().pow(-power); - } else { - final Fraction f = this.multiplyBy(this); - if (power % 2 == 0) { // if even... - return f.pow(power / 2); - } - return f.pow(power / 2).multiplyBy(this); - } - } - - /** - *

- * Gets the greatest common divisor of the absolute value of two numbers, using - * the "binary gcd" method which avoids division and modulo operations. See - * Knuth 4.5.2 algorithm B. This algorithm is due to Josef Stein (1961). - *

- * - * @param u a non-zero number - * @param v a non-zero number - * @return the greatest common divisor, never zero - */ - private static int greatestCommonDivisor(int u, int v) { - // From Commons Math: - if (u == 0 || v == 0) { - if (u == Integer.MIN_VALUE || v == Integer.MIN_VALUE) { - throw new ArithmeticException("overflow: gcd is 2^31"); - } - return Math.abs(u) + Math.abs(v); - } - // if either operand is abs 1, return 1: - if (Math.abs(u) == 1 || Math.abs(v) == 1) { - return 1; - } - // keep u and v negative, as negative integers range down to - // -2^31, while positive numbers can only be as large as 2^31-1 - // (i.e. we can't necessarily negate a negative number without - // overflow) - if (u > 0) { - u = -u; - } // make u negative - if (v > 0) { - v = -v; - } // make v negative - // B1. [Find power of 2] - int k = 0; - while ((u & 1) == 0 && (v & 1) == 0 && k < 31) { // while u and v are both even... - u /= 2; - v /= 2; - k++; // cast out twos. - } - if (k == 31) { - throw new ArithmeticException("overflow: gcd is 2^31"); - } - // B2. Initialize: u and v have been divided by 2^k and at least - // one is odd. - int t = (u & 1) == 1 ? v : -(u / 2)/* B3 */; - // t negative: u was odd, v may be even (t replaces v) - // t positive: u was even, v is odd (t replaces u) - do { - /* assert u<0 && v<0; */ - // B4/B3: cast out twos from t. - while ((t & 1) == 0) { // while t is even.. - t /= 2; // cast out twos - } - // B5 [reset max(u,v)] - if (t > 0) { - u = -t; - } else { - v = t; - } - // B6/B3. at this point both u and v should be odd. - t = (v - u) / 2; - // |u| larger: t positive (replace u) - // |v| larger: t negative (replace v) - } while (t != 0); - return -u * (1 << k); // gcd is u*2^k - } - - // Arithmetic - // ------------------------------------------------------------------- - - /** - * Multiply two integers, checking for overflow. - * - * @param x a factor - * @param y a factor - * @return the product {@code x*y} - * @throws ArithmeticException if the result can not be represented as an int - */ - private static int mulAndCheck(final int x, final int y) { - final long m = (long) x * (long) y; - if (m < Integer.MIN_VALUE || m > Integer.MAX_VALUE) { - throw new ArithmeticException("overflow: mul"); - } - return (int) m; - } - - /** - * Multiply two non-negative integers, checking for overflow. - * - * @param x a non-negative factor - * @param y a non-negative factor - * @return the product {@code x*y} - * @throws ArithmeticException if the result can not be represented as an int - */ - private static int mulPosAndCheck(final int x, final int y) { - /* assert x>=0 && y>=0; */ - final long m = (long) x * (long) y; - if (m > Integer.MAX_VALUE) { - throw new ArithmeticException("overflow: mulPos"); - } - return (int) m; - } - - /** - * Add two integers, checking for overflow. - * - * @param x an addend - * @param y an addend - * @return the sum {@code x+y} - * @throws ArithmeticException if the result can not be represented as an int - */ - private static int addAndCheck(final int x, final int y) { - final long s = (long) x + (long) y; - if (s < Integer.MIN_VALUE || s > Integer.MAX_VALUE) { - throw new ArithmeticException("overflow: add"); - } - return (int) s; - } - - /** - * Subtract two integers, checking for overflow. - * - * @param x the minuend - * @param y the subtrahend - * @return the difference {@code x-y} - * @throws ArithmeticException if the result can not be represented as an int - */ - private static int subAndCheck(final int x, final int y) { - final long s = (long) x - (long) y; - if (s < Integer.MIN_VALUE || s > Integer.MAX_VALUE) { - throw new ArithmeticException("overflow: add"); - } - return (int) s; - } - - /** - *

- * Adds the value of this fraction to another, returning the result in reduced - * form. The algorithm follows Knuth, 4.5.1. - *

- * - * @param fraction the fraction to add, must not be {@code null} - * @return a {@code Fraction} instance with the resulting values - * @throws IllegalArgumentException if the fraction is {@code null} - * @throws ArithmeticException if the resulting numerator or denominator - * exceeds {@code Integer.MAX_VALUE} - */ - public Fraction add(final Fraction fraction) { - return addSub(fraction, true /* add */); - } - - /** - *

- * Subtracts the value of another fraction from the value of this one, returning - * the result in reduced form. - *

- * - * @param fraction the fraction to subtract, must not be {@code null} - * @return a {@code Fraction} instance with the resulting values - * @throws IllegalArgumentException if the fraction is {@code null} - * @throws ArithmeticException if the resulting numerator or denominator - * cannot be represented in an {@code int}. - */ - public Fraction subtract(final Fraction fraction) { - return addSub(fraction, false /* subtract */); - } - - /** - * Implement add and subtract using algorithm described in Knuth 4.5.1. - * - * @param fraction the fraction to subtract, must not be {@code null} - * @param isAdd true to add, false to subtract - * @return a {@code Fraction} instance with the resulting values - * @throws IllegalArgumentException if the fraction is {@code null} - * @throws ArithmeticException if the resulting numerator or denominator - * cannot be represented in an {@code int}. - */ - private Fraction addSub(final Fraction fraction, final boolean isAdd) { - Validate.notNull(fraction, "fraction"); - // zero is identity for addition. - if (numerator == 0) { - return isAdd ? fraction : fraction.negate(); - } - if (fraction.numerator == 0) { - return this; - } - // if denominators are randomly distributed, d1 will be 1 about 61% - // of the time. - final int d1 = greatestCommonDivisor(denominator, fraction.denominator); - if (d1 == 1) { - // result is ( (u*v' +/- u'v) / u'v') - final int uvp = mulAndCheck(numerator, fraction.denominator); - final int upv = mulAndCheck(fraction.numerator, denominator); - return new Fraction(isAdd ? addAndCheck(uvp, upv) : subAndCheck(uvp, upv), - mulPosAndCheck(denominator, fraction.denominator)); - } - // the quantity 't' requires 65 bits of precision; see knuth 4.5.1 - // exercise 7. we're going to use a BigInteger. - // t = u(v'/d1) +/- v(u'/d1) - final BigInteger uvp = BigInteger.valueOf(numerator).multiply(BigInteger.valueOf(fraction.denominator / d1)); - final BigInteger upv = BigInteger.valueOf(fraction.numerator).multiply(BigInteger.valueOf(denominator / d1)); - final BigInteger t = isAdd ? uvp.add(upv) : uvp.subtract(upv); - // but d2 doesn't need extra precision because - // d2 = gcd(t,d1) = gcd(t mod d1, d1) - final int tmodd1 = t.mod(BigInteger.valueOf(d1)).intValue(); - final int d2 = tmodd1 == 0 ? d1 : greatestCommonDivisor(tmodd1, d1); - - // result is (t/d2) / (u'/d1)(v'/d2) - final BigInteger w = t.divide(BigInteger.valueOf(d2)); - if (w.bitLength() > 31) { - throw new ArithmeticException("overflow: numerator too large after multiply"); - } - return new Fraction(w.intValue(), mulPosAndCheck(denominator / d1, fraction.denominator / d2)); - } - - /** - *

- * Multiplies the value of this fraction by another, returning the result in - * reduced form. - *

- * - * @param fraction the fraction to multiply by, must not be {@code null} - * @return a {@code Fraction} instance with the resulting values - * @throws NullPointerException if the fraction is {@code null} - * @throws ArithmeticException if the resulting numerator or denominator - * exceeds {@code Integer.MAX_VALUE} - */ - public Fraction multiplyBy(final Fraction fraction) { - Validate.notNull(fraction, "fraction"); - if (numerator == 0 || fraction.numerator == 0) { - return ZERO; - } - // knuth 4.5.1 - // make sure we don't overflow unless the result *must* overflow. - final int d1 = greatestCommonDivisor(numerator, fraction.denominator); - final int d2 = greatestCommonDivisor(fraction.numerator, denominator); - return getReducedFraction(mulAndCheck(numerator / d1, fraction.numerator / d2), - mulPosAndCheck(denominator / d2, fraction.denominator / d1)); - } - - /** - *

- * Divide the value of this fraction by another. - *

- * - * @param fraction the fraction to divide by, must not be {@code null} - * @return a {@code Fraction} instance with the resulting values - * @throws NullPointerException if the fraction is {@code null} - * @throws ArithmeticException if the fraction to divide by is zero - * @throws ArithmeticException if the resulting numerator or denominator - * exceeds {@code Integer.MAX_VALUE} - */ - public Fraction divideBy(final Fraction fraction) { - Validate.notNull(fraction, "fraction"); - if (fraction.numerator == 0) { - throw new ArithmeticException("The fraction to divide by must not be zero"); - } - return multiplyBy(fraction.invert()); - } - - // Basics - // ------------------------------------------------------------------- - - /** - *

- * Compares this fraction to another object to test if they are equal. - *

- * . - * - *

- * To be equal, both values must be equal. Thus 2/4 is not equal to 1/2. - *

- * - * @param obj the reference object with which to compare - * @return {@code true} if this object is equal - */ - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof Fraction)) { - return false; - } - final Fraction other = (Fraction) obj; - return getNumerator() == other.getNumerator() && getDenominator() == other.getDenominator(); - } - - /** - *

- * Gets a hashCode for the fraction. - *

- * - * @return a hash code value for this object - */ - @Override - public int hashCode() { - if (hashCode == 0) { - // hash code update should be atomic. - hashCode = 37 * (37 * 17 + getNumerator()) + getDenominator(); - } - return hashCode; - } - - /** - *

- * Compares this object to another based on size. - *

- * - *

- * Note: this class has a natural ordering that is inconsistent with equals, - * because, for example, equals treats 1/2 and 2/4 as different, whereas - * compareTo treats them as equal. - * - * @param other the object to compare to - * @return -1 if this is less, 0 if equal, +1 if greater - * @throws ClassCastException if the object is not a {@code Fraction} - * @throws NullPointerException if the object is {@code null} - */ - @Override - public int compareTo(final Fraction other) { - if (this == other) { - return 0; - } - if (numerator == other.numerator && denominator == other.denominator) { - return 0; - } - - // otherwise see which is less - final long first = (long) numerator * (long) other.denominator; - final long second = (long) other.numerator * (long) denominator; - return Long.compare(first, second); - } - - /** - *

- * Gets the fraction as a {@code String}. - *

- * - *

- * The format used is 'numerator/denominator' always. - * - * @return a {@code String} form of the fraction - */ - @Override - public String toString() { - if (toString == null) { - toString = getNumerator() + "/" + getDenominator(); - } - return toString; - } - - /** - *

- * Gets the fraction as a proper {@code String} in the format X Y/Z. - *

- * - *

- * The format used in 'wholeNumber numerator/denominator'. - * If the whole number is zero it will be omitted. If the numerator is zero, - * only the whole number is returned. - *

- * - * @return a {@code String} form of the fraction - */ - public String toProperString() { - if (toProperString == null) { - if (numerator == 0) { - toProperString = "0"; - } else if (numerator == denominator) { - toProperString = "1"; - } else if (numerator == -1 * denominator) { - toProperString = "-1"; - } else if ((numerator > 0 ? -numerator : numerator) < -denominator) { - // note that we do the magnitude comparison test above with - // NEGATIVE (not positive) numbers, since negative numbers - // have a larger range. otherwise numerator==Integer.MIN_VALUE - // is handled incorrectly. - final int properNumerator = getProperNumerator(); - if (properNumerator == 0) { - toProperString = Integer.toString(getProperWhole()); - } else { - toProperString = getProperWhole() + " " + properNumerator + "/" + getDenominator(); - } - } else { - toProperString = getNumerator() + "/" + getDenominator(); - } - } - return toProperString; - } -} diff --git a/src/main/java/org/apache/commons/lang3/math/IEEE754rUtils.java b/src/main/java/org/apache/commons/lang3/math/IEEE754rUtils.java deleted file mode 100755 index e62d19f7..00000000 --- a/src/main/java/org/apache/commons/lang3/math/IEEE754rUtils.java +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.math; - -import org.apache.commons.lang3.Validate; - -/** - *

- * Provides IEEE-754r variants of NumberUtils methods. - *

- * - *

- * See: http://en.wikipedia.org/wiki/IEEE_754r - *

- * - * @since 2.4 - */ -public class IEEE754rUtils { - - /** - *

- * Returns the minimum value in an array. - *

- * - * @param array an array, must not be null or empty - * @return the minimum value in the array - * @throws NullPointerException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty - * @since 3.4 Changed signature from min(double[]) to min(double...) - */ - public static double min(final double... array) { - Validate.notNull(array, "array"); - Validate.isTrue(array.length != 0, "Array cannot be empty."); - - // Finds and returns min - double min = array[0]; - for (int i = 1; i < array.length; i++) { - min = min(array[i], min); - } - - return min; - } - - /** - *

- * Returns the minimum value in an array. - *

- * - * @param array an array, must not be null or empty - * @return the minimum value in the array - * @throws NullPointerException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty - * @since 3.4 Changed signature from min(float[]) to min(float...) - */ - public static float min(final float... array) { - Validate.notNull(array, "array"); - Validate.isTrue(array.length != 0, "Array cannot be empty."); - - // Finds and returns min - float min = array[0]; - for (int i = 1; i < array.length; i++) { - min = min(array[i], min); - } - - return min; - } - - /** - *

- * Gets the minimum of three {@code double} values. - *

- * - *

- * NaN is only returned if all numbers are NaN as per IEEE-754r. - *

- * - * @param a value 1 - * @param b value 2 - * @param c value 3 - * @return the smallest of the values - */ - public static double min(final double a, final double b, final double c) { - return min(min(a, b), c); - } - - /** - *

- * Gets the minimum of two {@code double} values. - *

- * - *

- * NaN is only returned if all numbers are NaN as per IEEE-754r. - *

- * - * @param a value 1 - * @param b value 2 - * @return the smallest of the values - */ - public static double min(final double a, final double b) { - if (Double.isNaN(a)) { - return b; - } else if (Double.isNaN(b)) { - return a; - } else { - return Math.min(a, b); - } - } - - /** - *

- * Gets the minimum of three {@code float} values. - *

- * - *

- * NaN is only returned if all numbers are NaN as per IEEE-754r. - *

- * - * @param a value 1 - * @param b value 2 - * @param c value 3 - * @return the smallest of the values - */ - public static float min(final float a, final float b, final float c) { - return min(min(a, b), c); - } - - /** - *

- * Gets the minimum of two {@code float} values. - *

- * - *

- * NaN is only returned if all numbers are NaN as per IEEE-754r. - *

- * - * @param a value 1 - * @param b value 2 - * @return the smallest of the values - */ - public static float min(final float a, final float b) { - if (Float.isNaN(a)) { - return b; - } else if (Float.isNaN(b)) { - return a; - } else { - return Math.min(a, b); - } - } - - /** - *

- * Returns the maximum value in an array. - *

- * - * @param array an array, must not be null or empty - * @return the minimum value in the array - * @throws NullPointerException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty - * @since 3.4 Changed signature from max(double[]) to max(double...) - */ - public static double max(final double... array) { - Validate.notNull(array, "array"); - Validate.isTrue(array.length != 0, "Array cannot be empty."); - - // Finds and returns max - double max = array[0]; - for (int j = 1; j < array.length; j++) { - max = max(array[j], max); - } - - return max; - } - - /** - *

- * Returns the maximum value in an array. - *

- * - * @param array an array, must not be null or empty - * @return the minimum value in the array - * @throws NullPointerException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty - * @since 3.4 Changed signature from max(float[]) to max(float...) - */ - public static float max(final float... array) { - Validate.notNull(array, "array"); - Validate.isTrue(array.length != 0, "Array cannot be empty."); - - // Finds and returns max - float max = array[0]; - for (int j = 1; j < array.length; j++) { - max = max(array[j], max); - } - - return max; - } - - /** - *

- * Gets the maximum of three {@code double} values. - *

- * - *

- * NaN is only returned if all numbers are NaN as per IEEE-754r. - *

- * - * @param a value 1 - * @param b value 2 - * @param c value 3 - * @return the largest of the values - */ - public static double max(final double a, final double b, final double c) { - return max(max(a, b), c); - } - - /** - *

- * Gets the maximum of two {@code double} values. - *

- * - *

- * NaN is only returned if all numbers are NaN as per IEEE-754r. - *

- * - * @param a value 1 - * @param b value 2 - * @return the largest of the values - */ - public static double max(final double a, final double b) { - if (Double.isNaN(a)) { - return b; - } else if (Double.isNaN(b)) { - return a; - } else { - return Math.max(a, b); - } - } - - /** - *

- * Gets the maximum of three {@code float} values. - *

- * - *

- * NaN is only returned if all numbers are NaN as per IEEE-754r. - *

- * - * @param a value 1 - * @param b value 2 - * @param c value 3 - * @return the largest of the values - */ - public static float max(final float a, final float b, final float c) { - return max(max(a, b), c); - } - - /** - *

- * Gets the maximum of two {@code float} values. - *

- * - *

- * NaN is only returned if all numbers are NaN as per IEEE-754r. - *

- * - * @param a value 1 - * @param b value 2 - * @return the largest of the values - */ - public static float max(final float a, final float b) { - if (Float.isNaN(a)) { - return b; - } else if (Float.isNaN(b)) { - return a; - } else { - return Math.max(a, b); - } - } - -} diff --git a/src/main/java/org/apache/commons/lang3/math/NumberUtils.java b/src/main/java/org/apache/commons/lang3/math/NumberUtils.java deleted file mode 100755 index d5272d21..00000000 --- a/src/main/java/org/apache/commons/lang3/math/NumberUtils.java +++ /dev/null @@ -1,2027 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.math; - -import java.lang.reflect.Array; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.math.RoundingMode; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Validate; - -/** - *

- * Provides extra functionality for Java Number classes. - *

- * - * @since 2.0 - */ -public class NumberUtils { - - /** Reusable Long constant for zero. */ - public static final Long LONG_ZERO = Long.valueOf(0L); - /** Reusable Long constant for one. */ - public static final Long LONG_ONE = Long.valueOf(1L); - /** Reusable Long constant for minus one. */ - public static final Long LONG_MINUS_ONE = Long.valueOf(-1L); - /** Reusable Integer constant for zero. */ - public static final Integer INTEGER_ZERO = Integer.valueOf(0); - /** Reusable Integer constant for one. */ - public static final Integer INTEGER_ONE = Integer.valueOf(1); - /** Reusable Integer constant for two */ - public static final Integer INTEGER_TWO = Integer.valueOf(2); - /** Reusable Integer constant for minus one. */ - public static final Integer INTEGER_MINUS_ONE = Integer.valueOf(-1); - /** Reusable Short constant for zero. */ - public static final Short SHORT_ZERO = Short.valueOf((short) 0); - /** Reusable Short constant for one. */ - public static final Short SHORT_ONE = Short.valueOf((short) 1); - /** Reusable Short constant for minus one. */ - public static final Short SHORT_MINUS_ONE = Short.valueOf((short) -1); - /** Reusable Byte constant for zero. */ - public static final Byte BYTE_ZERO = Byte.valueOf((byte) 0); - /** Reusable Byte constant for one. */ - public static final Byte BYTE_ONE = Byte.valueOf((byte) 1); - /** Reusable Byte constant for minus one. */ - public static final Byte BYTE_MINUS_ONE = Byte.valueOf((byte) -1); - /** Reusable Double constant for zero. */ - public static final Double DOUBLE_ZERO = Double.valueOf(0.0d); - /** Reusable Double constant for one. */ - public static final Double DOUBLE_ONE = Double.valueOf(1.0d); - /** Reusable Double constant for minus one. */ - public static final Double DOUBLE_MINUS_ONE = Double.valueOf(-1.0d); - /** Reusable Float constant for zero. */ - public static final Float FLOAT_ZERO = Float.valueOf(0.0f); - /** Reusable Float constant for one. */ - public static final Float FLOAT_ONE = Float.valueOf(1.0f); - /** Reusable Float constant for minus one. */ - public static final Float FLOAT_MINUS_ONE = Float.valueOf(-1.0f); - - /** - * {@link Integer#MAX_VALUE} as a {@link Long}. - * - * @since 3.12.0 - */ - public static final Long LONG_INT_MAX_VALUE = Long.valueOf(Integer.MAX_VALUE); - - /** - * {@link Integer#MIN_VALUE} as a {@link Long}. - * - * @since 3.12.0 - */ - public static final Long LONG_INT_MIN_VALUE = Long.valueOf(Integer.MIN_VALUE); - - /** - *

- * {@code NumberUtils} instances should NOT be constructed in standard - * programming. Instead, the class should be used as - * {@code NumberUtils.toInt("6");}. - *

- * - *

- * This constructor is public to permit tools that require a JavaBean instance - * to operate. - *

- */ - public NumberUtils() { - } - - // ----------------------------------------------------------------------- - /** - *

- * Convert a {@code String} to an {@code int}, returning {@code zero} if the - * conversion fails. - *

- * - *

- * If the string is {@code null}, {@code zero} is returned. - *

- * - *
-	 *   NumberUtils.toInt(null) = 0
-	 *   NumberUtils.toInt("")   = 0
-	 *   NumberUtils.toInt("1")  = 1
-	 * 
- * - * @param str the string to convert, may be null - * @return the int represented by the string, or {@code zero} if conversion - * fails - * @since 2.1 - */ - public static int toInt(final String str) { - return toInt(str, 0); - } - - /** - *

- * Convert a {@code String} to an {@code int}, returning a default value if the - * conversion fails. - *

- * - *

- * If the string is {@code null}, the default value is returned. - *

- * - *
-	 *   NumberUtils.toInt(null, 1) = 1
-	 *   NumberUtils.toInt("", 1)   = 1
-	 *   NumberUtils.toInt("1", 0)  = 1
-	 * 
- * - * @param str the string to convert, may be null - * @param defaultValue the default value - * @return the int represented by the string, or the default if conversion fails - * @since 2.1 - */ - public static int toInt(final String str, final int defaultValue) { - if (str == null) { - return defaultValue; - } - try { - return Integer.parseInt(str); - } catch (final NumberFormatException nfe) { - return defaultValue; - } - } - - /** - *

- * Convert a {@code String} to a {@code long}, returning {@code zero} if the - * conversion fails. - *

- * - *

- * If the string is {@code null}, {@code zero} is returned. - *

- * - *
-	 *   NumberUtils.toLong(null) = 0L
-	 *   NumberUtils.toLong("")   = 0L
-	 *   NumberUtils.toLong("1")  = 1L
-	 * 
- * - * @param str the string to convert, may be null - * @return the long represented by the string, or {@code 0} if conversion fails - * @since 2.1 - */ - public static long toLong(final String str) { - return toLong(str, 0L); - } - - /** - *

- * Convert a {@code String} to a {@code long}, returning a default value if the - * conversion fails. - *

- * - *

- * If the string is {@code null}, the default value is returned. - *

- * - *
-	 *   NumberUtils.toLong(null, 1L) = 1L
-	 *   NumberUtils.toLong("", 1L)   = 1L
-	 *   NumberUtils.toLong("1", 0L)  = 1L
-	 * 
- * - * @param str the string to convert, may be null - * @param defaultValue the default value - * @return the long represented by the string, or the default if conversion - * fails - * @since 2.1 - */ - public static long toLong(final String str, final long defaultValue) { - if (str == null) { - return defaultValue; - } - try { - return Long.parseLong(str); - } catch (final NumberFormatException nfe) { - return defaultValue; - } - } - - /** - *

- * Convert a {@code String} to a {@code float}, returning {@code 0.0f} if the - * conversion fails. - *

- * - *

- * If the string {@code str} is {@code null}, {@code 0.0f} is returned. - *

- * - *
-	 *   NumberUtils.toFloat(null)   = 0.0f
-	 *   NumberUtils.toFloat("")     = 0.0f
-	 *   NumberUtils.toFloat("1.5")  = 1.5f
-	 * 
- * - * @param str the string to convert, may be {@code null} - * @return the float represented by the string, or {@code 0.0f} if conversion - * fails - * @since 2.1 - */ - public static float toFloat(final String str) { - return toFloat(str, 0.0f); - } - - /** - *

- * Convert a {@code String} to a {@code float}, returning a default value if the - * conversion fails. - *

- * - *

- * If the string {@code str} is {@code null}, the default value is returned. - *

- * - *
-	 *   NumberUtils.toFloat(null, 1.1f)   = 1.0f
-	 *   NumberUtils.toFloat("", 1.1f)     = 1.1f
-	 *   NumberUtils.toFloat("1.5", 0.0f)  = 1.5f
-	 * 
- * - * @param str the string to convert, may be {@code null} - * @param defaultValue the default value - * @return the float represented by the string, or defaultValue if conversion - * fails - * @since 2.1 - */ - public static float toFloat(final String str, final float defaultValue) { - if (str == null) { - return defaultValue; - } - try { - return Float.parseFloat(str); - } catch (final NumberFormatException nfe) { - return defaultValue; - } - } - - /** - *

- * Convert a {@code String} to a {@code double}, returning {@code 0.0d} if the - * conversion fails. - *

- * - *

- * If the string {@code str} is {@code null}, {@code 0.0d} is returned. - *

- * - *
-	 *   NumberUtils.toDouble(null)   = 0.0d
-	 *   NumberUtils.toDouble("")     = 0.0d
-	 *   NumberUtils.toDouble("1.5")  = 1.5d
-	 * 
- * - * @param str the string to convert, may be {@code null} - * @return the double represented by the string, or {@code 0.0d} if conversion - * fails - * @since 2.1 - */ - public static double toDouble(final String str) { - return toDouble(str, 0.0d); - } - - /** - *

- * Convert a {@code String} to a {@code double}, returning a default value if - * the conversion fails. - *

- * - *

- * If the string {@code str} is {@code null}, the default value is returned. - *

- * - *
-	 *   NumberUtils.toDouble(null, 1.1d)   = 1.1d
-	 *   NumberUtils.toDouble("", 1.1d)     = 1.1d
-	 *   NumberUtils.toDouble("1.5", 0.0d)  = 1.5d
-	 * 
- * - * @param str the string to convert, may be {@code null} - * @param defaultValue the default value - * @return the double represented by the string, or defaultValue if conversion - * fails - * @since 2.1 - */ - public static double toDouble(final String str, final double defaultValue) { - if (str == null) { - return defaultValue; - } - try { - return Double.parseDouble(str); - } catch (final NumberFormatException nfe) { - return defaultValue; - } - } - - /** - *

- * Convert a {@code BigDecimal} to a {@code double}. - *

- * - *

- * If the {@code BigDecimal} {@code value} is {@code null}, then the specified - * default value is returned. - *

- * - *
-	 *   NumberUtils.toDouble(null)                     = 0.0d
-	 *   NumberUtils.toDouble(BigDecimal.valudOf(8.5d)) = 8.5d
-	 * 
- * - * @param value the {@code BigDecimal} to convert, may be {@code null}. - * @return the double represented by the {@code BigDecimal} or {@code 0.0d} if - * the {@code BigDecimal} is {@code null}. - * @since 3.8 - */ - public static double toDouble(final BigDecimal value) { - return toDouble(value, 0.0d); - } - - /** - *

- * Convert a {@code BigDecimal} to a {@code double}. - *

- * - *

- * If the {@code BigDecimal} {@code value} is {@code null}, then the specified - * default value is returned. - *

- * - *
-	 *   NumberUtils.toDouble(null, 1.1d)                     = 1.1d
-	 *   NumberUtils.toDouble(BigDecimal.valudOf(8.5d), 1.1d) = 8.5d
-	 * 
- * - * @param value the {@code BigDecimal} to convert, may be {@code null}. - * @param defaultValue the default value - * @return the double represented by the {@code BigDecimal} or the defaultValue - * if the {@code BigDecimal} is {@code null}. - * @since 3.8 - */ - public static double toDouble(final BigDecimal value, final double defaultValue) { - return value == null ? defaultValue : value.doubleValue(); - } - - // ----------------------------------------------------------------------- - /** - *

- * Convert a {@code String} to a {@code byte}, returning {@code zero} if the - * conversion fails. - *

- * - *

- * If the string is {@code null}, {@code zero} is returned. - *

- * - *
-	 *   NumberUtils.toByte(null) = 0
-	 *   NumberUtils.toByte("")   = 0
-	 *   NumberUtils.toByte("1")  = 1
-	 * 
- * - * @param str the string to convert, may be null - * @return the byte represented by the string, or {@code zero} if conversion - * fails - * @since 2.5 - */ - public static byte toByte(final String str) { - return toByte(str, (byte) 0); - } - - /** - *

- * Convert a {@code String} to a {@code byte}, returning a default value if the - * conversion fails. - *

- * - *

- * If the string is {@code null}, the default value is returned. - *

- * - *
-	 *   NumberUtils.toByte(null, 1) = 1
-	 *   NumberUtils.toByte("", 1)   = 1
-	 *   NumberUtils.toByte("1", 0)  = 1
-	 * 
- * - * @param str the string to convert, may be null - * @param defaultValue the default value - * @return the byte represented by the string, or the default if conversion - * fails - * @since 2.5 - */ - public static byte toByte(final String str, final byte defaultValue) { - if (str == null) { - return defaultValue; - } - try { - return Byte.parseByte(str); - } catch (final NumberFormatException nfe) { - return defaultValue; - } - } - - /** - *

- * Convert a {@code String} to a {@code short}, returning {@code zero} if the - * conversion fails. - *

- * - *

- * If the string is {@code null}, {@code zero} is returned. - *

- * - *
-	 *   NumberUtils.toShort(null) = 0
-	 *   NumberUtils.toShort("")   = 0
-	 *   NumberUtils.toShort("1")  = 1
-	 * 
- * - * @param str the string to convert, may be null - * @return the short represented by the string, or {@code zero} if conversion - * fails - * @since 2.5 - */ - public static short toShort(final String str) { - return toShort(str, (short) 0); - } - - /** - *

- * Convert a {@code String} to an {@code short}, returning a default value if - * the conversion fails. - *

- * - *

- * If the string is {@code null}, the default value is returned. - *

- * - *
-	 *   NumberUtils.toShort(null, 1) = 1
-	 *   NumberUtils.toShort("", 1)   = 1
-	 *   NumberUtils.toShort("1", 0)  = 1
-	 * 
- * - * @param str the string to convert, may be null - * @param defaultValue the default value - * @return the short represented by the string, or the default if conversion - * fails - * @since 2.5 - */ - public static short toShort(final String str, final short defaultValue) { - if (str == null) { - return defaultValue; - } - try { - return Short.parseShort(str); - } catch (final NumberFormatException nfe) { - return defaultValue; - } - } - - /** - * Convert a {@code BigDecimal} to a {@code BigDecimal} with a scale of two that - * has been rounded using {@code RoundingMode.HALF_EVEN}. If the supplied - * {@code value} is null, then {@code BigDecimal.ZERO} is returned. - * - *

- * Note, the scale of a {@code BigDecimal} is the number of digits to the right - * of the decimal point. - *

- * - * @param value the {@code BigDecimal} to convert, may be null. - * @return the scaled, with appropriate rounding, {@code BigDecimal}. - * @since 3.8 - */ - public static BigDecimal toScaledBigDecimal(final BigDecimal value) { - return toScaledBigDecimal(value, INTEGER_TWO, RoundingMode.HALF_EVEN); - } - - /** - * Convert a {@code BigDecimal} to a {@code BigDecimal} whose scale is the - * specified value with a {@code RoundingMode} applied. If the input - * {@code value} is {@code null}, we simply return {@code BigDecimal.ZERO}. - * - * @param value the {@code BigDecimal} to convert, may be null. - * @param scale the number of digits to the right of the decimal point. - * @param roundingMode a rounding behavior for numerical operations capable of - * discarding precision. - * @return the scaled, with appropriate rounding, {@code BigDecimal}. - * @since 3.8 - */ - public static BigDecimal toScaledBigDecimal(final BigDecimal value, final int scale, - final RoundingMode roundingMode) { - if (value == null) { - return BigDecimal.ZERO; - } - return value.setScale(scale, (roundingMode == null) ? RoundingMode.HALF_EVEN : roundingMode); - } - - /** - * Convert a {@code Float} to a {@code BigDecimal} with a scale of two that has - * been rounded using {@code RoundingMode.HALF_EVEN}. If the supplied - * {@code value} is null, then {@code BigDecimal.ZERO} is returned. - * - *

- * Note, the scale of a {@code BigDecimal} is the number of digits to the right - * of the decimal point. - *

- * - * @param value the {@code Float} to convert, may be null. - * @return the scaled, with appropriate rounding, {@code BigDecimal}. - * @since 3.8 - */ - public static BigDecimal toScaledBigDecimal(final Float value) { - return toScaledBigDecimal(value, INTEGER_TWO, RoundingMode.HALF_EVEN); - } - - /** - * Convert a {@code Float} to a {@code BigDecimal} whose scale is the specified - * value with a {@code RoundingMode} applied. If the input {@code value} is - * {@code null}, we simply return {@code BigDecimal.ZERO}. - * - * @param value the {@code Float} to convert, may be null. - * @param scale the number of digits to the right of the decimal point. - * @param roundingMode a rounding behavior for numerical operations capable of - * discarding precision. - * @return the scaled, with appropriate rounding, {@code BigDecimal}. - * @since 3.8 - */ - public static BigDecimal toScaledBigDecimal(final Float value, final int scale, final RoundingMode roundingMode) { - if (value == null) { - return BigDecimal.ZERO; - } - return toScaledBigDecimal(BigDecimal.valueOf(value), scale, roundingMode); - } - - /** - * Convert a {@code Double} to a {@code BigDecimal} with a scale of two that has - * been rounded using {@code RoundingMode.HALF_EVEN}. If the supplied - * {@code value} is null, then {@code BigDecimal.ZERO} is returned. - * - *

- * Note, the scale of a {@code BigDecimal} is the number of digits to the right - * of the decimal point. - *

- * - * @param value the {@code Double} to convert, may be null. - * @return the scaled, with appropriate rounding, {@code BigDecimal}. - * @since 3.8 - */ - public static BigDecimal toScaledBigDecimal(final Double value) { - return toScaledBigDecimal(value, INTEGER_TWO, RoundingMode.HALF_EVEN); - } - - /** - * Convert a {@code Double} to a {@code BigDecimal} whose scale is the specified - * value with a {@code RoundingMode} applied. If the input {@code value} is - * {@code null}, we simply return {@code BigDecimal.ZERO}. - * - * @param value the {@code Double} to convert, may be null. - * @param scale the number of digits to the right of the decimal point. - * @param roundingMode a rounding behavior for numerical operations capable of - * discarding precision. - * @return the scaled, with appropriate rounding, {@code BigDecimal}. - * @since 3.8 - */ - public static BigDecimal toScaledBigDecimal(final Double value, final int scale, final RoundingMode roundingMode) { - if (value == null) { - return BigDecimal.ZERO; - } - return toScaledBigDecimal(BigDecimal.valueOf(value), scale, roundingMode); - } - - /** - * Convert a {@code String} to a {@code BigDecimal} with a scale of two that has - * been rounded using {@code RoundingMode.HALF_EVEN}. If the supplied - * {@code value} is null, then {@code BigDecimal.ZERO} is returned. - * - *

- * Note, the scale of a {@code BigDecimal} is the number of digits to the right - * of the decimal point. - *

- * - * @param value the {@code String} to convert, may be null. - * @return the scaled, with appropriate rounding, {@code BigDecimal}. - * @since 3.8 - */ - public static BigDecimal toScaledBigDecimal(final String value) { - return toScaledBigDecimal(value, INTEGER_TWO, RoundingMode.HALF_EVEN); - } - - /** - * Convert a {@code String} to a {@code BigDecimal} whose scale is the specified - * value with a {@code RoundingMode} applied. If the input {@code value} is - * {@code null}, we simply return {@code BigDecimal.ZERO}. - * - * @param value the {@code String} to convert, may be null. - * @param scale the number of digits to the right of the decimal point. - * @param roundingMode a rounding behavior for numerical operations capable of - * discarding precision. - * @return the scaled, with appropriate rounding, {@code BigDecimal}. - * @since 3.8 - */ - public static BigDecimal toScaledBigDecimal(final String value, final int scale, final RoundingMode roundingMode) { - if (value == null) { - return BigDecimal.ZERO; - } - return toScaledBigDecimal(createBigDecimal(value), scale, roundingMode); - } - - // ----------------------------------------------------------------------- - // must handle Long, Float, Integer, Float, Short, - // BigDecimal, BigInteger and Byte - // useful methods: - // Byte.decode(String) - // Byte.valueOf(String, int radix) - // Byte.valueOf(String) - // Double.valueOf(String) - // Float.valueOf(String) - // Float.valueOf(String) - // Integer.valueOf(String, int radix) - // Integer.valueOf(String) - // Integer.decode(String) - // Integer.getInteger(String) - // Integer.getInteger(String, int val) - // Integer.getInteger(String, Integer val) - // Integer.valueOf(String) - // Double.valueOf(String) - // new Byte(String) - // Long.valueOf(String) - // Long.getLong(String) - // Long.getLong(String, int) - // Long.getLong(String, Integer) - // Long.valueOf(String, int) - // Long.valueOf(String) - // Short.valueOf(String) - // Short.decode(String) - // Short.valueOf(String, int) - // Short.valueOf(String) - // new BigDecimal(String) - // new BigInteger(String) - // new BigInteger(String, int radix) - // Possible inputs: - // 45 45.5 45E7 4.5E7 Hex Oct Binary xxxF xxxD xxxf xxxd - // plus minus everything. Prolly more. A lot are not separable. - - /** - *

- * Turns a string value into a java.lang.Number. - *

- * - *

- * If the string starts with {@code 0x} or {@code -0x} (lower or upper case) or - * {@code #} or {@code -#}, it will be interpreted as a hexadecimal Integer - or - * Long, if the number of digits after the prefix is more than 8 - or BigInteger - * if there are more than 16 digits. - *

- *

- * Then, the value is examined for a type qualifier on the end, i.e. one of - * {@code 'f', 'F', 'd', 'D', 'l', 'L'}. If it is found, it starts trying to - * create successively larger types from the type specified until one is found - * that can represent the value. - *

- * - *

- * If a type specifier is not found, it will check for a decimal point and then - * try successively larger types from {@code Integer} to {@code BigInteger} and - * from {@code Float} to {@code BigDecimal}. - *

- * - *

- * Integral values with a leading {@code 0} will be interpreted as octal; the - * returned number will be Integer, Long or BigDecimal as appropriate. - *

- * - *

- * Returns {@code null} if the string is {@code null}. - *

- * - *

- * This method does not trim the input string, i.e., strings with leading or - * trailing spaces will generate NumberFormatExceptions. - *

- * - * @param str String containing a number, may be null - * @return Number created from the string (or null if the input is null) - * @throws NumberFormatException if the value cannot be converted - */ - public static Number createNumber(final String str) { - if (str == null) { - return null; - } - if (StringUtils.isBlank(str)) { - throw new NumberFormatException("A blank string is not a valid number"); - } - // Need to deal with all possible hex prefixes here - final String[] hex_prefixes = { "0x", "0X", "-0x", "-0X", "#", "-#" }; - final int length = str.length(); - int pfxLen = 0; - for (final String pfx : hex_prefixes) { - if (str.startsWith(pfx)) { - pfxLen += pfx.length(); - break; - } - } - if (pfxLen > 0) { // we have a hex number - char firstSigDigit = 0; // strip leading zeroes - for (int i = pfxLen; i < length; i++) { - firstSigDigit = str.charAt(i); - if (firstSigDigit == '0') { // count leading zeroes - pfxLen++; - } else { - break; - } - } - final int hexDigits = length - pfxLen; - if (hexDigits > 16 || hexDigits == 16 && firstSigDigit > '7') { // too many for Long - return createBigInteger(str); - } - if (hexDigits > 8 || hexDigits == 8 && firstSigDigit > '7') { // too many for an int - return createLong(str); - } - return createInteger(str); - } - final char lastChar = str.charAt(length - 1); - final String mant; - final String dec; - final String exp; - final int decPos = str.indexOf('.'); - final int expPos = str.indexOf('e') + str.indexOf('E') + 1; // assumes both not present - // if both e and E are present, this is caught by the checks on expPos (which - // prevent IOOBE) - // and the parsing which will detect if e or E appear in a number due to using - // the wrong offset - - if (decPos > -1) { // there is a decimal point - if (expPos > -1) { // there is an exponent - if (expPos < decPos || expPos > length) { // prevents double exponent causing IOOBE - throw new NumberFormatException(str + " is not a valid number."); - } - dec = str.substring(decPos + 1, expPos); - } else { - dec = str.substring(decPos + 1); - } - mant = getMantissa(str, decPos); - } else { - if (expPos > -1) { - if (expPos > length) { // prevents double exponent causing IOOBE - throw new NumberFormatException(str + " is not a valid number."); - } - mant = getMantissa(str, expPos); - } else { - mant = getMantissa(str); - } - dec = null; - } - if (!Character.isDigit(lastChar) && lastChar != '.') { - if (expPos > -1 && expPos < length - 1) { - exp = str.substring(expPos + 1, length - 1); - } else { - exp = null; - } - // Requesting a specific type.. - final String numeric = str.substring(0, length - 1); - final boolean allZeros = isAllZeros(mant) && isAllZeros(exp); - switch (lastChar) { - case 'l': - case 'L': - if (dec == null && exp == null - && (!numeric.isEmpty() && numeric.charAt(0) == '-' && isDigits(numeric.substring(1)) - || isDigits(numeric))) { - try { - return createLong(numeric); - } catch (final NumberFormatException nfe) { // NOPMD - // Too big for a long - } - return createBigInteger(numeric); - - } - throw new NumberFormatException(str + " is not a valid number."); - case 'f': - case 'F': - try { - final Float f = createFloat(str); - if (!(f.isInfinite() || f.floatValue() == 0.0F && !allZeros)) { - // If it's too big for a float or the float value = 0 and the string - // has non-zeros in it, then float does not have the precision we want - return f; - } - - } catch (final NumberFormatException nfe) { // NOPMD - // ignore the bad number - } - //$FALL-THROUGH$ - case 'd': - case 'D': - try { - final Double d = createDouble(str); - if (!(d.isInfinite() || d.doubleValue() == 0.0D && !allZeros)) { - return d; - } - } catch (final NumberFormatException nfe) { // NOPMD - // ignore the bad number - } - try { - return createBigDecimal(numeric); - } catch (final NumberFormatException e) { // NOPMD - // ignore the bad number - } - //$FALL-THROUGH$ - default: - throw new NumberFormatException(str + " is not a valid number."); - - } - } - // User doesn't have a preference on the return type, so let's start - // small and go from there... - if (expPos > -1 && expPos < length - 1) { - exp = str.substring(expPos + 1); - } else { - exp = null; - } - if (dec == null && exp == null) { // no decimal point and no exponent - // Must be an Integer, Long, Biginteger - try { - return createInteger(str); - } catch (final NumberFormatException nfe) { // NOPMD - // ignore the bad number - } - try { - return createLong(str); - } catch (final NumberFormatException nfe) { // NOPMD - // ignore the bad number - } - return createBigInteger(str); - } - - // Must be a Float, Double, BigDecimal - final boolean allZeros = isAllZeros(mant) && isAllZeros(exp); - try { - final Float f = createFloat(str); - final Double d = createDouble(str); - if (!f.isInfinite() && !(f.floatValue() == 0.0F && !allZeros) && f.toString().equals(d.toString())) { - return f; - } - if (!d.isInfinite() && !(d.doubleValue() == 0.0D && !allZeros)) { - final BigDecimal b = createBigDecimal(str); - if (b.compareTo(BigDecimal.valueOf(d.doubleValue())) == 0) { - return d; - } - return b; - } - } catch (final NumberFormatException nfe) { // NOPMD - // ignore the bad number - } - return createBigDecimal(str); - } - - /** - *

- * Utility method for {@link #createNumber(java.lang.String)}. - *

- * - *

- * Returns mantissa of the given number. - *

- * - * @param str the string representation of the number - * @return mantissa of the given number - */ - private static String getMantissa(final String str) { - return getMantissa(str, str.length()); - } - - /** - *

- * Utility method for {@link #createNumber(java.lang.String)}. - *

- * - *

- * Returns mantissa of the given number. - *

- * - * @param str the string representation of the number - * @param stopPos the position of the exponent or decimal point - * @return mantissa of the given number - */ - private static String getMantissa(final String str, final int stopPos) { - final char firstChar = str.charAt(0); - final boolean hasSign = firstChar == '-' || firstChar == '+'; - - return hasSign ? str.substring(1, stopPos) : str.substring(0, stopPos); - } - - /** - *

- * Utility method for {@link #createNumber(java.lang.String)}. - *

- * - *

- * Returns {@code true} if s is {@code null}. - *

- * - * @param str the String to check - * @return if it is all zeros or {@code null} - */ - private static boolean isAllZeros(final String str) { - if (str == null) { - return true; - } - for (int i = str.length() - 1; i >= 0; i--) { - if (str.charAt(i) != '0') { - return false; - } - } - return !str.isEmpty(); - } - - // ----------------------------------------------------------------------- - /** - *

- * Convert a {@code String} to a {@code Float}. - *

- * - *

- * Returns {@code null} if the string is {@code null}. - *

- * - * @param str a {@code String} to convert, may be null - * @return converted {@code Float} (or null if the input is null) - * @throws NumberFormatException if the value cannot be converted - */ - public static Float createFloat(final String str) { - if (str == null) { - return null; - } - return Float.valueOf(str); - } - - /** - *

- * Convert a {@code String} to a {@code Double}. - *

- * - *

- * Returns {@code null} if the string is {@code null}. - *

- * - * @param str a {@code String} to convert, may be null - * @return converted {@code Double} (or null if the input is null) - * @throws NumberFormatException if the value cannot be converted - */ - public static Double createDouble(final String str) { - if (str == null) { - return null; - } - return Double.valueOf(str); - } - - /** - *

- * Convert a {@code String} to a {@code Integer}, handling hex (0xhhhh) and - * octal (0dddd) notations. N.B. a leading zero means octal; spaces are not - * trimmed. - *

- * - *

- * Returns {@code null} if the string is {@code null}. - *

- * - * @param str a {@code String} to convert, may be null - * @return converted {@code Integer} (or null if the input is null) - * @throws NumberFormatException if the value cannot be converted - */ - public static Integer createInteger(final String str) { - if (str == null) { - return null; - } - // decode() handles 0xAABD and 0777 (hex and octal) as well. - return Integer.decode(str); - } - - /** - *

- * Convert a {@code String} to a {@code Long}; since 3.1 it handles hex (0Xhhhh) - * and octal (0ddd) notations. N.B. a leading zero means octal; spaces are not - * trimmed. - *

- * - *

- * Returns {@code null} if the string is {@code null}. - *

- * - * @param str a {@code String} to convert, may be null - * @return converted {@code Long} (or null if the input is null) - * @throws NumberFormatException if the value cannot be converted - */ - public static Long createLong(final String str) { - if (str == null) { - return null; - } - return Long.decode(str); - } - - /** - *

- * Convert a {@code String} to a {@code BigInteger}; since 3.2 it handles hex - * (0x or #) and octal (0) notations. - *

- * - *

- * Returns {@code null} if the string is {@code null}. - *

- * - * @param str a {@code String} to convert, may be null - * @return converted {@code BigInteger} (or null if the input is null) - * @throws NumberFormatException if the value cannot be converted - */ - public static BigInteger createBigInteger(final String str) { - if (str == null) { - return null; - } - int pos = 0; // offset within string - int radix = 10; - boolean negate = false; // need to negate later? - if (str.startsWith("-")) { - negate = true; - pos = 1; - } - if (str.startsWith("0x", pos) || str.startsWith("0X", pos)) { // hex - radix = 16; - pos += 2; - } else if (str.startsWith("#", pos)) { // alternative hex (allowed by Long/Integer) - radix = 16; - pos++; - } else if (str.startsWith("0", pos) && str.length() > pos + 1) { // octal; so long as there are additional - // digits - radix = 8; - pos++; - } // default is to treat as decimal - - final BigInteger value = new BigInteger(str.substring(pos), radix); - return negate ? value.negate() : value; - } - - /** - *

- * Convert a {@code String} to a {@code BigDecimal}. - *

- * - *

- * Returns {@code null} if the string is {@code null}. - *

- * - * @param str a {@code String} to convert, may be null - * @return converted {@code BigDecimal} (or null if the input is null) - * @throws NumberFormatException if the value cannot be converted - */ - public static BigDecimal createBigDecimal(final String str) { - if (str == null) { - return null; - } - // handle JDK1.3.1 bug where "" throws IndexOutOfBoundsException - if (StringUtils.isBlank(str)) { - throw new NumberFormatException("A blank string is not a valid number"); - } - return new BigDecimal(str); - } - - // Min in array - // -------------------------------------------------------------------- - /** - *

- * Returns the minimum value in an array. - *

- * - * @param array an array, must not be null or empty - * @return the minimum value in the array - * @throws IllegalArgumentException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty - * @since 3.4 Changed signature from min(long[]) to min(long...) - */ - public static long min(final long... array) { - // Validates input - validateArray(array); - - // Finds and returns min - long min = array[0]; - for (int i = 1; i < array.length; i++) { - if (array[i] < min) { - min = array[i]; - } - } - - return min; - } - - /** - *

- * Returns the minimum value in an array. - *

- * - * @param array an array, must not be null or empty - * @return the minimum value in the array - * @throws IllegalArgumentException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty - * @since 3.4 Changed signature from min(int[]) to min(int...) - */ - public static int min(final int... array) { - // Validates input - validateArray(array); - - // Finds and returns min - int min = array[0]; - for (int j = 1; j < array.length; j++) { - if (array[j] < min) { - min = array[j]; - } - } - - return min; - } - - /** - *

- * Returns the minimum value in an array. - *

- * - * @param array an array, must not be null or empty - * @return the minimum value in the array - * @throws IllegalArgumentException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty - * @since 3.4 Changed signature from min(short[]) to min(short...) - */ - public static short min(final short... array) { - // Validates input - validateArray(array); - - // Finds and returns min - short min = array[0]; - for (int i = 1; i < array.length; i++) { - if (array[i] < min) { - min = array[i]; - } - } - - return min; - } - - /** - *

- * Returns the minimum value in an array. - *

- * - * @param array an array, must not be null or empty - * @return the minimum value in the array - * @throws IllegalArgumentException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty - * @since 3.4 Changed signature from min(byte[]) to min(byte...) - */ - public static byte min(final byte... array) { - // Validates input - validateArray(array); - - // Finds and returns min - byte min = array[0]; - for (int i = 1; i < array.length; i++) { - if (array[i] < min) { - min = array[i]; - } - } - - return min; - } - - /** - *

- * Returns the minimum value in an array. - *

- * - * @param array an array, must not be null or empty - * @return the minimum value in the array - * @throws IllegalArgumentException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty - * @see IEEE754rUtils#min(double[]) IEEE754rUtils for a version of this method - * that handles NaN differently - * @since 3.4 Changed signature from min(double[]) to min(double...) - */ - public static double min(final double... array) { - // Validates input - validateArray(array); - - // Finds and returns min - double min = array[0]; - for (int i = 1; i < array.length; i++) { - if (Double.isNaN(array[i])) { - return Double.NaN; - } - if (array[i] < min) { - min = array[i]; - } - } - - return min; - } - - /** - *

- * Returns the minimum value in an array. - *

- * - * @param array an array, must not be null or empty - * @return the minimum value in the array - * @throws IllegalArgumentException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty - * @see IEEE754rUtils#min(float[]) IEEE754rUtils for a version of this method - * that handles NaN differently - * @since 3.4 Changed signature from min(float[]) to min(float...) - */ - public static float min(final float... array) { - // Validates input - validateArray(array); - - // Finds and returns min - float min = array[0]; - for (int i = 1; i < array.length; i++) { - if (Float.isNaN(array[i])) { - return Float.NaN; - } - if (array[i] < min) { - min = array[i]; - } - } - - return min; - } - - // Max in array - // -------------------------------------------------------------------- - /** - *

- * Returns the maximum value in an array. - *

- * - * @param array an array, must not be null or empty - * @return the maximum value in the array - * @throws IllegalArgumentException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty - * @since 3.4 Changed signature from max(long[]) to max(long...) - */ - public static long max(final long... array) { - // Validates input - validateArray(array); - - // Finds and returns max - long max = array[0]; - for (int j = 1; j < array.length; j++) { - if (array[j] > max) { - max = array[j]; - } - } - - return max; - } - - /** - *

- * Returns the maximum value in an array. - *

- * - * @param array an array, must not be null or empty - * @return the maximum value in the array - * @throws IllegalArgumentException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty - * @since 3.4 Changed signature from max(int[]) to max(int...) - */ - public static int max(final int... array) { - // Validates input - validateArray(array); - - // Finds and returns max - int max = array[0]; - for (int j = 1; j < array.length; j++) { - if (array[j] > max) { - max = array[j]; - } - } - - return max; - } - - /** - *

- * Returns the maximum value in an array. - *

- * - * @param array an array, must not be null or empty - * @return the maximum value in the array - * @throws IllegalArgumentException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty - * @since 3.4 Changed signature from max(short[]) to max(short...) - */ - public static short max(final short... array) { - // Validates input - validateArray(array); - - // Finds and returns max - short max = array[0]; - for (int i = 1; i < array.length; i++) { - if (array[i] > max) { - max = array[i]; - } - } - - return max; - } - - /** - *

- * Returns the maximum value in an array. - *

- * - * @param array an array, must not be null or empty - * @return the maximum value in the array - * @throws IllegalArgumentException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty - * @since 3.4 Changed signature from max(byte[]) to max(byte...) - */ - public static byte max(final byte... array) { - // Validates input - validateArray(array); - - // Finds and returns max - byte max = array[0]; - for (int i = 1; i < array.length; i++) { - if (array[i] > max) { - max = array[i]; - } - } - - return max; - } - - /** - *

- * Returns the maximum value in an array. - *

- * - * @param array an array, must not be null or empty - * @return the maximum value in the array - * @throws IllegalArgumentException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty - * @see IEEE754rUtils#max(double[]) IEEE754rUtils for a version of this method - * that handles NaN differently - * @since 3.4 Changed signature from max(double[]) to max(double...) - */ - public static double max(final double... array) { - // Validates input - validateArray(array); - - // Finds and returns max - double max = array[0]; - for (int j = 1; j < array.length; j++) { - if (Double.isNaN(array[j])) { - return Double.NaN; - } - if (array[j] > max) { - max = array[j]; - } - } - - return max; - } - - /** - *

- * Returns the maximum value in an array. - *

- * - * @param array an array, must not be null or empty - * @return the maximum value in the array - * @throws IllegalArgumentException if {@code array} is {@code null} - * @throws IllegalArgumentException if {@code array} is empty - * @see IEEE754rUtils#max(float[]) IEEE754rUtils for a version of this method - * that handles NaN differently - * @since 3.4 Changed signature from max(float[]) to max(float...) - */ - public static float max(final float... array) { - // Validates input - validateArray(array); - - // Finds and returns max - float max = array[0]; - for (int j = 1; j < array.length; j++) { - if (Float.isNaN(array[j])) { - return Float.NaN; - } - if (array[j] > max) { - max = array[j]; - } - } - - return max; - } - - /** - * Checks if the specified array is neither null nor empty. - * - * @param array the array to check - * @throws IllegalArgumentException if {@code array} is either {@code null} or - * empty - */ - private static void validateArray(final Object array) { - Validate.notNull(array, "array"); - Validate.isTrue(Array.getLength(array) != 0, "Array cannot be empty."); - } - - // 3 param min - // ----------------------------------------------------------------------- - /** - *

- * Gets the minimum of three {@code long} values. - *

- * - * @param a value 1 - * @param b value 2 - * @param c value 3 - * @return the smallest of the values - */ - public static long min(long a, final long b, final long c) { - if (b < a) { - a = b; - } - if (c < a) { - a = c; - } - return a; - } - - /** - *

- * Gets the minimum of three {@code int} values. - *

- * - * @param a value 1 - * @param b value 2 - * @param c value 3 - * @return the smallest of the values - */ - public static int min(int a, final int b, final int c) { - if (b < a) { - a = b; - } - if (c < a) { - a = c; - } - return a; - } - - /** - *

- * Gets the minimum of three {@code short} values. - *

- * - * @param a value 1 - * @param b value 2 - * @param c value 3 - * @return the smallest of the values - */ - public static short min(short a, final short b, final short c) { - if (b < a) { - a = b; - } - if (c < a) { - a = c; - } - return a; - } - - /** - *

- * Gets the minimum of three {@code byte} values. - *

- * - * @param a value 1 - * @param b value 2 - * @param c value 3 - * @return the smallest of the values - */ - public static byte min(byte a, final byte b, final byte c) { - if (b < a) { - a = b; - } - if (c < a) { - a = c; - } - return a; - } - - /** - *

- * Gets the minimum of three {@code double} values. - *

- * - *

- * If any value is {@code NaN}, {@code NaN} is returned. Infinity is handled. - *

- * - * @param a value 1 - * @param b value 2 - * @param c value 3 - * @return the smallest of the values - * @see IEEE754rUtils#min(double, double, double) for a version of this method - * that handles NaN differently - */ - public static double min(final double a, final double b, final double c) { - return Math.min(Math.min(a, b), c); - } - - /** - *

- * Gets the minimum of three {@code float} values. - *

- * - *

- * If any value is {@code NaN}, {@code NaN} is returned. Infinity is handled. - *

- * - * @param a value 1 - * @param b value 2 - * @param c value 3 - * @return the smallest of the values - * @see IEEE754rUtils#min(float, float, float) for a version of this method that - * handles NaN differently - */ - public static float min(final float a, final float b, final float c) { - return Math.min(Math.min(a, b), c); - } - - // 3 param max - // ----------------------------------------------------------------------- - /** - *

- * Gets the maximum of three {@code long} values. - *

- * - * @param a value 1 - * @param b value 2 - * @param c value 3 - * @return the largest of the values - */ - public static long max(long a, final long b, final long c) { - if (b > a) { - a = b; - } - if (c > a) { - a = c; - } - return a; - } - - /** - *

- * Gets the maximum of three {@code int} values. - *

- * - * @param a value 1 - * @param b value 2 - * @param c value 3 - * @return the largest of the values - */ - public static int max(int a, final int b, final int c) { - if (b > a) { - a = b; - } - if (c > a) { - a = c; - } - return a; - } - - /** - *

- * Gets the maximum of three {@code short} values. - *

- * - * @param a value 1 - * @param b value 2 - * @param c value 3 - * @return the largest of the values - */ - public static short max(short a, final short b, final short c) { - if (b > a) { - a = b; - } - if (c > a) { - a = c; - } - return a; - } - - /** - *

- * Gets the maximum of three {@code byte} values. - *

- * - * @param a value 1 - * @param b value 2 - * @param c value 3 - * @return the largest of the values - */ - public static byte max(byte a, final byte b, final byte c) { - if (b > a) { - a = b; - } - if (c > a) { - a = c; - } - return a; - } - - /** - *

- * Gets the maximum of three {@code double} values. - *

- * - *

- * If any value is {@code NaN}, {@code NaN} is returned. Infinity is handled. - *

- * - * @param a value 1 - * @param b value 2 - * @param c value 3 - * @return the largest of the values - * @see IEEE754rUtils#max(double, double, double) for a version of this method - * that handles NaN differently - */ - public static double max(final double a, final double b, final double c) { - return Math.max(Math.max(a, b), c); - } - - /** - *

- * Gets the maximum of three {@code float} values. - *

- * - *

- * If any value is {@code NaN}, {@code NaN} is returned. Infinity is handled. - *

- * - * @param a value 1 - * @param b value 2 - * @param c value 3 - * @return the largest of the values - * @see IEEE754rUtils#max(float, float, float) for a version of this method that - * handles NaN differently - */ - public static float max(final float a, final float b, final float c) { - return Math.max(Math.max(a, b), c); - } - - // ----------------------------------------------------------------------- - /** - *

- * Checks whether the {@code String} contains only digit characters. - *

- * - *

- * {@code Null} and empty String will return {@code false}. - *

- * - * @param str the {@code String} to check - * @return {@code true} if str contains only Unicode numeric - */ - public static boolean isDigits(final String str) { - return StringUtils.isNumeric(str); - } - - /** - *

- * Checks whether the String a valid Java number. - *

- * - *

- * Valid numbers include hexadecimal marked with the {@code 0x} or {@code 0X} - * qualifier, octal numbers, scientific notation and numbers marked with a type - * qualifier (e.g. 123L). - *

- * - *

- * Non-hexadecimal strings beginning with a leading zero are treated as octal - * values. Thus the string {@code 09} will return {@code false}, since {@code 9} - * is not a valid octal value. However, numbers beginning with {@code 0.} are - * treated as decimal. - *

- * - *

- * {@code null} and empty/blank {@code String} will return {@code false}. - *

- * - *

- * Note, {@link #createNumber(String)} should return a number for every input - * resulting in {@code true}. - *

- * - * @param str the {@code String} to check - * @return {@code true} if the string is a correctly formatted number - * @since 3.3 the code supports hex {@code 0Xhhh} an octal {@code 0ddd} - * validation - * @deprecated This feature will be removed in Lang 4.0, use - * {@link NumberUtils#isCreatable(String)} instead - */ - @Deprecated - public static boolean isNumber(final String str) { - return isCreatable(str); - } - - /** - *

- * Checks whether the String a valid Java number. - *

- * - *

- * Valid numbers include hexadecimal marked with the {@code 0x} or {@code 0X} - * qualifier, octal numbers, scientific notation and numbers marked with a type - * qualifier (e.g. 123L). - *

- * - *

- * Non-hexadecimal strings beginning with a leading zero are treated as octal - * values. Thus the string {@code 09} will return {@code false}, since {@code 9} - * is not a valid octal value. However, numbers beginning with {@code 0.} are - * treated as decimal. - *

- * - *

- * {@code null} and empty/blank {@code String} will return {@code false}. - *

- * - *

- * Note, {@link #createNumber(String)} should return a number for every input - * resulting in {@code true}. - *

- * - * @param str the {@code String} to check - * @return {@code true} if the string is a correctly formatted number - * @since 3.5 - */ - public static boolean isCreatable(final String str) { - if (StringUtils.isEmpty(str)) { - return false; - } - final char[] chars = str.toCharArray(); - int sz = chars.length; - boolean hasExp = false; - boolean hasDecPoint = false; - boolean allowSigns = false; - boolean foundDigit = false; - // deal with any possible sign up front - final int start = chars[0] == '-' || chars[0] == '+' ? 1 : 0; - if (sz > start + 1 && chars[start] == '0' && !StringUtils.contains(str, '.')) { // leading 0, skip if is a - // decimal number - if (chars[start + 1] == 'x' || chars[start + 1] == 'X') { // leading 0x/0X - int i = start + 2; - if (i == sz) { - return false; // str == "0x" - } - // checking hex (it can't be anything else) - for (; i < chars.length; i++) { - if ((chars[i] < '0' || chars[i] > '9') && (chars[i] < 'a' || chars[i] > 'f') - && (chars[i] < 'A' || chars[i] > 'F')) { - return false; - } - } - return true; - } else if (Character.isDigit(chars[start + 1])) { - // leading 0, but not hex, must be octal - int i = start + 1; - for (; i < chars.length; i++) { - if (chars[i] < '0' || chars[i] > '7') { - return false; - } - } - return true; - } - } - sz--; // don't want to loop to the last char, check it afterwords - // for type qualifiers - int i = start; - // loop to the next to last char or to the last char if we need another digit to - // make a valid number (e.g. chars[0..5] = "1234E") - while (i < sz || i < sz + 1 && allowSigns && !foundDigit) { - if (chars[i] >= '0' && chars[i] <= '9') { - foundDigit = true; - allowSigns = false; - - } else if (chars[i] == '.') { - if (hasDecPoint || hasExp) { - // two decimal points or dec in exponent - return false; - } - hasDecPoint = true; - } else if (chars[i] == 'e' || chars[i] == 'E') { - // we've already taken care of hex. - if (hasExp) { - // two E's - return false; - } - if (!foundDigit) { - return false; - } - hasExp = true; - allowSigns = true; - } else if (chars[i] == '+' || chars[i] == '-') { - if (!allowSigns) { - return false; - } - allowSigns = false; - foundDigit = false; // we need a digit after the E - } else { - return false; - } - i++; - } - if (i < chars.length) { - if (chars[i] >= '0' && chars[i] <= '9') { - // no type qualifier, OK - return true; - } - if (chars[i] == 'e' || chars[i] == 'E') { - // can't have an E at the last byte - return false; - } - if (chars[i] == '.') { - if (hasDecPoint || hasExp) { - // two decimal points or dec in exponent - return false; - } - // single trailing decimal point after non-exponent is ok - return foundDigit; - } - if (!allowSigns && (chars[i] == 'd' || chars[i] == 'D' || chars[i] == 'f' || chars[i] == 'F')) { - return foundDigit; - } - if (chars[i] == 'l' || chars[i] == 'L') { - // not allowing L with an exponent or decimal point - return foundDigit && !hasExp && !hasDecPoint; - } - // last character is illegal - return false; - } - // allowSigns is true iff the val ends in 'E' - // found digit it to make sure weird stuff like '.' and '1E-' doesn't pass - return !allowSigns && foundDigit; - } - - /** - *

- * Checks whether the given String is a parsable number. - *

- * - *

- * Parsable numbers include those Strings understood by - * {@link Integer#parseInt(String)}, {@link Long#parseLong(String)}, - * {@link Float#parseFloat(String)} or {@link Double#parseDouble(String)}. This - * method can be used instead of catching {@link java.text.ParseException} when - * calling one of those methods. - *

- * - *

- * Hexadecimal and scientific notations are not considered - * parsable. See {@link #isCreatable(String)} on those cases. - *

- * - *

- * {@code Null} and empty String will return {@code false}. - *

- * - * @param str the String to check. - * @return {@code true} if the string is a parsable number. - * @since 3.4 - */ - public static boolean isParsable(final String str) { - if (StringUtils.isEmpty(str)) { - return false; - } - if (str.charAt(str.length() - 1) == '.') { - return false; - } - if (str.charAt(0) == '-') { - if (str.length() == 1) { - return false; - } - return withDecimalsParsing(str, 1); - } - return withDecimalsParsing(str, 0); - } - - private static boolean withDecimalsParsing(final String str, final int beginIdx) { - int decimalPoints = 0; - for (int i = beginIdx; i < str.length(); i++) { - final boolean isDecimalPoint = str.charAt(i) == '.'; - if (isDecimalPoint) { - decimalPoints++; - } - if (decimalPoints > 1) { - return false; - } - if (!isDecimalPoint && !Character.isDigit(str.charAt(i))) { - return false; - } - } - return true; - } - - /** - *

- * Compares two {@code int} values numerically. This is the same functionality - * as provided in Java 7. - *

- * - * @param x the first {@code int} to compare - * @param y the second {@code int} to compare - * @return the value {@code 0} if {@code x == y}; a value less than {@code 0} if - * {@code x < y}; and a value greater than {@code 0} if {@code x > y} - * @since 3.4 - */ - public static int compare(final int x, final int y) { - if (x == y) { - return 0; - } - return x < y ? -1 : 1; - } - - /** - *

- * Compares to {@code long} values numerically. This is the same functionality - * as provided in Java 7. - *

- * - * @param x the first {@code long} to compare - * @param y the second {@code long} to compare - * @return the value {@code 0} if {@code x == y}; a value less than {@code 0} if - * {@code x < y}; and a value greater than {@code 0} if {@code x > y} - * @since 3.4 - */ - public static int compare(final long x, final long y) { - if (x == y) { - return 0; - } - return x < y ? -1 : 1; - } - - /** - *

- * Compares to {@code short} values numerically. This is the same functionality - * as provided in Java 7. - *

- * - * @param x the first {@code short} to compare - * @param y the second {@code short} to compare - * @return the value {@code 0} if {@code x == y}; a value less than {@code 0} if - * {@code x < y}; and a value greater than {@code 0} if {@code x > y} - * @since 3.4 - */ - public static int compare(final short x, final short y) { - if (x == y) { - return 0; - } - return x < y ? -1 : 1; - } - - /** - *

- * Compares two {@code byte} values numerically. This is the same functionality - * as provided in Java 7. - *

- * - * @param x the first {@code byte} to compare - * @param y the second {@code byte} to compare - * @return the value {@code 0} if {@code x == y}; a value less than {@code 0} if - * {@code x < y}; and a value greater than {@code 0} if {@code x > y} - * @since 3.4 - */ - public static int compare(final byte x, final byte y) { - return x - y; - } -} diff --git a/src/main/java/org/apache/commons/lang3/math/package-info.java b/src/main/java/org/apache/commons/lang3/math/package-info.java deleted file mode 100755 index c2a8058b..00000000 --- a/src/main/java/org/apache/commons/lang3/math/package-info.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - *

- * Extends {@link java.math} for business mathematical classes. This package is - * intended for business mathematical use, not scientific use. See - * Commons Math for a more - * complete set of mathematical classes. These classes are immutable, and - * therefore thread-safe. - *

- * - *

- * Although Commons Math also exists, some basic mathematical functions are - * contained within Lang. These include classes to a - * {@link org.apache.commons.lang3.math.Fraction} class, various utilities for - * random numbers, and the flagship class, - * {@link org.apache.commons.lang3.math.NumberUtils} which contains a handful of - * classic number functions. - *

- * - *

- * There are two aspects of this package that should be highlighted. The first - * is {@link org.apache.commons.lang3.math.NumberUtils#createNumber(String)}, a - * method which does its best to convert a String into a - * {@link java.lang.Number} object. You have no idea what type of Number it will - * return, so you should call the relevant {@code xxxValue} method when you - * reach the point of needing a number. NumberUtils also has a related - * {@link org.apache.commons.lang3.math.NumberUtils#isCreatable(String)} method. - *

- * - * @since 2.0 - */ -package org.apache.commons.lang3.math; diff --git a/src/main/java/org/apache/commons/lang3/mutable/Mutable.java b/src/main/java/org/apache/commons/lang3/mutable/Mutable.java deleted file mode 100755 index ad896e2d..00000000 --- a/src/main/java/org/apache/commons/lang3/mutable/Mutable.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.mutable; - -/** - * Provides mutable access to a value. - *

- * {@code Mutable} is used as a generic interface to the implementations in this - * package. - *

- * A typical use case would be to enable a primitive or string to be passed to a - * method and allow that method to effectively change the value of the - * primitive/string. Another use case is to store a frequently changing - * primitive in a collection (for example a total in a map) without needing to - * create new Integer/Long wrapper objects. - * - * @param the type to set and get - * @since 2.1 - */ -public interface Mutable { - - /** - * Gets the value of this mutable. - * - * @return the stored value - */ - T getValue(); - - /** - * Sets the value of this mutable. - * - * @param value the value to store - * @throws NullPointerException if the object is null and null is invalid - * @throws ClassCastException if the type is invalid - */ - void setValue(T value); - -} diff --git a/src/main/java/org/apache/commons/lang3/mutable/MutableBoolean.java b/src/main/java/org/apache/commons/lang3/mutable/MutableBoolean.java deleted file mode 100755 index 2d34d330..00000000 --- a/src/main/java/org/apache/commons/lang3/mutable/MutableBoolean.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.mutable; - -import java.io.Serializable; - -import org.apache.commons.lang3.BooleanUtils; - -/** - * A mutable {@code boolean} wrapper. - *

- * Note that as MutableBoolean does not extend Boolean, it is not treated by - * HString.format as a Boolean parameter. - * - * @see Boolean - * @since 2.2 - */ -public class MutableBoolean implements Mutable, Serializable, Comparable { - - /** - * Required for serialization support. - * - * @see java.io.Serializable - */ - private static final long serialVersionUID = -4830728138360036487L; - - /** The mutable value. */ - private boolean value; - - /** - * Constructs a new MutableBoolean with the default value of false. - */ - public MutableBoolean() { - } - - /** - * Constructs a new MutableBoolean with the specified value. - * - * @param value the initial value to store - */ - public MutableBoolean(final boolean value) { - this.value = value; - } - - /** - * Constructs a new MutableBoolean with the specified value. - * - * @param value the initial value to store, not null - * @throws NullPointerException if the object is null - */ - public MutableBoolean(final Boolean value) { - this.value = value.booleanValue(); - } - - // ----------------------------------------------------------------------- - /** - * Gets the value as a Boolean instance. - * - * @return the value as a Boolean, never null - */ - @Override - public Boolean getValue() { - return Boolean.valueOf(this.value); - } - - /** - * Sets the value. - * - * @param value the value to set - */ - public void setValue(final boolean value) { - this.value = value; - } - - /** - * Sets the value to false. - * - * @since 3.3 - */ - public void setFalse() { - this.value = false; - } - - /** - * Sets the value to true. - * - * @since 3.3 - */ - public void setTrue() { - this.value = true; - } - - /** - * Sets the value from any Boolean instance. - * - * @param value the value to set, not null - * @throws NullPointerException if the object is null - */ - @Override - public void setValue(final Boolean value) { - this.value = value.booleanValue(); - } - - // ----------------------------------------------------------------------- - /** - * Checks if the current value is {@code true}. - * - * @return {@code true} if the current value is {@code true} - * @since 2.5 - */ - public boolean isTrue() { - return value; - } - - /** - * Checks if the current value is {@code false}. - * - * @return {@code true} if the current value is {@code false} - * @since 2.5 - */ - public boolean isFalse() { - return !value; - } - - // ----------------------------------------------------------------------- - /** - * Returns the value of this MutableBoolean as a boolean. - * - * @return the boolean value represented by this object. - */ - public boolean booleanValue() { - return value; - } - - // ----------------------------------------------------------------------- - /** - * Gets this mutable as an instance of Boolean. - * - * @return a Boolean instance containing the value from this mutable, never null - * @since 2.5 - */ - public Boolean toBoolean() { - return Boolean.valueOf(booleanValue()); - } - - // ----------------------------------------------------------------------- - /** - * Compares this object to the specified object. The result is {@code true} if - * and only if the argument is not {@code null} and is an {@code MutableBoolean} - * object that contains the same {@code boolean} value as this object. - * - * @param obj the object to compare with, null returns false - * @return {@code true} if the objects are the same; {@code false} otherwise. - */ - @Override - public boolean equals(final Object obj) { - if (obj instanceof MutableBoolean) { - return value == ((MutableBoolean) obj).booleanValue(); - } - return false; - } - - /** - * Returns a suitable hash code for this mutable. - * - * @return the hash code returned by {@code Boolean.TRUE} or - * {@code Boolean.FALSE} - */ - @Override - public int hashCode() { - return value ? Boolean.TRUE.hashCode() : Boolean.FALSE.hashCode(); - } - - // ----------------------------------------------------------------------- - /** - * Compares this mutable to another in ascending order. - * - * @param other the other mutable to compare to, not null - * @return negative if this is less, zero if equal, positive if greater where - * false is less than true - */ - @Override - public int compareTo(final MutableBoolean other) { - return BooleanUtils.compare(this.value, other.value); - } - - // ----------------------------------------------------------------------- - /** - * Returns the String value of this mutable. - * - * @return the mutable value as a string - */ - @Override - public String toString() { - return String.valueOf(value); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/mutable/MutableByte.java b/src/main/java/org/apache/commons/lang3/mutable/MutableByte.java deleted file mode 100755 index 398ace8b..00000000 --- a/src/main/java/org/apache/commons/lang3/mutable/MutableByte.java +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.mutable; - -import org.apache.commons.lang3.math.NumberUtils; - -/** - * A mutable {@code byte} wrapper. - *

- * Note that as MutableByte does not extend Byte, it is not treated by - * HString.format as a Byte parameter. - * - * @see Byte - * @since 2.1 - */ -public class MutableByte extends Number implements Comparable, Mutable { - - /** - * Required for serialization support. - * - * @see java.io.Serializable - */ - private static final long serialVersionUID = -1585823265L; - - /** The mutable value. */ - private byte value; - - /** - * Constructs a new MutableByte with the default value of zero. - */ - public MutableByte() { - } - - /** - * Constructs a new MutableByte with the specified value. - * - * @param value the initial value to store - */ - public MutableByte(final byte value) { - this.value = value; - } - - /** - * Constructs a new MutableByte with the specified value. - * - * @param value the initial value to store, not null - * @throws NullPointerException if the object is null - */ - public MutableByte(final Number value) { - this.value = value.byteValue(); - } - - /** - * Constructs a new MutableByte parsing the given string. - * - * @param value the string to parse, not null - * @throws NumberFormatException if the string cannot be parsed into a byte - * @since 2.5 - */ - public MutableByte(final String value) { - this.value = Byte.parseByte(value); - } - - // ----------------------------------------------------------------------- - /** - * Gets the value as a Byte instance. - * - * @return the value as a Byte, never null - */ - @Override - public Byte getValue() { - return Byte.valueOf(this.value); - } - - /** - * Sets the value. - * - * @param value the value to set - */ - public void setValue(final byte value) { - this.value = value; - } - - /** - * Sets the value from any Number instance. - * - * @param value the value to set, not null - * @throws NullPointerException if the object is null - */ - @Override - public void setValue(final Number value) { - this.value = value.byteValue(); - } - - // ----------------------------------------------------------------------- - /** - * Increments the value. - * - * @since 2.2 - */ - public void increment() { - value++; - } - - /** - * Increments this instance's value by 1; this method returns the value - * associated with the instance immediately prior to the increment operation. - * This method is not thread safe. - * - * @return the value associated with the instance before it was incremented - * @since 3.5 - */ - public byte getAndIncrement() { - final byte last = value; - value++; - return last; - } - - /** - * Increments this instance's value by 1; this method returns the value - * associated with the instance immediately after the increment operation. This - * method is not thread safe. - * - * @return the value associated with the instance after it is incremented - * @since 3.5 - */ - public byte incrementAndGet() { - value++; - return value; - } - - /** - * Decrements the value. - * - * @since 2.2 - */ - public void decrement() { - value--; - } - - /** - * Decrements this instance's value by 1; this method returns the value - * associated with the instance immediately prior to the decrement operation. - * This method is not thread safe. - * - * @return the value associated with the instance before it was decremented - * @since 3.5 - */ - public byte getAndDecrement() { - final byte last = value; - value--; - return last; - } - - /** - * Decrements this instance's value by 1; this method returns the value - * associated with the instance immediately after the decrement operation. This - * method is not thread safe. - * - * @return the value associated with the instance after it is decremented - * @since 3.5 - */ - public byte decrementAndGet() { - value--; - return value; - } - - // ----------------------------------------------------------------------- - /** - * Adds a value to the value of this instance. - * - * @param operand the value to add, not null - * @since 2.2 - */ - public void add(final byte operand) { - this.value += operand; - } - - /** - * Adds a value to the value of this instance. - * - * @param operand the value to add, not null - * @throws NullPointerException if the object is null - * @since 2.2 - */ - public void add(final Number operand) { - this.value += operand.byteValue(); - } - - /** - * Subtracts a value from the value of this instance. - * - * @param operand the value to subtract, not null - * @since 2.2 - */ - public void subtract(final byte operand) { - this.value -= operand; - } - - /** - * Subtracts a value from the value of this instance. - * - * @param operand the value to subtract, not null - * @throws NullPointerException if the object is null - * @since 2.2 - */ - public void subtract(final Number operand) { - this.value -= operand.byteValue(); - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately after the addition operation. - * This method is not thread safe. - * - * @param operand the quantity to add, not null - * @return the value associated with this instance after adding the operand - * @since 3.5 - */ - public byte addAndGet(final byte operand) { - this.value += operand; - return value; - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately after the addition operation. - * This method is not thread safe. - * - * @param operand the quantity to add, not null - * @throws NullPointerException if {@code operand} is null - * @return the value associated with this instance after adding the operand - * @since 3.5 - */ - public byte addAndGet(final Number operand) { - this.value += operand.byteValue(); - return value; - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately prior to the addition - * operation. This method is not thread safe. - * - * @param operand the quantity to add, not null - * @return the value associated with this instance immediately before the - * operand was added - * @since 3.5 - */ - public byte getAndAdd(final byte operand) { - final byte last = value; - this.value += operand; - return last; - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately prior to the addition - * operation. This method is not thread safe. - * - * @param operand the quantity to add, not null - * @throws NullPointerException if {@code operand} is null - * @return the value associated with this instance immediately before the - * operand was added - * @since 3.5 - */ - public byte getAndAdd(final Number operand) { - final byte last = value; - this.value += operand.byteValue(); - return last; - } - - // ----------------------------------------------------------------------- - // shortValue relies on Number implementation - /** - * Returns the value of this MutableByte as a byte. - * - * @return the numeric value represented by this object after conversion to type - * byte. - */ - @Override - public byte byteValue() { - return value; - } - - /** - * Returns the value of this MutableByte as an int. - * - * @return the numeric value represented by this object after conversion to type - * int. - */ - @Override - public int intValue() { - return value; - } - - /** - * Returns the value of this MutableByte as a long. - * - * @return the numeric value represented by this object after conversion to type - * long. - */ - @Override - public long longValue() { - return value; - } - - /** - * Returns the value of this MutableByte as a float. - * - * @return the numeric value represented by this object after conversion to type - * float. - */ - @Override - public float floatValue() { - return value; - } - - /** - * Returns the value of this MutableByte as a double. - * - * @return the numeric value represented by this object after conversion to type - * double. - */ - @Override - public double doubleValue() { - return value; - } - - // ----------------------------------------------------------------------- - /** - * Gets this mutable as an instance of Byte. - * - * @return a Byte instance containing the value from this mutable - */ - public Byte toByte() { - return Byte.valueOf(byteValue()); - } - - // ----------------------------------------------------------------------- - /** - * Compares this object to the specified object. The result is {@code true} if - * and only if the argument is not {@code null} and is a {@code MutableByte} - * object that contains the same {@code byte} value as this object. - * - * @param obj the object to compare with, null returns false - * @return {@code true} if the objects are the same; {@code false} otherwise. - */ - @Override - public boolean equals(final Object obj) { - if (obj instanceof MutableByte) { - return value == ((MutableByte) obj).byteValue(); - } - return false; - } - - /** - * Returns a suitable hash code for this mutable. - * - * @return a suitable hash code - */ - @Override - public int hashCode() { - return value; - } - - // ----------------------------------------------------------------------- - /** - * Compares this mutable to another in ascending order. - * - * @param other the other mutable to compare to, not null - * @return negative if this is less, zero if equal, positive if greater - */ - @Override - public int compareTo(final MutableByte other) { - return NumberUtils.compare(this.value, other.value); - } - - // ----------------------------------------------------------------------- - /** - * Returns the String value of this mutable. - * - * @return the mutable value as a string - */ - @Override - public String toString() { - return String.valueOf(value); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/mutable/MutableDouble.java b/src/main/java/org/apache/commons/lang3/mutable/MutableDouble.java deleted file mode 100755 index fd40c9e7..00000000 --- a/src/main/java/org/apache/commons/lang3/mutable/MutableDouble.java +++ /dev/null @@ -1,434 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.mutable; - -/** - * A mutable {@code double} wrapper. - *

- * Note that as MutableDouble does not extend Double, it is not treated by - * HString.format as a Double parameter. - * - * @see Double - * @since 2.1 - */ -public class MutableDouble extends Number implements Comparable, Mutable { - - /** - * Required for serialization support. - * - * @see java.io.Serializable - */ - private static final long serialVersionUID = 1587163916L; - - /** The mutable value. */ - private double value; - - /** - * Constructs a new MutableDouble with the default value of zero. - */ - public MutableDouble() { - } - - /** - * Constructs a new MutableDouble with the specified value. - * - * @param value the initial value to store - */ - public MutableDouble(final double value) { - this.value = value; - } - - /** - * Constructs a new MutableDouble with the specified value. - * - * @param value the initial value to store, not null - * @throws NullPointerException if the object is null - */ - public MutableDouble(final Number value) { - this.value = value.doubleValue(); - } - - /** - * Constructs a new MutableDouble parsing the given string. - * - * @param value the string to parse, not null - * @throws NumberFormatException if the string cannot be parsed into a double - * @since 2.5 - */ - public MutableDouble(final String value) { - this.value = Double.parseDouble(value); - } - - // ----------------------------------------------------------------------- - /** - * Gets the value as a Double instance. - * - * @return the value as a Double, never null - */ - @Override - public Double getValue() { - return Double.valueOf(this.value); - } - - /** - * Sets the value. - * - * @param value the value to set - */ - public void setValue(final double value) { - this.value = value; - } - - /** - * Sets the value from any Number instance. - * - * @param value the value to set, not null - * @throws NullPointerException if the object is null - */ - @Override - public void setValue(final Number value) { - this.value = value.doubleValue(); - } - - // ----------------------------------------------------------------------- - /** - * Checks whether the double value is the special NaN value. - * - * @return true if NaN - */ - public boolean isNaN() { - return Double.isNaN(value); - } - - /** - * Checks whether the double value is infinite. - * - * @return true if infinite - */ - public boolean isInfinite() { - return Double.isInfinite(value); - } - - // ----------------------------------------------------------------------- - /** - * Increments the value. - * - * @since 2.2 - */ - public void increment() { - value++; - } - - /** - * Increments this instance's value by 1; this method returns the value - * associated with the instance immediately prior to the increment operation. - * This method is not thread safe. - * - * @return the value associated with the instance before it was incremented - * @since 3.5 - */ - public double getAndIncrement() { - final double last = value; - value++; - return last; - } - - /** - * Increments this instance's value by 1; this method returns the value - * associated with the instance immediately after the increment operation. This - * method is not thread safe. - * - * @return the value associated with the instance after it is incremented - * @since 3.5 - */ - public double incrementAndGet() { - value++; - return value; - } - - /** - * Decrements the value. - * - * @since 2.2 - */ - public void decrement() { - value--; - } - - /** - * Decrements this instance's value by 1; this method returns the value - * associated with the instance immediately prior to the decrement operation. - * This method is not thread safe. - * - * @return the value associated with the instance before it was decremented - * @since 3.5 - */ - public double getAndDecrement() { - final double last = value; - value--; - return last; - } - - /** - * Decrements this instance's value by 1; this method returns the value - * associated with the instance immediately after the decrement operation. This - * method is not thread safe. - * - * @return the value associated with the instance after it is decremented - * @since 3.5 - */ - public double decrementAndGet() { - value--; - return value; - } - - // ----------------------------------------------------------------------- - /** - * Adds a value to the value of this instance. - * - * @param operand the value to add - * @since 2.2 - */ - public void add(final double operand) { - this.value += operand; - } - - /** - * Adds a value to the value of this instance. - * - * @param operand the value to add, not null - * @throws NullPointerException if the object is null - * @since 2.2 - */ - public void add(final Number operand) { - this.value += operand.doubleValue(); - } - - /** - * Subtracts a value from the value of this instance. - * - * @param operand the value to subtract, not null - * @since 2.2 - */ - public void subtract(final double operand) { - this.value -= operand; - } - - /** - * Subtracts a value from the value of this instance. - * - * @param operand the value to subtract, not null - * @throws NullPointerException if the object is null - * @since 2.2 - */ - public void subtract(final Number operand) { - this.value -= operand.doubleValue(); - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately after the addition operation. - * This method is not thread safe. - * - * @param operand the quantity to add, not null - * @return the value associated with this instance after adding the operand - * @since 3.5 - */ - public double addAndGet(final double operand) { - this.value += operand; - return value; - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately after the addition operation. - * This method is not thread safe. - * - * @param operand the quantity to add, not null - * @throws NullPointerException if {@code operand} is null - * @return the value associated with this instance after adding the operand - * @since 3.5 - */ - public double addAndGet(final Number operand) { - this.value += operand.doubleValue(); - return value; - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately prior to the addition - * operation. This method is not thread safe. - * - * @param operand the quantity to add, not null - * @return the value associated with this instance immediately before the - * operand was added - * @since 3.5 - */ - public double getAndAdd(final double operand) { - final double last = value; - this.value += operand; - return last; - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately prior to the addition - * operation. This method is not thread safe. - * - * @param operand the quantity to add, not null - * @throws NullPointerException if {@code operand} is null - * @return the value associated with this instance immediately before the - * operand was added - * @since 3.5 - */ - public double getAndAdd(final Number operand) { - final double last = value; - this.value += operand.doubleValue(); - return last; - } - - // ----------------------------------------------------------------------- - // shortValue and byteValue rely on Number implementation - /** - * Returns the value of this MutableDouble as an int. - * - * @return the numeric value represented by this object after conversion to type - * int. - */ - @Override - public int intValue() { - return (int) value; - } - - /** - * Returns the value of this MutableDouble as a long. - * - * @return the numeric value represented by this object after conversion to type - * long. - */ - @Override - public long longValue() { - return (long) value; - } - - /** - * Returns the value of this MutableDouble as a float. - * - * @return the numeric value represented by this object after conversion to type - * float. - */ - @Override - public float floatValue() { - return (float) value; - } - - /** - * Returns the value of this MutableDouble as a double. - * - * @return the numeric value represented by this object after conversion to type - * double. - */ - @Override - public double doubleValue() { - return value; - } - - // ----------------------------------------------------------------------- - /** - * Gets this mutable as an instance of Double. - * - * @return a Double instance containing the value from this mutable, never null - */ - public Double toDouble() { - return Double.valueOf(doubleValue()); - } - - // ----------------------------------------------------------------------- - /** - * Compares this object against the specified object. The result is {@code true} - * if and only if the argument is not {@code null} and is a {@code Double} - * object that represents a double that has the identical bit pattern to the bit - * pattern of the double represented by this object. For this purpose, two - * {@code double} values are considered to be the same if and only if the method - * {@link Double#doubleToLongBits(double)}returns the same long value when - * applied to each. - *

- * Note that in most cases, for two instances of class {@code Double},{@code d1} - * and {@code d2}, the value of {@code d1.equals(d2)} is {@code true} if and - * only if

- * - *
-	 * d1.doubleValue() == d2.doubleValue()
-	 * 
- * - *
- *

- * also has the value {@code true}. However, there are two exceptions: - *

    - *
  • If {@code d1} and {@code d2} both represent {@code Double.NaN}, then the - * {@code equals} method returns {@code true}, even though - * {@code Double.NaN==Double.NaN} has the value {@code false}. - *
  • If {@code d1} represents {@code +0.0} while {@code d2} represents - * {@code -0.0}, or vice versa, the {@code equal} test has the value - * {@code false}, even though {@code +0.0==-0.0} has the value {@code true}. - * This allows hashtables to operate properly. - *
- * - * @param obj the object to compare with, null returns false - * @return {@code true} if the objects are the same; {@code false} otherwise. - */ - @Override - public boolean equals(final Object obj) { - return obj instanceof MutableDouble - && Double.doubleToLongBits(((MutableDouble) obj).value) == Double.doubleToLongBits(value); - } - - /** - * Returns a suitable hash code for this mutable. - * - * @return a suitable hash code - */ - @Override - public int hashCode() { - final long bits = Double.doubleToLongBits(value); - return (int) (bits ^ bits >>> 32); - } - - // ----------------------------------------------------------------------- - /** - * Compares this mutable to another in ascending order. - * - * @param other the other mutable to compare to, not null - * @return negative if this is less, zero if equal, positive if greater - */ - @Override - public int compareTo(final MutableDouble other) { - return Double.compare(this.value, other.value); - } - - // ----------------------------------------------------------------------- - /** - * Returns the String value of this mutable. - * - * @return the mutable value as a string - */ - @Override - public String toString() { - return String.valueOf(value); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/mutable/MutableFloat.java b/src/main/java/org/apache/commons/lang3/mutable/MutableFloat.java deleted file mode 100755 index b70191d6..00000000 --- a/src/main/java/org/apache/commons/lang3/mutable/MutableFloat.java +++ /dev/null @@ -1,434 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.mutable; - -/** - * A mutable {@code float} wrapper. - *

- * Note that as MutableFloat does not extend Float, it is not treated by - * HString.format as a Float parameter. - * - * @see Float - * @since 2.1 - */ -public class MutableFloat extends Number implements Comparable, Mutable { - - /** - * Required for serialization support. - * - * @see java.io.Serializable - */ - private static final long serialVersionUID = 5787169186L; - - /** The mutable value. */ - private float value; - - /** - * Constructs a new MutableFloat with the default value of zero. - */ - public MutableFloat() { - } - - /** - * Constructs a new MutableFloat with the specified value. - * - * @param value the initial value to store - */ - public MutableFloat(final float value) { - this.value = value; - } - - /** - * Constructs a new MutableFloat with the specified value. - * - * @param value the initial value to store, not null - * @throws NullPointerException if the object is null - */ - public MutableFloat(final Number value) { - this.value = value.floatValue(); - } - - /** - * Constructs a new MutableFloat parsing the given string. - * - * @param value the string to parse, not null - * @throws NumberFormatException if the string cannot be parsed into a float - * @since 2.5 - */ - public MutableFloat(final String value) { - this.value = Float.parseFloat(value); - } - - // ----------------------------------------------------------------------- - /** - * Gets the value as a Float instance. - * - * @return the value as a Float, never null - */ - @Override - public Float getValue() { - return Float.valueOf(this.value); - } - - /** - * Sets the value. - * - * @param value the value to set - */ - public void setValue(final float value) { - this.value = value; - } - - /** - * Sets the value from any Number instance. - * - * @param value the value to set, not null - * @throws NullPointerException if the object is null - */ - @Override - public void setValue(final Number value) { - this.value = value.floatValue(); - } - - // ----------------------------------------------------------------------- - /** - * Checks whether the float value is the special NaN value. - * - * @return true if NaN - */ - public boolean isNaN() { - return Float.isNaN(value); - } - - /** - * Checks whether the float value is infinite. - * - * @return true if infinite - */ - public boolean isInfinite() { - return Float.isInfinite(value); - } - - // ----------------------------------------------------------------------- - /** - * Increments the value. - * - * @since 2.2 - */ - public void increment() { - value++; - } - - /** - * Increments this instance's value by 1; this method returns the value - * associated with the instance immediately prior to the increment operation. - * This method is not thread safe. - * - * @return the value associated with the instance before it was incremented - * @since 3.5 - */ - public float getAndIncrement() { - final float last = value; - value++; - return last; - } - - /** - * Increments this instance's value by 1; this method returns the value - * associated with the instance immediately after the increment operation. This - * method is not thread safe. - * - * @return the value associated with the instance after it is incremented - * @since 3.5 - */ - public float incrementAndGet() { - value++; - return value; - } - - /** - * Decrements the value. - * - * @since 2.2 - */ - public void decrement() { - value--; - } - - /** - * Decrements this instance's value by 1; this method returns the value - * associated with the instance immediately prior to the decrement operation. - * This method is not thread safe. - * - * @return the value associated with the instance before it was decremented - * @since 3.5 - */ - public float getAndDecrement() { - final float last = value; - value--; - return last; - } - - /** - * Decrements this instance's value by 1; this method returns the value - * associated with the instance immediately after the decrement operation. This - * method is not thread safe. - * - * @return the value associated with the instance after it is decremented - * @since 3.5 - */ - public float decrementAndGet() { - value--; - return value; - } - - // ----------------------------------------------------------------------- - /** - * Adds a value to the value of this instance. - * - * @param operand the value to add, not null - * @since 2.2 - */ - public void add(final float operand) { - this.value += operand; - } - - /** - * Adds a value to the value of this instance. - * - * @param operand the value to add, not null - * @throws NullPointerException if the object is null - * @since 2.2 - */ - public void add(final Number operand) { - this.value += operand.floatValue(); - } - - /** - * Subtracts a value from the value of this instance. - * - * @param operand the value to subtract - * @since 2.2 - */ - public void subtract(final float operand) { - this.value -= operand; - } - - /** - * Subtracts a value from the value of this instance. - * - * @param operand the value to subtract, not null - * @throws NullPointerException if the object is null - * @since 2.2 - */ - public void subtract(final Number operand) { - this.value -= operand.floatValue(); - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately after the addition operation. - * This method is not thread safe. - * - * @param operand the quantity to add, not null - * @return the value associated with this instance after adding the operand - * @since 3.5 - */ - public float addAndGet(final float operand) { - this.value += operand; - return value; - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately after the addition operation. - * This method is not thread safe. - * - * @param operand the quantity to add, not null - * @throws NullPointerException if {@code operand} is null - * @return the value associated with this instance after adding the operand - * @since 3.5 - */ - public float addAndGet(final Number operand) { - this.value += operand.floatValue(); - return value; - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately prior to the addition - * operation. This method is not thread safe. - * - * @param operand the quantity to add, not null - * @return the value associated with this instance immediately before the - * operand was added - * @since 3.5 - */ - public float getAndAdd(final float operand) { - final float last = value; - this.value += operand; - return last; - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately prior to the addition - * operation. This method is not thread safe. - * - * @param operand the quantity to add, not null - * @throws NullPointerException if {@code operand} is null - * @return the value associated with this instance immediately before the - * operand was added - * @since 3.5 - */ - public float getAndAdd(final Number operand) { - final float last = value; - this.value += operand.floatValue(); - return last; - } - - // ----------------------------------------------------------------------- - // shortValue and byteValue rely on Number implementation - /** - * Returns the value of this MutableFloat as an int. - * - * @return the numeric value represented by this object after conversion to type - * int. - */ - @Override - public int intValue() { - return (int) value; - } - - /** - * Returns the value of this MutableFloat as a long. - * - * @return the numeric value represented by this object after conversion to type - * long. - */ - @Override - public long longValue() { - return (long) value; - } - - /** - * Returns the value of this MutableFloat as a float. - * - * @return the numeric value represented by this object after conversion to type - * float. - */ - @Override - public float floatValue() { - return value; - } - - /** - * Returns the value of this MutableFloat as a double. - * - * @return the numeric value represented by this object after conversion to type - * double. - */ - @Override - public double doubleValue() { - return value; - } - - // ----------------------------------------------------------------------- - /** - * Gets this mutable as an instance of Float. - * - * @return a Float instance containing the value from this mutable, never null - */ - public Float toFloat() { - return Float.valueOf(floatValue()); - } - - // ----------------------------------------------------------------------- - /** - * Compares this object against some other object. The result is {@code true} if - * and only if the argument is not {@code null} and is a {@code Float} object - * that represents a {@code float} that has the identical bit pattern to the bit - * pattern of the {@code float} represented by this object. For this purpose, - * two float values are considered to be the same if and only if the method - * {@link Float#floatToIntBits(float)}returns the same int value when applied to - * each. - *

- * Note that in most cases, for two instances of class {@code Float},{@code f1} - * and {@code f2}, the value of {@code f1.equals(f2)} is {@code true} if and - * only if

- * - *
-	 * f1.floatValue() == f2.floatValue()
-	 * 
- * - *
- *

- * also has the value {@code true}. However, there are two exceptions: - *

    - *
  • If {@code f1} and {@code f2} both represent {@code Float.NaN}, then the - * {@code equals} method returns {@code true}, even though - * {@code Float.NaN==Float.NaN} has the value {@code false}. - *
  • If {@code f1} represents {@code +0.0f} while {@code f2} represents - * {@code -0.0f}, or vice versa, the {@code equal} test has the value - * {@code false}, even though {@code 0.0f==-0.0f} has the value {@code true}. - *
- * This definition allows hashtables to operate properly. - * - * @param obj the object to compare with, null returns false - * @return {@code true} if the objects are the same; {@code false} otherwise. - * @see java.lang.Float#floatToIntBits(float) - */ - @Override - public boolean equals(final Object obj) { - return obj instanceof MutableFloat - && Float.floatToIntBits(((MutableFloat) obj).value) == Float.floatToIntBits(value); - } - - /** - * Returns a suitable hash code for this mutable. - * - * @return a suitable hash code - */ - @Override - public int hashCode() { - return Float.floatToIntBits(value); - } - - // ----------------------------------------------------------------------- - /** - * Compares this mutable to another in ascending order. - * - * @param other the other mutable to compare to, not null - * @return negative if this is less, zero if equal, positive if greater - */ - @Override - public int compareTo(final MutableFloat other) { - return Float.compare(this.value, other.value); - } - - // ----------------------------------------------------------------------- - /** - * Returns the String value of this mutable. - * - * @return the mutable value as a string - */ - @Override - public String toString() { - return String.valueOf(value); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/mutable/MutableInt.java b/src/main/java/org/apache/commons/lang3/mutable/MutableInt.java deleted file mode 100755 index 6006025b..00000000 --- a/src/main/java/org/apache/commons/lang3/mutable/MutableInt.java +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.mutable; - -import org.apache.commons.lang3.math.NumberUtils; - -/** - * A mutable {@code int} wrapper. - *

- * Note that as MutableInt does not extend Integer, it is not treated by - * HString.format as an Integer parameter. - * - * @see Integer - * @since 2.1 - */ -public class MutableInt extends Number implements Comparable, Mutable { - - /** - * Required for serialization support. - * - * @see java.io.Serializable - */ - private static final long serialVersionUID = 512176391864L; - - /** The mutable value. */ - private int value; - - /** - * Constructs a new MutableInt with the default value of zero. - */ - public MutableInt() { - } - - /** - * Constructs a new MutableInt with the specified value. - * - * @param value the initial value to store - */ - public MutableInt(final int value) { - this.value = value; - } - - /** - * Constructs a new MutableInt with the specified value. - * - * @param value the initial value to store, not null - * @throws NullPointerException if the object is null - */ - public MutableInt(final Number value) { - this.value = value.intValue(); - } - - /** - * Constructs a new MutableInt parsing the given string. - * - * @param value the string to parse, not null - * @throws NumberFormatException if the string cannot be parsed into an int - * @since 2.5 - */ - public MutableInt(final String value) { - this.value = Integer.parseInt(value); - } - - // ----------------------------------------------------------------------- - /** - * Gets the value as a Integer instance. - * - * @return the value as a Integer, never null - */ - @Override - public Integer getValue() { - return Integer.valueOf(this.value); - } - - /** - * Sets the value. - * - * @param value the value to set - */ - public void setValue(final int value) { - this.value = value; - } - - /** - * Sets the value from any Number instance. - * - * @param value the value to set, not null - * @throws NullPointerException if the object is null - */ - @Override - public void setValue(final Number value) { - this.value = value.intValue(); - } - - // ----------------------------------------------------------------------- - /** - * Increments the value. - * - * @since 2.2 - */ - public void increment() { - value++; - } - - /** - * Increments this instance's value by 1; this method returns the value - * associated with the instance immediately prior to the increment operation. - * This method is not thread safe. - * - * @return the value associated with the instance before it was incremented - * @since 3.5 - */ - public int getAndIncrement() { - final int last = value; - value++; - return last; - } - - /** - * Increments this instance's value by 1; this method returns the value - * associated with the instance immediately after the increment operation. This - * method is not thread safe. - * - * @return the value associated with the instance after it is incremented - * @since 3.5 - */ - public int incrementAndGet() { - value++; - return value; - } - - /** - * Decrements the value. - * - * @since 2.2 - */ - public void decrement() { - value--; - } - - /** - * Decrements this instance's value by 1; this method returns the value - * associated with the instance immediately prior to the decrement operation. - * This method is not thread safe. - * - * @return the value associated with the instance before it was decremented - * @since 3.5 - */ - public int getAndDecrement() { - final int last = value; - value--; - return last; - } - - /** - * Decrements this instance's value by 1; this method returns the value - * associated with the instance immediately after the decrement operation. This - * method is not thread safe. - * - * @return the value associated with the instance after it is decremented - * @since 3.5 - */ - public int decrementAndGet() { - value--; - return value; - } - - // ----------------------------------------------------------------------- - /** - * Adds a value to the value of this instance. - * - * @param operand the value to add, not null - * @since 2.2 - */ - public void add(final int operand) { - this.value += operand; - } - - /** - * Adds a value to the value of this instance. - * - * @param operand the value to add, not null - * @throws NullPointerException if the object is null - * @since 2.2 - */ - public void add(final Number operand) { - this.value += operand.intValue(); - } - - /** - * Subtracts a value from the value of this instance. - * - * @param operand the value to subtract, not null - * @since 2.2 - */ - public void subtract(final int operand) { - this.value -= operand; - } - - /** - * Subtracts a value from the value of this instance. - * - * @param operand the value to subtract, not null - * @throws NullPointerException if the object is null - * @since 2.2 - */ - public void subtract(final Number operand) { - this.value -= operand.intValue(); - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately after the addition operation. - * This method is not thread safe. - * - * @param operand the quantity to add, not null - * @return the value associated with this instance after adding the operand - * @since 3.5 - */ - public int addAndGet(final int operand) { - this.value += operand; - return value; - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately after the addition operation. - * This method is not thread safe. - * - * @param operand the quantity to add, not null - * @throws NullPointerException if {@code operand} is null - * @return the value associated with this instance after adding the operand - * @since 3.5 - */ - public int addAndGet(final Number operand) { - this.value += operand.intValue(); - return value; - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately prior to the addition - * operation. This method is not thread safe. - * - * @param operand the quantity to add, not null - * @return the value associated with this instance immediately before the - * operand was added - * @since 3.5 - */ - public int getAndAdd(final int operand) { - final int last = value; - this.value += operand; - return last; - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately prior to the addition - * operation. This method is not thread safe. - * - * @param operand the quantity to add, not null - * @throws NullPointerException if {@code operand} is null - * @return the value associated with this instance immediately before the - * operand was added - * @since 3.5 - */ - public int getAndAdd(final Number operand) { - final int last = value; - this.value += operand.intValue(); - return last; - } - - // ----------------------------------------------------------------------- - // shortValue and byteValue rely on Number implementation - /** - * Returns the value of this MutableInt as an int. - * - * @return the numeric value represented by this object after conversion to type - * int. - */ - @Override - public int intValue() { - return value; - } - - /** - * Returns the value of this MutableInt as a long. - * - * @return the numeric value represented by this object after conversion to type - * long. - */ - @Override - public long longValue() { - return value; - } - - /** - * Returns the value of this MutableInt as a float. - * - * @return the numeric value represented by this object after conversion to type - * float. - */ - @Override - public float floatValue() { - return value; - } - - /** - * Returns the value of this MutableInt as a double. - * - * @return the numeric value represented by this object after conversion to type - * double. - */ - @Override - public double doubleValue() { - return value; - } - - // ----------------------------------------------------------------------- - /** - * Gets this mutable as an instance of Integer. - * - * @return a Integer instance containing the value from this mutable, never null - */ - public Integer toInteger() { - return Integer.valueOf(intValue()); - } - - // ----------------------------------------------------------------------- - /** - * Compares this object to the specified object. The result is {@code true} if - * and only if the argument is not {@code null} and is a {@code MutableInt} - * object that contains the same {@code int} value as this object. - * - * @param obj the object to compare with, null returns false - * @return {@code true} if the objects are the same; {@code false} otherwise. - */ - @Override - public boolean equals(final Object obj) { - if (obj instanceof MutableInt) { - return value == ((MutableInt) obj).intValue(); - } - return false; - } - - /** - * Returns a suitable hash code for this mutable. - * - * @return a suitable hash code - */ - @Override - public int hashCode() { - return value; - } - - // ----------------------------------------------------------------------- - /** - * Compares this mutable to another in ascending order. - * - * @param other the other mutable to compare to, not null - * @return negative if this is less, zero if equal, positive if greater - */ - @Override - public int compareTo(final MutableInt other) { - return NumberUtils.compare(this.value, other.value); - } - - // ----------------------------------------------------------------------- - /** - * Returns the String value of this mutable. - * - * @return the mutable value as a string - */ - @Override - public String toString() { - return String.valueOf(value); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/mutable/MutableLong.java b/src/main/java/org/apache/commons/lang3/mutable/MutableLong.java deleted file mode 100755 index 9e2a671e..00000000 --- a/src/main/java/org/apache/commons/lang3/mutable/MutableLong.java +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.mutable; - -import org.apache.commons.lang3.math.NumberUtils; - -/** - * A mutable {@code long} wrapper. - *

- * Note that as MutableLong does not extend Long, it is not treated by - * HString.format as a Long parameter. - * - * @see Long - * @since 2.1 - */ -public class MutableLong extends Number implements Comparable, Mutable { - - /** - * Required for serialization support. - * - * @see java.io.Serializable - */ - private static final long serialVersionUID = 62986528375L; - - /** The mutable value. */ - private long value; - - /** - * Constructs a new MutableLong with the default value of zero. - */ - public MutableLong() { - } - - /** - * Constructs a new MutableLong with the specified value. - * - * @param value the initial value to store - */ - public MutableLong(final long value) { - this.value = value; - } - - /** - * Constructs a new MutableLong with the specified value. - * - * @param value the initial value to store, not null - * @throws NullPointerException if the object is null - */ - public MutableLong(final Number value) { - this.value = value.longValue(); - } - - /** - * Constructs a new MutableLong parsing the given string. - * - * @param value the string to parse, not null - * @throws NumberFormatException if the string cannot be parsed into a long - * @since 2.5 - */ - public MutableLong(final String value) { - this.value = Long.parseLong(value); - } - - // ----------------------------------------------------------------------- - /** - * Gets the value as a Long instance. - * - * @return the value as a Long, never null - */ - @Override - public Long getValue() { - return Long.valueOf(this.value); - } - - /** - * Sets the value. - * - * @param value the value to set - */ - public void setValue(final long value) { - this.value = value; - } - - /** - * Sets the value from any Number instance. - * - * @param value the value to set, not null - * @throws NullPointerException if the object is null - */ - @Override - public void setValue(final Number value) { - this.value = value.longValue(); - } - - // ----------------------------------------------------------------------- - /** - * Increments the value. - * - * @since 2.2 - */ - public void increment() { - value++; - } - - /** - * Increments this instance's value by 1; this method returns the value - * associated with the instance immediately prior to the increment operation. - * This method is not thread safe. - * - * @return the value associated with the instance before it was incremented - * @since 3.5 - */ - public long getAndIncrement() { - final long last = value; - value++; - return last; - } - - /** - * Increments this instance's value by 1; this method returns the value - * associated with the instance immediately after the increment operation. This - * method is not thread safe. - * - * @return the value associated with the instance after it is incremented - * @since 3.5 - */ - public long incrementAndGet() { - value++; - return value; - } - - /** - * Decrements the value. - * - * @since 2.2 - */ - public void decrement() { - value--; - } - - /** - * Decrements this instance's value by 1; this method returns the value - * associated with the instance immediately prior to the decrement operation. - * This method is not thread safe. - * - * @return the value associated with the instance before it was decremented - * @since 3.5 - */ - public long getAndDecrement() { - final long last = value; - value--; - return last; - } - - /** - * Decrements this instance's value by 1; this method returns the value - * associated with the instance immediately after the decrement operation. This - * method is not thread safe. - * - * @return the value associated with the instance after it is decremented - * @since 3.5 - */ - public long decrementAndGet() { - value--; - return value; - } - - // ----------------------------------------------------------------------- - /** - * Adds a value to the value of this instance. - * - * @param operand the value to add, not null - * @since 2.2 - */ - public void add(final long operand) { - this.value += operand; - } - - /** - * Adds a value to the value of this instance. - * - * @param operand the value to add, not null - * @throws NullPointerException if the object is null - * @since 2.2 - */ - public void add(final Number operand) { - this.value += operand.longValue(); - } - - /** - * Subtracts a value from the value of this instance. - * - * @param operand the value to subtract, not null - * @since 2.2 - */ - public void subtract(final long operand) { - this.value -= operand; - } - - /** - * Subtracts a value from the value of this instance. - * - * @param operand the value to subtract, not null - * @throws NullPointerException if the object is null - * @since 2.2 - */ - public void subtract(final Number operand) { - this.value -= operand.longValue(); - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately after the addition operation. - * This method is not thread safe. - * - * @param operand the quantity to add, not null - * @return the value associated with this instance after adding the operand - * @since 3.5 - */ - public long addAndGet(final long operand) { - this.value += operand; - return value; - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately after the addition operation. - * This method is not thread safe. - * - * @param operand the quantity to add, not null - * @throws NullPointerException if {@code operand} is null - * @return the value associated with this instance after adding the operand - * @since 3.5 - */ - public long addAndGet(final Number operand) { - this.value += operand.longValue(); - return value; - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately prior to the addition - * operation. This method is not thread safe. - * - * @param operand the quantity to add, not null - * @return the value associated with this instance immediately before the - * operand was added - * @since 3.5 - */ - public long getAndAdd(final long operand) { - final long last = value; - this.value += operand; - return last; - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately prior to the addition - * operation. This method is not thread safe. - * - * @param operand the quantity to add, not null - * @throws NullPointerException if {@code operand} is null - * @return the value associated with this instance immediately before the - * operand was added - * @since 3.5 - */ - public long getAndAdd(final Number operand) { - final long last = value; - this.value += operand.longValue(); - return last; - } - - // ----------------------------------------------------------------------- - // shortValue and byteValue rely on Number implementation - /** - * Returns the value of this MutableLong as an int. - * - * @return the numeric value represented by this object after conversion to type - * int. - */ - @Override - public int intValue() { - return (int) value; - } - - /** - * Returns the value of this MutableLong as a long. - * - * @return the numeric value represented by this object after conversion to type - * long. - */ - @Override - public long longValue() { - return value; - } - - /** - * Returns the value of this MutableLong as a float. - * - * @return the numeric value represented by this object after conversion to type - * float. - */ - @Override - public float floatValue() { - return value; - } - - /** - * Returns the value of this MutableLong as a double. - * - * @return the numeric value represented by this object after conversion to type - * double. - */ - @Override - public double doubleValue() { - return value; - } - - // ----------------------------------------------------------------------- - /** - * Gets this mutable as an instance of Long. - * - * @return a Long instance containing the value from this mutable, never null - */ - public Long toLong() { - return Long.valueOf(longValue()); - } - - // ----------------------------------------------------------------------- - /** - * Compares this object to the specified object. The result is {@code true} if - * and only if the argument is not {@code null} and is a {@code MutableLong} - * object that contains the same {@code long} value as this object. - * - * @param obj the object to compare with, null returns false - * @return {@code true} if the objects are the same; {@code false} otherwise. - */ - @Override - public boolean equals(final Object obj) { - if (obj instanceof MutableLong) { - return value == ((MutableLong) obj).longValue(); - } - return false; - } - - /** - * Returns a suitable hash code for this mutable. - * - * @return a suitable hash code - */ - @Override - public int hashCode() { - return (int) (value ^ (value >>> 32)); - } - - // ----------------------------------------------------------------------- - /** - * Compares this mutable to another in ascending order. - * - * @param other the other mutable to compare to, not null - * @return negative if this is less, zero if equal, positive if greater - */ - @Override - public int compareTo(final MutableLong other) { - return NumberUtils.compare(this.value, other.value); - } - - // ----------------------------------------------------------------------- - /** - * Returns the String value of this mutable. - * - * @return the mutable value as a string - */ - @Override - public String toString() { - return String.valueOf(value); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/mutable/MutableObject.java b/src/main/java/org/apache/commons/lang3/mutable/MutableObject.java deleted file mode 100755 index ced3cc98..00000000 --- a/src/main/java/org/apache/commons/lang3/mutable/MutableObject.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.commons.lang3.mutable; - -import java.io.Serializable; - -/** - * A mutable {@code Object} wrapper. - * - * @param the type to set and get - * @since 2.1 - */ -public class MutableObject implements Mutable, Serializable { - - /** - * Required for serialization support. - * - * @see java.io.Serializable - */ - private static final long serialVersionUID = 86241875189L; - - /** The mutable value. */ - private T value; - - /** - * Constructs a new MutableObject with the default value of {@code null}. - */ - public MutableObject() { - } - - /** - * Constructs a new MutableObject with the specified value. - * - * @param value the initial value to store - */ - public MutableObject(final T value) { - this.value = value; - } - - // ----------------------------------------------------------------------- - /** - * Gets the value. - * - * @return the value, may be null - */ - @Override - public T getValue() { - return this.value; - } - - /** - * Sets the value. - * - * @param value the value to set - */ - @Override - public void setValue(final T value) { - this.value = value; - } - - // ----------------------------------------------------------------------- - /** - *

- * Compares this object against the specified object. The result is {@code true} - * if and only if the argument is not {@code null} and is a - * {@code MutableObject} object that contains the same {@code T} value as this - * object. - *

- * - * @param obj the object to compare with, {@code null} returns {@code false} - * @return {@code true} if the objects are the same; {@code true} if the objects - * have equivalent {@code value} fields; {@code false} otherwise. - */ - @Override - public boolean equals(final Object obj) { - if (obj == null) { - return false; - } - if (this == obj) { - return true; - } - if (this.getClass() == obj.getClass()) { - final MutableObject that = (MutableObject) obj; - return this.value.equals(that.value); - } - return false; - } - - /** - * Returns the value's hash code or {@code 0} if the value is {@code null}. - * - * @return the value's hash code or {@code 0} if the value is {@code null}. - */ - @Override - public int hashCode() { - return value == null ? 0 : value.hashCode(); - } - - // ----------------------------------------------------------------------- - /** - * Returns the String value of this mutable. - * - * @return the mutable value as a string - */ - @Override - public String toString() { - return value == null ? "null" : value.toString(); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/mutable/MutableShort.java b/src/main/java/org/apache/commons/lang3/mutable/MutableShort.java deleted file mode 100755 index 7351081c..00000000 --- a/src/main/java/org/apache/commons/lang3/mutable/MutableShort.java +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.mutable; - -import org.apache.commons.lang3.math.NumberUtils; - -/** - * A mutable {@code short} wrapper. - *

- * Note that as MutableShort does not extend Short, it is not treated by - * HString.format as a Short parameter. - * - * @see Short - * @since 2.1 - */ -public class MutableShort extends Number implements Comparable, Mutable { - - /** - * Required for serialization support. - * - * @see java.io.Serializable - */ - private static final long serialVersionUID = -2135791679L; - - /** The mutable value. */ - private short value; - - /** - * Constructs a new MutableShort with the default value of zero. - */ - public MutableShort() { - } - - /** - * Constructs a new MutableShort with the specified value. - * - * @param value the initial value to store - */ - public MutableShort(final short value) { - this.value = value; - } - - /** - * Constructs a new MutableShort with the specified value. - * - * @param value the initial value to store, not null - * @throws NullPointerException if the object is null - */ - public MutableShort(final Number value) { - this.value = value.shortValue(); - } - - /** - * Constructs a new MutableShort parsing the given string. - * - * @param value the string to parse, not null - * @throws NumberFormatException if the string cannot be parsed into a short - * @since 2.5 - */ - public MutableShort(final String value) { - this.value = Short.parseShort(value); - } - - // ----------------------------------------------------------------------- - /** - * Gets the value as a Short instance. - * - * @return the value as a Short, never null - */ - @Override - public Short getValue() { - return Short.valueOf(this.value); - } - - /** - * Sets the value. - * - * @param value the value to set - */ - public void setValue(final short value) { - this.value = value; - } - - /** - * Sets the value from any Number instance. - * - * @param value the value to set, not null - * @throws NullPointerException if the object is null - */ - @Override - public void setValue(final Number value) { - this.value = value.shortValue(); - } - - // ----------------------------------------------------------------------- - /** - * Increments the value. - * - * @since 2.2 - */ - public void increment() { - value++; - } - - /** - * Increments this instance's value by 1; this method returns the value - * associated with the instance immediately prior to the increment operation. - * This method is not thread safe. - * - * @return the value associated with the instance before it was incremented - * @since 3.5 - */ - public short getAndIncrement() { - final short last = value; - value++; - return last; - } - - /** - * Increments this instance's value by 1; this method returns the value - * associated with the instance immediately after the increment operation. This - * method is not thread safe. - * - * @return the value associated with the instance after it is incremented - * @since 3.5 - */ - public short incrementAndGet() { - value++; - return value; - } - - /** - * Decrements the value. - * - * @since 2.2 - */ - public void decrement() { - value--; - } - - /** - * Decrements this instance's value by 1; this method returns the value - * associated with the instance immediately prior to the decrement operation. - * This method is not thread safe. - * - * @return the value associated with the instance before it was decremented - * @since 3.5 - */ - public short getAndDecrement() { - final short last = value; - value--; - return last; - } - - /** - * Decrements this instance's value by 1; this method returns the value - * associated with the instance immediately after the decrement operation. This - * method is not thread safe. - * - * @return the value associated with the instance after it is decremented - * @since 3.5 - */ - public short decrementAndGet() { - value--; - return value; - } - - // ----------------------------------------------------------------------- - /** - * Adds a value to the value of this instance. - * - * @param operand the value to add, not null - * @since 2.2 - */ - public void add(final short operand) { - this.value += operand; - } - - /** - * Adds a value to the value of this instance. - * - * @param operand the value to add, not null - * @throws NullPointerException if the object is null - * @since 2.2 - */ - public void add(final Number operand) { - this.value += operand.shortValue(); - } - - /** - * Subtracts a value from the value of this instance. - * - * @param operand the value to subtract, not null - * @since 2.2 - */ - public void subtract(final short operand) { - this.value -= operand; - } - - /** - * Subtracts a value from the value of this instance. - * - * @param operand the value to subtract, not null - * @throws NullPointerException if the object is null - * @since 2.2 - */ - public void subtract(final Number operand) { - this.value -= operand.shortValue(); - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately after the addition operation. - * This method is not thread safe. - * - * @param operand the quantity to add, not null - * @return the value associated with this instance after adding the operand - * @since 3.5 - */ - public short addAndGet(final short operand) { - this.value += operand; - return value; - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately after the addition operation. - * This method is not thread safe. - * - * @param operand the quantity to add, not null - * @throws NullPointerException if {@code operand} is null - * @return the value associated with this instance after adding the operand - * @since 3.5 - */ - public short addAndGet(final Number operand) { - this.value += operand.shortValue(); - return value; - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately prior to the addition - * operation. This method is not thread safe. - * - * @param operand the quantity to add, not null - * @return the value associated with this instance immediately before the - * operand was added - * @since 3.5 - */ - public short getAndAdd(final short operand) { - final short last = value; - this.value += operand; - return last; - } - - /** - * Increments this instance's value by {@code operand}; this method returns the - * value associated with the instance immediately prior to the addition - * operation. This method is not thread safe. - * - * @param operand the quantity to add, not null - * @throws NullPointerException if {@code operand} is null - * @return the value associated with this instance immediately before the - * operand was added - * @since 3.5 - */ - public short getAndAdd(final Number operand) { - final short last = value; - this.value += operand.shortValue(); - return last; - } - - // ----------------------------------------------------------------------- - // byteValue relies on Number implementation - /** - * Returns the value of this MutableShort as a short. - * - * @return the numeric value represented by this object after conversion to type - * short. - */ - @Override - public short shortValue() { - return value; - } - - /** - * Returns the value of this MutableShort as an int. - * - * @return the numeric value represented by this object after conversion to type - * int. - */ - @Override - public int intValue() { - return value; - } - - /** - * Returns the value of this MutableShort as a long. - * - * @return the numeric value represented by this object after conversion to type - * long. - */ - @Override - public long longValue() { - return value; - } - - /** - * Returns the value of this MutableShort as a float. - * - * @return the numeric value represented by this object after conversion to type - * float. - */ - @Override - public float floatValue() { - return value; - } - - /** - * Returns the value of this MutableShort as a double. - * - * @return the numeric value represented by this object after conversion to type - * double. - */ - @Override - public double doubleValue() { - return value; - } - - // ----------------------------------------------------------------------- - /** - * Gets this mutable as an instance of Short. - * - * @return a Short instance containing the value from this mutable, never null - */ - public Short toShort() { - return Short.valueOf(shortValue()); - } - - // ----------------------------------------------------------------------- - /** - * Compares this object to the specified object. The result is {@code true} if - * and only if the argument is not {@code null} and is a {@code MutableShort} - * object that contains the same {@code short} value as this object. - * - * @param obj the object to compare with, null returns false - * @return {@code true} if the objects are the same; {@code false} otherwise. - */ - @Override - public boolean equals(final Object obj) { - if (obj instanceof MutableShort) { - return value == ((MutableShort) obj).shortValue(); - } - return false; - } - - /** - * Returns a suitable hash code for this mutable. - * - * @return a suitable hash code - */ - @Override - public int hashCode() { - return value; - } - - // ----------------------------------------------------------------------- - /** - * Compares this mutable to another in ascending order. - * - * @param other the other mutable to compare to, not null - * @return negative if this is less, zero if equal, positive if greater - */ - @Override - public int compareTo(final MutableShort other) { - return NumberUtils.compare(this.value, other.value); - } - - // ----------------------------------------------------------------------- - /** - * Returns the String value of this mutable. - * - * @return the mutable value as a string - */ - @Override - public String toString() { - return String.valueOf(value); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/mutable/package-info.java b/src/main/java/org/apache/commons/lang3/mutable/package-info.java deleted file mode 100755 index 4835fcf7..00000000 --- a/src/main/java/org/apache/commons/lang3/mutable/package-info.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - *

- * Provides typed mutable wrappers to primitive values and Object. These - * wrappers are similar to the wrappers provided by the Java API, but allow the - * wrapped value to be changed without needing to create a separate wrapper - * object. These classes are not thread-safe. - *

- * - * @since 2.1 - */ -package org.apache.commons.lang3.mutable; diff --git a/src/main/java/org/apache/commons/lang3/package-info.java b/src/main/java/org/apache/commons/lang3/package-info.java deleted file mode 100755 index 15125c0e..00000000 --- a/src/main/java/org/apache/commons/lang3/package-info.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - *

- * Provides highly reusable static utility methods, chiefly concerned with - * adding value to the {@link java.lang} classes. Most of these classes are - * immutable and thus thread-safe. However - * {@link org.apache.commons.lang3.CharSet} is not currently guaranteed - * thread-safe under all circumstances. - *

- * - *

- * The top level package contains various Utils classes, whilst there are - * various subpackages including {@link org.apache.commons.lang3.math}, - * {@link org.apache.commons.lang3.concurrent} and - * {@link org.apache.commons.lang3.builder}. Using the Utils classes is - * generally simplicity itself. They are the equivalent of global functions in - * another language, a collection of stand-alone, thread-safe, static methods. - * In contrast, subpackages may contain interfaces which may have to be - * implemented or classes which may need to be extended to get the full - * functionality from the code. They may, however, contain more global-like - * functions. - *

- * - *

- * Lang 3.0 requires JDK 1.5+, since Lang 3.2 it requires JDK 6+; The legacy - * release 2.6 requires JDK 1.2+. In both cases you can find features of later - * JDKs being maintained by us and likely to be removed or modified in favour of - * the JDK in the next major version. Note that Lang 3.0 uses a different - * package than its predecessors, allowing it to be used at the same time as an - * earlier version. - *

- * - *

- * You will find deprecated methods as you stroll through the Lang - * documentation. These are removed in the next major version. - *

- * - *

- * All util classes contain empty public constructors with warnings not to use. - * This may seem an odd thing to do, but it allows tools like Velocity to access - * the class as if it were a bean. In other words, yes we know about private - * constructors and have chosen not to use them. - *

- * - *

String manipulation - StringUtils, StringEscapeUtils, - * RandomStringUtils

- * - *

- * Lang has a series of String utilities. The first is - * {@link org.apache.commons.lang3.StringUtils}, oodles and oodles of functions - * which tweak, transform, squeeze and cuddle {@link java.lang.String - * java.lang.Strings}. In addition to StringUtils, there are a series of other - * String manipulating classes; - * {@link org.apache.commons.lang3.RandomStringUtils} and - * {@link org.apache.commons.lang3.StringEscapeUtils StringEscapeUtils}. - * RandomStringUtils speaks for itself. It's provides ways in which to generate - * pieces of text, such as might be used for default passwords. - * StringEscapeUtils contains methods to escape and unescape Java, JavaScript, - * JSON, HTML and XML. - *

- * - *

- * These are ideal classes to start using if you're looking to get into Lang. - * StringUtils' {@link org.apache.commons.lang3.StringUtils#capitalize(String)}, - * {@link org.apache.commons.lang3.StringUtils#substringBetween(String, String)}/{@link org.apache.commons.lang3.StringUtils#substringBefore(String, String) - * Before}/{@link org.apache.commons.lang3.StringUtils#substringAfter(String, String) - * After}, {@link org.apache.commons.lang3.StringUtils#split(String)} and - * {@link org.apache.commons.lang3.StringUtils#join(Object[])} are good methods - * to begin with. - *

- * - *

Character handling - CharSetUtils, CharSet, CharRange, CharUtils

- * - *

- * In addition to dealing with Strings, it's also important to deal with chars - * and Characters. {@link org.apache.commons.lang3.CharUtils} exists for this - * purpose, while {@link org.apache.commons.lang3.CharSetUtils} exists for - * set-manipulation of Strings. Be careful, although CharSetUtils takes an - * argument of type String, it is only as a set of characters. For example, - * {@code CharSetUtils.delete("testtest", "tr")} will remove all t's and all r's - * from the String, not just the String "tr". - *

- * - *

- * {@link org.apache.commons.lang3.CharRange} and - * {@link org.apache.commons.lang3.CharSet} are both used internally by - * CharSetUtils, and will probably rarely be used. - *

- * - *

JVM interaction - SystemUtils, CharEncoding

- * - *

- * SystemUtils is a simple little class which makes it easy to find out - * information about which platform you are on. For some, this is a necessary - * evil. It was never something I expected to use myself until I was trying to - * ensure that Commons Lang itself compiled under JDK 1.2. Having pushed out a - * few JDK 1.3 bits that had slipped in ({@code Collections.EMPTY_MAP} is a - * classic offender), I then found that one of the Unit Tests was dying - * mysteriously under JDK 1.2, but ran fine under JDK 1.3. There was no obvious - * solution and I needed to move onwards, so the simple solution was to wrap - * that particular test in a - * if (SystemUtils.isJavaVersionAtLeast(1.3f)) {, make a note and - * move on. - *

- * - *

- * The {@link org.apache.commons.lang3.CharEncoding} class is also used to - * interact with the Java environment and may be used to see which character - * encodings are supported in a particular environment. - *

- * - *

Serialization - SerializationUtils, SerializationException

- * - *

- * Serialization doesn't have to be that hard! A simple util class can take away - * the pain, plus it provides a method to clone an object by unserializing and - * reserializing, an old Java trick. - *

- * - *

Assorted functions - ObjectUtils, ClassUtils, ArrayUtils, - * BooleanUtils

- * - *

- * Would you believe it, {@link org.apache.commons.lang3.ObjectUtils} contains - * handy functions for Objects, mainly null-safe implementations of the methods - * on {@link java.lang.Object}. - *

- * - *

- * {@link org.apache.commons.lang3.ClassUtils} is largely a set of helper - * methods for reflection. Of special note are the comparators hidden away in - * ClassUtils, useful for sorting Class and Package objects by name; however - * they merely sort alphabetically and don't understand the common habit of - * sorting {@code java} and {@code javax} first. - *

- * - *

- * Next up, {@link org.apache.commons.lang3.ArrayUtils}. This is a big one with - * many methods and many overloads of these methods so it is probably worth an - * in depth look here. Before we begin, assume that every method mentioned is - * overloaded for all the primitives and for Object. Also, the short-hand 'xxx' - * implies a generic primitive type, but usually also includes Object. - *

- * - *
    - *
  • ArrayUtils provides singleton empty arrays for all the basic types. These - * will largely be of use in the Collections API with its toArray methods, but - * also will be of use with methods which want to return an empty array on - * error.
  • - *
  • {@code add(xxx[], xxx)} will add a primitive type to an array, resizing - * the array as you'd expect. Object is also supported.
  • - *
  • {@code clone(xxx[])} clones a primitive or Object array.
  • - *
  • {@code contains(xxx[], xxx)} searches for a primitive or Object in a - * primitive or Object array.
  • - *
  • {@code getLength(Object)} returns the length of any array or an - * IllegalArgumentException if the parameter is not an array. - * {@code hashCode(Object)}, {@code equals(Object, Object)}, - * {@code toString(Object)}
  • - *
  • {@code indexOf(xxx[], xxx)} and {@code indexOf(xxx[], xxx, int)} are - * copies of the classic String methods, but this time for primitive/Object - * arrays. In addition, a lastIndexOf set of methods exists.
  • - *
  • {@code isEmpty(xxx[])} lets you know if an array is zero-sized or null. - *
  • - *
  • {@code isSameLength(xxx[], xxx[])} returns true if the arrays are the - * same length.
  • - *
  • Along side the add methods, there are also remove methods of two types. - * The first type remove the value at an index, {@code remove(xxx[], int)}, - * while the second type remove the first value from the array, - * {@code remove(xxx[], xxx)}.
  • - *
  • Nearing the end now. The {@code reverse(xxx[])} method turns an array - * around.
  • - *
  • The {@code subarray(xxx[], int, int)} method splices an array out of a - * larger array.
  • - *
  • Primitive to primitive wrapper conversion is handled by the - * {@code toObject(xxx[])} and {@code toPrimitive(Xxx[])} methods.
  • - *
- * - *

- * Lastly, {@link org.apache.commons.lang3.ArrayUtils#toMap(Object[])} is worthy - * of special note. It is not a heavily overloaded method for working with - * arrays, but a simple way to create Maps from literals. - *

- * - *

Using toMap

- * - *
- * 
- * Map colorMap = ArrayUtils.toMap(new String[][] {{
- *   {"RED", "#FF0000"},
- *   {"GREEN", "#00FF00"},
- *   {"BLUE", "#0000FF"}
- * });
- * 
- * 
- * - *

- * Our final util class is {@link org.apache.commons.lang3.BooleanUtils}. It - * contains various Boolean acting methods, probably of most interest is the - * {@link org.apache.commons.lang3.BooleanUtils#toBoolean(String)} method which - * turns various positive/negative Strings into a Boolean object, and not just - * true/false as with Boolean.valueOf. - *

- * - *

Flotsam - BitField, Validate

- *

- * On reaching the end of our package, we are left with a couple of classes that - * haven't fit any of the topics so far. - *

- *

- * The {@link org.apache.commons.lang3.BitField} class provides a wrapper class - * around the classic bitmask integer, whilst the - * {@link org.apache.commons.lang3.Validate} class may be used for assertions - * (remember, we support Java 1.2). - *

- * - * @since 1.0 - */ -package org.apache.commons.lang3; diff --git a/src/main/java/org/apache/commons/lang3/stream/Streams.java b/src/main/java/org/apache/commons/lang3/stream/Streams.java deleted file mode 100755 index ae3ee621..00000000 --- a/src/main/java/org/apache/commons/lang3/stream/Streams.java +++ /dev/null @@ -1,558 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.stream; - -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.BinaryOperator; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.stream.Collector; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.apache.commons.lang3.function.Failable; -import org.apache.commons.lang3.function.FailableConsumer; -import org.apache.commons.lang3.function.FailableFunction; -import org.apache.commons.lang3.function.FailablePredicate; - -/** - * Provides utility functions, and classes for working with the - * {@code java.util.stream} package, or more generally, with Java 8 lambdas. - * More specifically, it attempts to address the fact that lambdas are supposed - * not to throw Exceptions, at least not checked Exceptions, AKA instances of - * {@link Exception}. This enforces the use of constructs like - * - *
- * Consumer<java.lang.reflect.Method> consumer = m -> {
- * 	try {
- * 		m.invoke(o, args);
- * 	} catch (Throwable t) {
- * 		throw Failable.rethrow(t);
- * 	}
- * };
- * stream.forEach(consumer);
- * 
- * - * Using a {@link FailableStream}, this can be rewritten as follows: - * - *
- * Streams.failable(stream).forEach((m) -> m.invoke(o, args));
- * 
- * - * Obviously, the second version is much more concise and the spirit of Lambda - * expressions is met better than in the first version. - * - * @see Stream - * @see Failable - * @since 3.11 - */ -public class Streams { - - /** - * A Collector type for arrays. - * - * @param The array type. - */ - public static class ArrayCollector implements Collector, O[]> { - private static final Set characteristics = Collections.emptySet(); - private final Class elementType; - - /** - * Constructs a new instance for the given element type. - * - * @param elementType The element type. - */ - public ArrayCollector(final Class elementType) { - this.elementType = elementType; - } - - @Override - public BiConsumer, O> accumulator() { - return List::add; - } - - @Override - public Set characteristics() { - return characteristics; - } - - @Override - public BinaryOperator> combiner() { - return (left, right) -> { - left.addAll(right); - return left; - }; - } - - @Override - public Function, O[]> finisher() { - return list -> { - @SuppressWarnings("unchecked") - final O[] array = (O[]) Array.newInstance(elementType, list.size()); - return list.toArray(array); - }; - } - - @Override - public Supplier> supplier() { - return ArrayList::new; - } - } - - /** - * A reduced, and simplified version of a {@link Stream} with failable method - * signatures. - * - * @param The streams element type. - */ - public static class FailableStream { - - private Stream stream; - private boolean terminated; - - /** - * Constructs a new instance with the given {@code stream}. - * - * @param stream The stream. - */ - public FailableStream(final Stream stream) { - this.stream = stream; - } - - /** - * Returns whether all elements of this stream match the provided predicate. May - * not evaluate the predicate on all elements if not necessary for determining - * the result. If the stream is empty then {@code true} is returned and the - * predicate is not evaluated. - * - *

- * This is a short-circuiting terminal operation. - * - * Note This method evaluates the universal quantification of the - * predicate over the elements of the stream (for all x P(x)). If the stream is - * empty, the quantification is said to be vacuously satisfied and is - * always {@code true} (regardless of P(x)). - * - * @param predicate A non-interfering, stateless predicate to apply to elements - * of this stream - * @return {@code true} If either all elements of the stream match the provided - * predicate or the stream is empty, otherwise {@code false}. - */ - public boolean allMatch(final FailablePredicate predicate) { - assertNotTerminated(); - return stream().allMatch(Failable.asPredicate(predicate)); - } - - /** - * Returns whether any elements of this stream match the provided predicate. May - * not evaluate the predicate on all elements if not necessary for determining - * the result. If the stream is empty then {@code false} is returned and the - * predicate is not evaluated. - * - *

- * This is a short-circuiting terminal operation. - * - * Note This method evaluates the existential quantification of the - * predicate over the elements of the stream (for some x P(x)). - * - * @param predicate A non-interfering, stateless predicate to apply to elements - * of this stream - * @return {@code true} if any elements of the stream match the provided - * predicate, otherwise {@code false} - */ - public boolean anyMatch(final FailablePredicate predicate) { - assertNotTerminated(); - return stream().anyMatch(Failable.asPredicate(predicate)); - } - - protected void assertNotTerminated() { - if (terminated) { - throw new IllegalStateException("This stream is already terminated."); - } - } - - /** - * Performs a mutable reduction operation on the elements of this stream using a - * {@code Collector}. A {@code Collector} encapsulates the functions used as - * arguments to {@link #collect(Supplier, BiConsumer, BiConsumer)}, allowing for - * reuse of collection strategies and composition of collect operations such as - * multiple-level grouping or partitioning. - * - *

- * If the underlying stream is parallel, and the {@code Collector} is - * concurrent, and either the stream is unordered or the collector is unordered, - * then a concurrent reduction will be performed (see {@link Collector} for - * details on concurrent reduction.) - * - *

- * This is a terminal operation. - * - *

- * When executed in parallel, multiple intermediate results may be instantiated, - * populated, and merged so as to maintain isolation of mutable data structures. - * Therefore, even when executed in parallel with non-thread-safe data - * structures (such as {@code ArrayList}), no additional synchronization is - * needed for a parallel reduction. - * - * Note The following will accumulate strings into an ArrayList: - * - *

-		 * {
-		 * 	@code
-		 * 	List asList = stringStream.collect(Collectors.toList());
-		 * }
-		 * 
- * - *

- * The following will classify {@code Person} objects by city: - * - *

-		 * {
-		 * 	@code
-		 * 	Map> peopleByCity = personStream.collect(Collectors.groupingBy(Person::getCity));
-		 * }
-		 * 
- * - *

- * The following will classify {@code Person} objects by state and city, - * cascading two {@code Collector}s together: - * - *

-		 * {
-		 * 	@code
-		 * 	Map>> peopleByStateAndCity = personStream
-		 * 			.collect(Collectors.groupingBy(Person::getState, Collectors.groupingBy(Person::getCity)));
-		 * }
-		 * 
- * - * @param the type of the result - * @param the intermediate accumulation type of the {@code Collector} - * @param collector the {@code Collector} describing the reduction - * @return the result of the reduction - * @see #collect(Supplier, BiConsumer, BiConsumer) - * @see Collectors - */ - public R collect(final Collector collector) { - makeTerminated(); - return stream().collect(collector); - } - - /** - * Performs a mutable reduction operation on the elements of this - * FailableStream. A mutable reduction is one in which the reduced value is a - * mutable result container, such as an {@code ArrayList}, and elements are - * incorporated by updating the state of the result rather than by replacing the - * result. This produces a result equivalent to: - * - *
-		 * {@code
-		 *     R result = supplier.get();
-		 *     for (T element : this stream)
-		 *         accumulator.accept(result, element);
-		 *     return result;
-		 * }
-		 * 
- * - *

- * Like {@link #reduce(Object, BinaryOperator)}, {@code collect} operations can - * be parallelized without requiring additional synchronization. - * - *

- * This is a terminal operation. - * - * Note There are many existing classes in the JDK whose signatures are - * well-suited for use with method references as arguments to {@code collect()}. - * For example, the following will accumulate strings into an {@code ArrayList}: - * - *

-		 * {
-		 * 	@code
-		 * 	List asList = stringStream.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
-		 * }
-		 * 
- * - *

- * The following will take a stream of strings and concatenates them into a - * single string: - * - *

-		 * {
-		 * 	@code
-		 * 	String concat = stringStream.collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
-		 * 			.toString();
-		 * }
-		 * 
- * - * @param type of the result - * @param
Type of the accumulator. - * @param pupplier a function that creates a new result container. For a - * parallel execution, this function may be called multiple - * times and must return a fresh value each time. - * @param accumulator An associative, non-interfering, stateless function for - * incorporating an additional element into a result - * @param combiner An associative, non-interfering, stateless function for - * combining two values, which must be compatible with the - * accumulator function - * @return The result of the reduction - */ - public R collect(final Supplier pupplier, final BiConsumer accumulator, - final BiConsumer combiner) { - makeTerminated(); - return stream().collect(pupplier, accumulator, combiner); - } - - /** - * Returns a FailableStream consisting of the elements of this stream that match - * the given FailablePredicate. - * - *

- * This is an intermediate operation. - * - * @param predicate a non-interfering, stateless predicate to apply to each - * element to determine if it should be included. - * @return the new stream - */ - public FailableStream filter(final FailablePredicate predicate) { - assertNotTerminated(); - stream = stream.filter(Failable.asPredicate(predicate)); - return this; - } - - /** - * Performs an action for each element of this stream. - * - *

- * This is a terminal operation. - * - *

- * The behavior of this operation is explicitly nondeterministic. For parallel - * stream pipelines, this operation does not guarantee to respect the - * encounter order of the stream, as doing so would sacrifice the benefit of - * parallelism. For any given element, the action may be performed at whatever - * time and in whatever thread the library chooses. If the action accesses - * shared state, it is responsible for providing the required synchronization. - * - * @param action a non-interfering action to perform on the elements - */ - public void forEach(final FailableConsumer action) { - makeTerminated(); - stream().forEach(Failable.asConsumer(action)); - } - - protected void makeTerminated() { - assertNotTerminated(); - terminated = true; - } - - /** - * Returns a stream consisting of the results of applying the given function to - * the elements of this stream. - * - *

- * This is an intermediate operation. - * - * @param The element type of the new stream - * @param mapper A non-interfering, stateless function to apply to each element - * @return the new stream - */ - public FailableStream map(final FailableFunction mapper) { - assertNotTerminated(); - return new FailableStream<>(stream.map(Failable.asFunction(mapper))); - } - - /** - * Performs a reduction on the elements of this stream, using the provided - * identity value and an associative accumulation function, and returns the - * reduced value. This is equivalent to: - * - *

-		 * {@code
-		 *     T result = identity;
-		 *     for (T element : this stream)
-		 *         result = accumulator.apply(result, element)
-		 *     return result;
-		 * }
-		 * 
- * - * but is not constrained to execute sequentially. - * - *

- * The {@code identity} value must be an identity for the accumulator function. - * This means that for all {@code t}, {@code accumulator.apply(identity, t)} is - * equal to {@code t}. The {@code accumulator} function must be an associative - * function. - * - *

- * This is a terminal operation. - * - * Note Sum, min, max, average, and string concatenation are all special cases - * of reduction. Summing a stream of numbers can be expressed as: - * - *

-		 * {
-		 * 	@code
-		 * 	Integer sum = integers.reduce(0, (a, b) -> a + b);
-		 * }
-		 * 
- * - * or: - * - *
-		 * {
-		 * 	@code
-		 * 	Integer sum = integers.reduce(0, Integer::sum);
-		 * }
-		 * 
- * - *

- * While this may seem a more roundabout way to perform an aggregation compared - * to simply mutating a running total in a loop, reduction operations - * parallelize more gracefully, without needing additional synchronization and - * with greatly reduced risk of data races. - * - * @param identity the identity value for the accumulating function - * @param accumulator an associative, non-interfering, stateless function for - * combining two values - * @return the result of the reduction - */ - public O reduce(final O identity, final BinaryOperator accumulator) { - makeTerminated(); - return stream().reduce(identity, accumulator); - } - - /** - * Converts the FailableStream into an equivalent stream. - * - * @return A stream, which will return the same elements, which this - * FailableStream would return. - */ - public Stream stream() { - return stream; - } - } - - /** - * Converts the given {@link Collection} into a {@link FailableStream}. This is - * basically a simplified, reduced version of the {@link Stream} class, with the - * same underlying element stream, except that failable objects, like - * {@link FailablePredicate}, {@link FailableFunction}, or - * {@link FailableConsumer} may be applied, instead of {@link Predicate}, - * {@link Function}, or {@link Consumer}. The idea is to rewrite a code snippet - * like this: - * - *

-	 * final List<O> list;
-	 * final Method m;
-	 * final Function<O, String> mapper = (o) -> {
-	 * 	try {
-	 * 		return (String) m.invoke(o);
-	 * 	} catch (Throwable t) {
-	 * 		throw Failable.rethrow(t);
-	 * 	}
-	 * };
-	 * final List<String> strList = list.stream().map(mapper).collect(Collectors.toList());
-	 * 
- * - * as follows: - * - *
-	 * final List<O> list;
-	 * final Method m;
-	 * final List<String> strList = Failable.stream(list.stream()).map((o) -> (String) m.invoke(o))
-	 * 		.collect(Collectors.toList());
-	 * 
- * - * While the second version may not be quite as efficient (because it - * depends on the creation of additional, intermediate objects, of type - * FailableStream), it is much more concise, and readable, and meets the spirit - * of Lambdas better than the first version. - * - * @param The streams element type. - * @param stream The stream, which is being converted. - * @return The {@link FailableStream}, which has been created by converting the - * stream. - */ - public static FailableStream stream(final Collection stream) { - return stream(stream.stream()); - } - - /** - * Converts the given {@link Stream stream} into a {@link FailableStream}. This - * is basically a simplified, reduced version of the {@link Stream} class, with - * the same underlying element stream, except that failable objects, like - * {@link FailablePredicate}, {@link FailableFunction}, or - * {@link FailableConsumer} may be applied, instead of {@link Predicate}, - * {@link Function}, or {@link Consumer}. The idea is to rewrite a code snippet - * like this: - * - *
-	 * final List<O> list;
-	 * final Method m;
-	 * final Function<O, String> mapper = (o) -> {
-	 * 	try {
-	 * 		return (String) m.invoke(o);
-	 * 	} catch (Throwable t) {
-	 * 		throw Failable.rethrow(t);
-	 * 	}
-	 * };
-	 * final List<String> strList = list.stream().map(mapper).collect(Collectors.toList());
-	 * 
- * - * as follows: - * - *
-	 * final List<O> list;
-	 * final Method m;
-	 * final List<String> strList = Failable.stream(list.stream()).map((o) -> (String) m.invoke(o))
-	 * 		.collect(Collectors.toList());
-	 * 
- * - * While the second version may not be quite as efficient (because it - * depends on the creation of additional, intermediate objects, of type - * FailableStream), it is much more concise, and readable, and meets the spirit - * of Lambdas better than the first version. - * - * @param The streams element type. - * @param stream The stream, which is being converted. - * @return The {@link FailableStream}, which has been created by converting the - * stream. - */ - public static FailableStream stream(final Stream stream) { - return new FailableStream<>(stream); - } - - /** - * Returns a {@code Collector} that accumulates the input elements into a new - * array. - * - * @param pElementType Type of an element in the array. - * @param the type of the input elements - * @return a {@code Collector} which collects all the input elements into an - * array, in encounter order - */ - public static Collector toArray(final Class pElementType) { - return new ArrayCollector<>(pElementType); - } -} diff --git a/src/main/java/org/apache/commons/lang3/stream/package-info.java b/src/main/java/org/apache/commons/lang3/stream/package-info.java deleted file mode 100755 index 6c7c1bee..00000000 --- a/src/main/java/org/apache/commons/lang3/stream/package-info.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Provides utility classes to complement those in {@code java.util.stream}. - * - *

- * Contains utilities to allow streaming of failable functional interfaces from - * the {@code org.apache.commons.lang3.functions} package allowing streaming of - * functional expressions that may raise an Exception. - * - * @since 3.11 - */ -package org.apache.commons.lang3.stream; diff --git a/src/main/java/org/apache/commons/lang3/text/CompositeFormat.java b/src/main/java/org/apache/commons/lang3/text/CompositeFormat.java deleted file mode 100755 index cca78ce9..00000000 --- a/src/main/java/org/apache/commons/lang3/text/CompositeFormat.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.text; - -import java.text.FieldPosition; -import java.text.Format; -import java.text.ParseException; -import java.text.ParsePosition; - -/** - * Formats using one formatter and parses using a different formatter. An - * example of use for this would be a webapp where data is taken in one way and - * stored in a database another way. - * - * @!deprecated as of 3.6, use commons-text - * CompositeFormat instead - */ -//@Deprecated -public class CompositeFormat extends Format { - - /** - * Required for serialization support. - * - * @see java.io.Serializable - */ - private static final long serialVersionUID = -4329119827877627683L; - - /** The parser to use. */ - private final Format parser; - /** The formatter to use. */ - private final Format formatter; - - /** - * Create a format that points its parseObject method to one implementation and - * its format method to another. - * - * @param parser implementation - * @param formatter implementation - */ - public CompositeFormat(final Format parser, final Format formatter) { - this.parser = parser; - this.formatter = formatter; - } - - /** - * Uses the formatter Format instance. - * - * @param obj the object to format - * @param toAppendTo the {@link StringBuffer} to append to - * @param pos the FieldPosition to use (or ignore). - * @return {@code toAppendTo} - * @see Format#format(Object, StringBuffer, FieldPosition) - */ - @Override // Therefore has to use StringBuffer - public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) { - return formatter.format(obj, toAppendTo, pos); - } - - /** - * Uses the parser Format instance. - * - * @param source the String source - * @param pos the ParsePosition containing the position to parse from, will - * be updated according to parsing success (index) or failure - * (error index) - * @return the parsed Object - * @see Format#parseObject(String, ParsePosition) - */ - @Override - public Object parseObject(final String source, final ParsePosition pos) { - return parser.parseObject(source, pos); - } - - /** - * Provides access to the parser Format implementation. - * - * @return parser Format implementation - */ - public Format getParser() { - return this.parser; - } - - /** - * Provides access to the parser Format implementation. - * - * @return formatter Format implementation - */ - public Format getFormatter() { - return this.formatter; - } - - /** - * Utility method to parse and then reformat a String. - * - * @param input String to reformat - * @return A reformatted String - * @throws ParseException thrown by parseObject(String) call - */ - public String reformat(final String input) throws ParseException { - return format(parseObject(input)); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/text/ExtendedMessageFormat.java b/src/main/java/org/apache/commons/lang3/text/ExtendedMessageFormat.java deleted file mode 100755 index 78e6a8e3..00000000 --- a/src/main/java/org/apache/commons/lang3/text/ExtendedMessageFormat.java +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.text; - -import java.text.Format; -import java.text.MessageFormat; -import java.text.ParsePosition; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; - -import org.apache.commons.lang3.LocaleUtils; -import org.apache.commons.lang3.ObjectUtils; -import org.apache.commons.lang3.Validate; - -/** - * Extends {@code java.text.MessageFormat} to allow pluggable/additional - * formatting options for embedded format elements. Client code should specify a - * registry of {@code FormatFactory} instances associated with {@code String} - * format names. This registry will be consulted when the format elements are - * parsed from the message pattern. In this way custom patterns can be - * specified, and the formats supported by {@code java.text.MessageFormat} can - * be overridden at the format and/or format style level (see MessageFormat). A - * "format element" embedded in the message pattern is specified (()? - * signifies optionality):
- * {argument-number({@code ,}format-name - * ({@code ,}format-style)?)?} - * - *

- * format-name and format-style values are trimmed of surrounding - * whitespace in the manner of {@code java.text.MessageFormat}. If - * format-name denotes {@code FormatFactory formatFactoryInstance} in - * {@code registry}, a {@code Format} matching format-name and - * format-style is requested from {@code formatFactoryInstance}. If this - * is successful, the {@code Format} found is used for this format element. - *

- * - *

- * NOTICE: The various subformat mutator methods are considered - * unnecessary; they exist on the parent class to allow the type of - * customization which it is the job of this class to provide in a configurable - * fashion. These methods have thus been disabled and will throw - * {@code UnsupportedOperationException} if called. - *

- * - *

- * Limitations inherited from {@code java.text.MessageFormat}: - *

- *
    - *
  • When using "choice" subformats, support for nested formatting - * instructions is limited to that provided by the base class.
  • - *
  • Thread-safety of {@code Format}s, including {@code MessageFormat} and - * thus {@code ExtendedMessageFormat}, is not guaranteed.
  • - *
- * - * @since 2.4 - * @!deprecated as of 3.6, use commons-text - * ExtendedMessageFormat instead - */ -//@Deprecated -public class ExtendedMessageFormat extends MessageFormat { - private static final long serialVersionUID = -2362048321261811743L; - private static final int HASH_SEED = 31; - - private static final String DUMMY_PATTERN = ""; - private static final char START_FMT = ','; - private static final char END_FE = '}'; - private static final char START_FE = '{'; - private static final char QUOTE = '\''; - - private String toPattern; - private final Map registry; - - /** - * Create a new ExtendedMessageFormat for the default locale. - * - * @param pattern the pattern to use, not null - * @throws IllegalArgumentException in case of a bad pattern. - */ - public ExtendedMessageFormat(final String pattern) { - this(pattern, Locale.getDefault()); - } - - /** - * Create a new ExtendedMessageFormat. - * - * @param pattern the pattern to use, not null - * @param locale the locale to use, not null - * @throws IllegalArgumentException in case of a bad pattern. - */ - public ExtendedMessageFormat(final String pattern, final Locale locale) { - this(pattern, locale, null); - } - - /** - * Create a new ExtendedMessageFormat for the default locale. - * - * @param pattern the pattern to use, not null - * @param registry the registry of format factories, may be null - * @throws IllegalArgumentException in case of a bad pattern. - */ - public ExtendedMessageFormat(final String pattern, final Map registry) { - this(pattern, Locale.getDefault(), registry); - } - - /** - * Create a new ExtendedMessageFormat. - * - * @param pattern the pattern to use, not null. - * @param locale the locale to use. - * @param registry the registry of format factories, may be null. - * @throws IllegalArgumentException in case of a bad pattern. - */ - public ExtendedMessageFormat(final String pattern, final Locale locale, - final Map registry) { - super(DUMMY_PATTERN); - setLocale(LocaleUtils.toLocale(locale)); - this.registry = registry; - applyPattern(pattern); - } - - /** - * {@inheritDoc} - */ - @Override - public String toPattern() { - return toPattern; - } - - /** - * Apply the specified pattern. - * - * @param pattern String - */ - @Override - public final void applyPattern(final String pattern) { - if (registry == null) { - super.applyPattern(pattern); - toPattern = super.toPattern(); - return; - } - final ArrayList foundFormats = new ArrayList<>(); - final ArrayList foundDescriptions = new ArrayList<>(); - final StringBuilder stripCustom = new StringBuilder(pattern.length()); - - final ParsePosition pos = new ParsePosition(0); - final char[] c = pattern.toCharArray(); - int fmtCount = 0; - while (pos.getIndex() < pattern.length()) { - switch (c[pos.getIndex()]) { - case QUOTE: - appendQuotedString(pattern, pos, stripCustom); - break; - case START_FE: - fmtCount++; - seekNonWs(pattern, pos); - final int start = pos.getIndex(); - final int index = readArgumentIndex(pattern, next(pos)); - stripCustom.append(START_FE).append(index); - seekNonWs(pattern, pos); - Format format = null; - String formatDescription = null; - if (c[pos.getIndex()] == START_FMT) { - formatDescription = parseFormatDescription(pattern, next(pos)); - format = getFormat(formatDescription); - if (format == null) { - stripCustom.append(START_FMT).append(formatDescription); - } - } - foundFormats.add(format); - foundDescriptions.add(format == null ? null : formatDescription); - Validate.isTrue(foundFormats.size() == fmtCount); - Validate.isTrue(foundDescriptions.size() == fmtCount); - if (c[pos.getIndex()] != END_FE) { - throw new IllegalArgumentException("Unreadable format element at position " + start); - } - //$FALL-THROUGH$ - default: - stripCustom.append(c[pos.getIndex()]); - next(pos); - } - } - super.applyPattern(stripCustom.toString()); - toPattern = insertFormats(super.toPattern(), foundDescriptions); - if (containsElements(foundFormats)) { - final Format[] origFormats = getFormats(); - // only loop over what we know we have, as MessageFormat on Java 1.3 - // seems to provide an extra format element: - int i = 0; - for (final Iterator it = foundFormats.iterator(); it.hasNext(); i++) { - final Format f = it.next(); - if (f != null) { - origFormats[i] = f; - } - } - super.setFormats(origFormats); - } - } - - /** - * Throws UnsupportedOperationException - see class Javadoc for details. - * - * @param formatElementIndex format element index - * @param newFormat the new format - * @throws UnsupportedOperationException always thrown since this isn't - * supported by ExtendMessageFormat - */ - @Override - public void setFormat(final int formatElementIndex, final Format newFormat) { - throw new UnsupportedOperationException(); - } - - /** - * Throws UnsupportedOperationException - see class Javadoc for details. - * - * @param argumentIndex argument index - * @param newFormat the new format - * @throws UnsupportedOperationException always thrown since this isn't - * supported by ExtendMessageFormat - */ - @Override - public void setFormatByArgumentIndex(final int argumentIndex, final Format newFormat) { - throw new UnsupportedOperationException(); - } - - /** - * Throws UnsupportedOperationException - see class Javadoc for details. - * - * @param newFormats new formats - * @throws UnsupportedOperationException always thrown since this isn't - * supported by ExtendMessageFormat - */ - @Override - public void setFormats(final Format[] newFormats) { - throw new UnsupportedOperationException(); - } - - /** - * Throws UnsupportedOperationException - see class Javadoc for details. - * - * @param newFormats new formats - * @throws UnsupportedOperationException always thrown since this isn't - * supported by ExtendMessageFormat - */ - @Override - public void setFormatsByArgumentIndex(final Format[] newFormats) { - throw new UnsupportedOperationException(); - } - - /** - * Check if this extended message format is equal to another object. - * - * @param obj the object to compare to - * @return true if this object equals the other, otherwise false - */ - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } - if (obj == null) { - return false; - } - if (!super.equals(obj)) { - return false; - } - if (ObjectUtils.notEqual(getClass(), obj.getClass())) { - return false; - } - final ExtendedMessageFormat rhs = (ExtendedMessageFormat) obj; - if (ObjectUtils.notEqual(toPattern, rhs.toPattern)) { - return false; - } - return !ObjectUtils.notEqual(registry, rhs.registry); - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - int result = super.hashCode(); - result = HASH_SEED * result + Objects.hashCode(registry); - result = HASH_SEED * result + Objects.hashCode(toPattern); - return result; - } - - /** - * Gets a custom format from a format description. - * - * @param desc String - * @return Format - */ - private Format getFormat(final String desc) { - if (registry != null) { - String name = desc; - String args = null; - final int i = desc.indexOf(START_FMT); - if (i > 0) { - name = desc.substring(0, i).trim(); - args = desc.substring(i + 1).trim(); - } - final FormatFactory factory = registry.get(name); - if (factory != null) { - return factory.getFormat(name, args, getLocale()); - } - } - return null; - } - - /** - * Read the argument index from the current format element - * - * @param pattern pattern to parse - * @param pos current parse position - * @return argument index - */ - private int readArgumentIndex(final String pattern, final ParsePosition pos) { - final int start = pos.getIndex(); - seekNonWs(pattern, pos); - final StringBuilder result = new StringBuilder(); - boolean error = false; - for (; !error && pos.getIndex() < pattern.length(); next(pos)) { - char c = pattern.charAt(pos.getIndex()); - if (Character.isWhitespace(c)) { - seekNonWs(pattern, pos); - c = pattern.charAt(pos.getIndex()); - if (c != START_FMT && c != END_FE) { - error = true; - continue; - } - } - if ((c == START_FMT || c == END_FE) && result.length() > 0) { - try { - return Integer.parseInt(result.toString()); - } catch (final NumberFormatException e) { // NOPMD - // we've already ensured only digits, so unless something - // outlandishly large was specified we should be okay. - } - } - error = !Character.isDigit(c); - result.append(c); - } - if (error) { - throw new IllegalArgumentException("Invalid format argument index at position " + start + ": " - + pattern.substring(start, pos.getIndex())); - } - throw new IllegalArgumentException("Unterminated format element at position " + start); - } - - /** - * Parse the format component of a format element. - * - * @param pattern string to parse - * @param pos current parse position - * @return Format description String - */ - private String parseFormatDescription(final String pattern, final ParsePosition pos) { - final int start = pos.getIndex(); - seekNonWs(pattern, pos); - final int text = pos.getIndex(); - int depth = 1; - for (; pos.getIndex() < pattern.length(); next(pos)) { - switch (pattern.charAt(pos.getIndex())) { - case START_FE: - depth++; - break; - case END_FE: - depth--; - if (depth == 0) { - return pattern.substring(text, pos.getIndex()); - } - break; - case QUOTE: - getQuotedString(pattern, pos); - break; - default: - break; - } - } - throw new IllegalArgumentException("Unterminated format element at position " + start); - } - - /** - * Insert formats back into the pattern for toPattern() support. - * - * @param pattern source - * @param customPatterns The custom patterns to re-insert, if any - * @return full pattern - */ - private String insertFormats(final String pattern, final ArrayList customPatterns) { - if (!containsElements(customPatterns)) { - return pattern; - } - final StringBuilder sb = new StringBuilder(pattern.length() * 2); - final ParsePosition pos = new ParsePosition(0); - int fe = -1; - int depth = 0; - while (pos.getIndex() < pattern.length()) { - final char c = pattern.charAt(pos.getIndex()); - switch (c) { - case QUOTE: - appendQuotedString(pattern, pos, sb); - break; - case START_FE: - depth++; - sb.append(START_FE).append(readArgumentIndex(pattern, next(pos))); - // do not look for custom patterns when they are embedded, e.g. in a choice - if (depth == 1) { - fe++; - final String customPattern = customPatterns.get(fe); - if (customPattern != null) { - sb.append(START_FMT).append(customPattern); - } - } - break; - case END_FE: - depth--; - //$FALL-THROUGH$ - default: - sb.append(c); - next(pos); - } - } - return sb.toString(); - } - - /** - * Consume whitespace from the current parse position. - * - * @param pattern String to read - * @param pos current position - */ - private void seekNonWs(final String pattern, final ParsePosition pos) { - int len = 0; - final char[] buffer = pattern.toCharArray(); - do { - len = StrMatcher.splitMatcher().isMatch(buffer, pos.getIndex()); - pos.setIndex(pos.getIndex() + len); - } while (len > 0 && pos.getIndex() < pattern.length()); - } - - /** - * Convenience method to advance parse position by 1 - * - * @param pos ParsePosition - * @return {@code pos} - */ - private ParsePosition next(final ParsePosition pos) { - pos.setIndex(pos.getIndex() + 1); - return pos; - } - - /** - * Consume a quoted string, adding it to {@code appendTo} if specified. - * - * @param pattern pattern to parse - * @param pos current parse position - * @param appendTo optional StringBuilder to append - * @return {@code appendTo} - */ - private StringBuilder appendQuotedString(final String pattern, final ParsePosition pos, - final StringBuilder appendTo) { - assert pattern.toCharArray()[pos.getIndex()] == QUOTE : "Quoted string must start with quote character"; - - // handle quote character at the beginning of the string - if (appendTo != null) { - appendTo.append(QUOTE); - } - next(pos); - - final int start = pos.getIndex(); - final char[] c = pattern.toCharArray(); - final int lastHold = start; - for (int i = pos.getIndex(); i < pattern.length(); i++) { - if (c[pos.getIndex()] == QUOTE) { - next(pos); - return appendTo == null ? null : appendTo.append(c, lastHold, pos.getIndex() - lastHold); - } - next(pos); - } - throw new IllegalArgumentException("Unterminated quoted string at position " + start); - } - - /** - * Consume quoted string only - * - * @param pattern pattern to parse - * @param pos current parse position - */ - private void getQuotedString(final String pattern, final ParsePosition pos) { - appendQuotedString(pattern, pos, null); - } - - /** - * Learn whether the specified Collection contains non-null elements. - * - * @param coll to check - * @return {@code true} if some Object was found, {@code false} otherwise. - */ - private boolean containsElements(final Collection coll) { - if (coll == null || coll.isEmpty()) { - return false; - } - for (final Object name : coll) { - if (name != null) { - return true; - } - } - return false; - } -} diff --git a/src/main/java/org/apache/commons/lang3/text/FormatFactory.java b/src/main/java/org/apache/commons/lang3/text/FormatFactory.java deleted file mode 100755 index 3ff4b6f7..00000000 --- a/src/main/java/org/apache/commons/lang3/text/FormatFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.text; - -import java.text.Format; -import java.util.Locale; - -/** - * Format factory. - * - * @since 2.4 - * @!deprecated as of 3.6, use commons-text - * FormatFactory instead - */ -//@Deprecated -public interface FormatFactory { - - /** - * Create or retrieve a format instance. - * - * @param name The format type name - * @param arguments Arguments used to create the format instance. This allows - * the {@code FormatFactory} to implement the "format style" - * concept from {@code java.text.MessageFormat}. - * @param locale The locale, may be null - * @return The format instance - */ - Format getFormat(String name, String arguments, Locale locale); - -} diff --git a/src/main/java/org/apache/commons/lang3/text/FormattableUtils.java b/src/main/java/org/apache/commons/lang3/text/FormattableUtils.java deleted file mode 100755 index fdead3d2..00000000 --- a/src/main/java/org/apache/commons/lang3/text/FormattableUtils.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.text; - -import static java.util.FormattableFlags.LEFT_JUSTIFY; - -import java.util.Formattable; -import java.util.Formatter; - -import org.apache.commons.lang3.ObjectUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Validate; - -import net.lax1dude.eaglercraft.v1_8.HString; - -/** - *

- * Provides utilities for working with the {@code Formattable} interface. - *

- * - *

- * The {@link Formattable} interface provides basic control over formatting when - * using a {@code Formatter}. It is primarily concerned with numeric precision - * and padding, and is not designed to allow generalised alternate formats. - *

- * - * @since 3.0 - * @!deprecated as of 3.6, use commons-text - * FormattableUtils instead - */ -//@Deprecated -public class FormattableUtils { - - /** - * A format that simply outputs the value as a string. - */ - private static final String SIMPLEST_FORMAT = "%s"; - - /** - *

- * {@code FormattableUtils} instances should NOT be constructed in standard - * programming. Instead, the methods of the class should be invoked statically. - *

- * - *

- * This constructor is public to permit tools that require a JavaBean instance - * to operate. - *

- */ - public FormattableUtils() { - } - - // ----------------------------------------------------------------------- - /** - * Gets the default formatted representation of the specified - * {@code Formattable}. - * - * @param formattable the instance to convert to a string, not null - * @return the resulting string, not null - */ - public static String toString(final Formattable formattable) { - return HString.format(SIMPLEST_FORMAT, formattable); - } - - /** - * Handles the common {@code Formattable} operations of truncate-pad-append, - * with no ellipsis on precision overflow, and padding width underflow with - * spaces. - * - * @param seq the string to handle, not null - * @param formatter the destination formatter, not null - * @param flags the flags for formatting, see {@code Formattable} - * @param width the width of the output, see {@code Formattable} - * @param precision the precision of the output, see {@code Formattable} - * @return the {@code formatter} instance, not null - */ - public static Formatter append(final CharSequence seq, final Formatter formatter, final int flags, final int width, - final int precision) { - return append(seq, formatter, flags, width, precision, ' ', null); - } - - /** - * Handles the common {@link Formattable} operations of truncate-pad-append, - * with no ellipsis on precision overflow. - * - * @param seq the string to handle, not null - * @param formatter the destination formatter, not null - * @param flags the flags for formatting, see {@code Formattable} - * @param width the width of the output, see {@code Formattable} - * @param precision the precision of the output, see {@code Formattable} - * @param padChar the pad character to use - * @return the {@code formatter} instance, not null - */ - public static Formatter append(final CharSequence seq, final Formatter formatter, final int flags, final int width, - final int precision, final char padChar) { - return append(seq, formatter, flags, width, precision, padChar, null); - } - - /** - * Handles the common {@link Formattable} operations of truncate-pad-append, - * padding width underflow with spaces. - * - * @param seq the string to handle, not null - * @param formatter the destination formatter, not null - * @param flags the flags for formatting, see {@code Formattable} - * @param width the width of the output, see {@code Formattable} - * @param precision the precision of the output, see {@code Formattable} - * @param ellipsis the ellipsis to use when precision dictates truncation, null - * or empty causes a hard truncation - * @return the {@code formatter} instance, not null - */ - public static Formatter append(final CharSequence seq, final Formatter formatter, final int flags, final int width, - final int precision, final CharSequence ellipsis) { - return append(seq, formatter, flags, width, precision, ' ', ellipsis); - } - - /** - * Handles the common {@link Formattable} operations of truncate-pad-append. - * - * @param seq the string to handle, not null - * @param formatter the destination formatter, not null - * @param flags the flags for formatting, see {@code Formattable} - * @param width the width of the output, see {@code Formattable} - * @param precision the precision of the output, see {@code Formattable} - * @param padChar the pad character to use - * @param ellipsis the ellipsis to use when precision dictates truncation, null - * or empty causes a hard truncation - * @return the {@code formatter} instance, not null - */ - public static Formatter append(final CharSequence seq, final Formatter formatter, final int flags, final int width, - final int precision, final char padChar, final CharSequence ellipsis) { - Validate.isTrue(ellipsis == null || precision < 0 || ellipsis.length() <= precision, - "Specified ellipsis '%1$s' exceeds precision of %2$s", ellipsis, Integer.valueOf(precision)); - final StringBuilder buf = new StringBuilder(seq); - if (precision >= 0 && precision < seq.length()) { - final CharSequence _ellipsis = ObjectUtils.defaultIfNull(ellipsis, StringUtils.EMPTY); - buf.replace(precision - _ellipsis.length(), seq.length(), _ellipsis.toString()); - } - final boolean leftJustify = (flags & LEFT_JUSTIFY) == LEFT_JUSTIFY; - for (int i = buf.length(); i < width; i++) { - buf.insert(leftJustify ? i : 0, padChar); - } - formatter.format(buf.toString()); - return formatter; - } - -} diff --git a/src/main/java/org/apache/commons/lang3/text/StrBuilder.java b/src/main/java/org/apache/commons/lang3/text/StrBuilder.java deleted file mode 100755 index f403ce0b..00000000 --- a/src/main/java/org/apache/commons/lang3/text/StrBuilder.java +++ /dev/null @@ -1,3143 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.text; - -import java.io.IOException; -import java.io.Reader; -import java.io.Serializable; -import java.io.Writer; -import java.nio.CharBuffer; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; - -import net.lax1dude.eaglercraft.v1_8.HString; - -import org.apache.commons.lang3.CharUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.builder.Builder; - -/** - * Builds a string from constituent parts providing a more flexible and powerful - * API than StringBuffer. - *

- * The main differences from StringBuffer/StringBuilder are: - *

- *
    - *
  • Not synchronized
  • - *
  • Not final
  • - *
  • Subclasses have direct access to character array
  • - *
  • Additional methods - *
      - *
    • appendWithSeparators - adds an array of values, with a separator
    • - *
    • appendPadding - adds a length padding characters
    • - *
    • appendFixedLength - adds a fixed width field to the builder
    • - *
    • toCharArray/getChars - simpler ways to get a range of the character - * array
    • - *
    • delete - delete char or string
    • - *
    • replace - search and replace for a char or string
    • - *
    • leftString/rightString/midString - substring without exceptions
    • - *
    • contains - whether the builder contains a char or string
    • - *
    • size/clear/isEmpty - collections style API methods
    • - *
    - *
  • - *
  • Views - *
      - *
    • asTokenizer - uses the internal buffer as the source of a - * StrTokenizer
    • - *
    • asReader - uses the internal buffer as the source of a Reader
    • - *
    • asWriter - allows a Writer to write directly to the internal buffer
    • - *
    - *
  • - *
- *

- * The aim has been to provide an API that mimics very closely what StringBuffer - * provides, but with additional methods. It should be noted that some edge - * cases, with invalid indices or null input, have been altered - see individual - * methods. The biggest of these changes is that by default, null will not - * output the text 'null'. This can be controlled by a property, - * {@link #setNullText(String)}. - *

- * Prior to 3.0, this class implemented Cloneable but did not implement the - * clone method so could not be used. From 3.0 onwards it no longer implements - * the interface. - * - * @since 2.2 - * @!deprecated as of 3.6, use commons-text - * TextStringBuilder instead - */ -//@Deprecated -public class StrBuilder implements CharSequence, Appendable, Serializable, Builder { - - /** - * The extra capacity for new builders. - */ - static final int CAPACITY = 32; - - /** - * Required for serialization support. - * - * @see java.io.Serializable - */ - private static final long serialVersionUID = 7628716375283629643L; - - /** Internal data storage. */ - protected char[] buffer; // TODO make private? - /** Current size of the buffer. */ - protected int size; // TODO make private? - /** The new line. */ - private String newLine; - /** The null text. */ - private String nullText; - - // ----------------------------------------------------------------------- - /** - * Constructor that creates an empty builder initial capacity 32 characters. - */ - public StrBuilder() { - this(CAPACITY); - } - - /** - * Constructor that creates an empty builder the specified initial capacity. - * - * @param initialCapacity the initial capacity, zero or less will be converted - * to 32 - */ - public StrBuilder(int initialCapacity) { - if (initialCapacity <= 0) { - initialCapacity = CAPACITY; - } - buffer = new char[initialCapacity]; - } - - /** - * Constructor that creates a builder from the string, allocating 32 extra - * characters for growth. - * - * @param str the string to copy, null treated as blank string - */ - public StrBuilder(final String str) { - if (str == null) { - buffer = new char[CAPACITY]; - } else { - buffer = new char[str.length() + CAPACITY]; - append(str); - } - } - - // ----------------------------------------------------------------------- - /** - * Gets the text to be appended when a new line is added. - * - * @return the new line text, null means use system default - */ - public String getNewLineText() { - return newLine; - } - - /** - * Sets the text to be appended when a new line is added. - * - * @param newLine the new line text, null means use system default - * @return this, to enable chaining - */ - public StrBuilder setNewLineText(final String newLine) { - this.newLine = newLine; - return this; - } - - // ----------------------------------------------------------------------- - /** - * Gets the text to be appended when null is added. - * - * @return the null text, null means no append - */ - public String getNullText() { - return nullText; - } - - /** - * Sets the text to be appended when null is added. - * - * @param nullText the null text, null means no append - * @return this, to enable chaining - */ - public StrBuilder setNullText(String nullText) { - if (nullText != null && nullText.isEmpty()) { - nullText = null; - } - this.nullText = nullText; - return this; - } - - // ----------------------------------------------------------------------- - /** - * Gets the length of the string builder. - * - * @return the length - */ - @Override - public int length() { - return size; - } - - /** - * Updates the length of the builder by either dropping the last characters or - * adding filler of Unicode zero. - * - * @param length the length to set to, must be zero or positive - * @return this, to enable chaining - * @throws IndexOutOfBoundsException if the length is negative - */ - public StrBuilder setLength(final int length) { - if (length < 0) { - throw new StringIndexOutOfBoundsException(length); - } - if (length < size) { - size = length; - } else if (length > size) { - ensureCapacity(length); - final int oldEnd = size; - final int newEnd = length; - size = length; - for (int i = oldEnd; i < newEnd; i++) { - buffer[i] = CharUtils.NUL; - } - } - return this; - } - - // ----------------------------------------------------------------------- - /** - * Gets the current size of the internal character array buffer. - * - * @return the capacity - */ - public int capacity() { - return buffer.length; - } - - /** - * Checks the capacity and ensures that it is at least the size specified. - * - * @param capacity the capacity to ensure - * @return this, to enable chaining - */ - public StrBuilder ensureCapacity(final int capacity) { - if (capacity > buffer.length) { - final char[] old = buffer; - buffer = new char[capacity * 2]; - System.arraycopy(old, 0, buffer, 0, size); - } - return this; - } - - /** - * Minimizes the capacity to the actual length of the string. - * - * @return this, to enable chaining - */ - public StrBuilder minimizeCapacity() { - if (buffer.length > length()) { - final char[] old = buffer; - buffer = new char[length()]; - System.arraycopy(old, 0, buffer, 0, size); - } - return this; - } - - // ----------------------------------------------------------------------- - /** - * Gets the length of the string builder. - *

- * This method is the same as {@link #length()} and is provided to match the API - * of Collections. - * - * @return the length - */ - public int size() { - return size; - } - - /** - * Checks is the string builder is empty (convenience Collections API style - * method). - *

- * This method is the same as checking {@link #length()} and is provided to - * match the API of Collections. - * - * @return {@code true} if the size is {@code 0}. - */ - public boolean isEmpty() { - return size == 0; - } - - /** - * Checks is the string builder is not empty (convenience Collections API style - * method). - *

- * This method is the same as checking {@link #length()} and is provided to - * match the API of Collections. - * - * @return {@code true} if the size is greater than {@code 0}. - * @since 3.12.0 - */ - public boolean isNotEmpty() { - return size > 0; - } - - /** - * Clears the string builder (convenience Collections API style method). - *

- * This method does not reduce the size of the internal character buffer. To do - * that, call {@code clear()} followed by {@link #minimizeCapacity()}. - *

- * This method is the same as {@link #setLength(int)} called with zero and is - * provided to match the API of Collections. - * - * @return this, to enable chaining - */ - public StrBuilder clear() { - size = 0; - return this; - } - - // ----------------------------------------------------------------------- - /** - * Gets the character at the specified index. - * - * @see #setCharAt(int, char) - * @see #deleteCharAt(int) - * @param index the index to retrieve, must be valid - * @return the character at the index - * @throws IndexOutOfBoundsException if the index is invalid - */ - @Override - public char charAt(final int index) { - if (index < 0 || index >= length()) { - throw new StringIndexOutOfBoundsException(index); - } - return buffer[index]; - } - - /** - * Sets the character at the specified index. - * - * @see #charAt(int) - * @see #deleteCharAt(int) - * @param index the index to set - * @param ch the new character - * @return this, to enable chaining - * @throws IndexOutOfBoundsException if the index is invalid - */ - public StrBuilder setCharAt(final int index, final char ch) { - if (index < 0 || index >= length()) { - throw new StringIndexOutOfBoundsException(index); - } - buffer[index] = ch; - return this; - } - - /** - * Deletes the character at the specified index. - * - * @see #charAt(int) - * @see #setCharAt(int, char) - * @param index the index to delete - * @return this, to enable chaining - * @throws IndexOutOfBoundsException if the index is invalid - */ - public StrBuilder deleteCharAt(final int index) { - if (index < 0 || index >= size) { - throw new StringIndexOutOfBoundsException(index); - } - deleteImpl(index, index + 1, 1); - return this; - } - - // ----------------------------------------------------------------------- - /** - * Copies the builder's character array into a new character array. - * - * @return a new array that represents the contents of the builder - */ - public char[] toCharArray() { - if (size == 0) { - return new char[0]; - } - final char[] chars = new char[size]; - System.arraycopy(buffer, 0, chars, 0, size); - return chars; - } - - /** - * Copies part of the builder's character array into a new character array. - * - * @param startIndex the start index, inclusive, must be valid - * @param endIndex the end index, exclusive, must be valid except that if too - * large it is treated as end of string - * @return a new array that holds part of the contents of the builder - * @throws IndexOutOfBoundsException if startIndex is invalid, or if endIndex is - * invalid (but endIndex greater than size is - * valid) - */ - public char[] toCharArray(final int startIndex, int endIndex) { - endIndex = validateRange(startIndex, endIndex); - final int len = endIndex - startIndex; - if (len == 0) { - return new char[0]; - } - final char[] chars = new char[len]; - System.arraycopy(buffer, startIndex, chars, 0, len); - return chars; - } - - /** - * Copies the character array into the specified array. - * - * @param destination the destination array, null will cause an array to be - * created - * @return the input array, unless that was null or too small - */ - public char[] getChars(char[] destination) { - final int len = length(); - if (destination == null || destination.length < len) { - destination = new char[len]; - } - System.arraycopy(buffer, 0, destination, 0, len); - return destination; - } - - /** - * Copies the character array into the specified array. - * - * @param startIndex first index to copy, inclusive, must be valid - * @param endIndex last index, exclusive, must be valid - * @param destination the destination array, must not be null or too small - * @param destinationIndex the index to start copying in destination - * @throws NullPointerException if the array is null - * @throws IndexOutOfBoundsException if any index is invalid - */ - public void getChars(final int startIndex, final int endIndex, final char[] destination, - final int destinationIndex) { - if (startIndex < 0) { - throw new StringIndexOutOfBoundsException(startIndex); - } - if (endIndex < 0 || endIndex > length()) { - throw new StringIndexOutOfBoundsException(endIndex); - } - if (startIndex > endIndex) { - throw new StringIndexOutOfBoundsException("end < start"); - } - System.arraycopy(buffer, startIndex, destination, destinationIndex, endIndex - startIndex); - } - - // ----------------------------------------------------------------------- - /** - * If possible, reads chars from the provided {@link Readable} directly into - * underlying character buffer without making extra copies. - * - * @param readable object to read from - * @return the number of characters read - * @throws IOException if an I/O error occurs. - * - * @since 3.4 - * @see #appendTo(Appendable) - */ - public int readFrom(final Readable readable) throws IOException { - final int oldSize = size; - if (readable instanceof Reader) { - final Reader r = (Reader) readable; - ensureCapacity(size + 1); - int read; - while ((read = r.read(buffer, size, buffer.length - size)) != -1) { - size += read; - ensureCapacity(size + 1); - } - } else if (readable instanceof CharBuffer) { - final CharBuffer cb = (CharBuffer) readable; - final int remaining = cb.remaining(); - ensureCapacity(size + remaining); - cb.get(buffer, size, remaining); - size += remaining; - } else { - while (true) { - ensureCapacity(size + 1); - final CharBuffer buf = CharBuffer.wrap(buffer, size, buffer.length - size); - final int read = readable.read(buf); - if (read == -1) { - break; - } - size += read; - } - } - return size - oldSize; - } - - // ----------------------------------------------------------------------- - /** - * Appends the new line string to this string builder. - *

- * The new line string can be altered using {@link #setNewLineText(String)}. - * This might be used to force the output to always use Unix line endings even - * when on Windows. - * - * @return this, to enable chaining - */ - public StrBuilder appendNewLine() { - if (newLine == null) { - append(System.lineSeparator()); - return this; - } - return append(newLine); - } - - /** - * Appends the text representing {@code null} to this string builder. - * - * @return this, to enable chaining - */ - public StrBuilder appendNull() { - if (nullText == null) { - return this; - } - return append(nullText); - } - - /** - * Appends an object to this string builder. Appending null will call - * {@link #appendNull()}. - * - * @param obj the object to append - * @return this, to enable chaining - */ - public StrBuilder append(final Object obj) { - if (obj == null) { - return appendNull(); - } - if (obj instanceof CharSequence) { - return append((CharSequence) obj); - } - return append(obj.toString()); - } - - /** - * Appends a CharSequence to this string builder. Appending null will call - * {@link #appendNull()}. - * - * @param seq the CharSequence to append - * @return this, to enable chaining - * @since 3.0 - */ - @Override - public StrBuilder append(final CharSequence seq) { - if (seq == null) { - return appendNull(); - } - if (seq instanceof StrBuilder) { - return append((StrBuilder) seq); - } - if (seq instanceof StringBuilder) { - return append((StringBuilder) seq); - } - if (seq instanceof StringBuffer) { - return append((StringBuffer) seq); - } - if (seq instanceof CharBuffer) { - return append((CharBuffer) seq); - } - return append(seq.toString()); - } - - /** - * Appends part of a CharSequence to this string builder. Appending null will - * call {@link #appendNull()}. - * - * @param seq the CharSequence to append - * @param startIndex the start index, inclusive, must be valid - * @param length the length to append, must be valid - * @return this, to enable chaining - * @since 3.0 - */ - @Override - public StrBuilder append(final CharSequence seq, final int startIndex, final int length) { - if (seq == null) { - return appendNull(); - } - return append(seq.toString(), startIndex, length); - } - - /** - * Appends a string to this string builder. Appending null will call - * {@link #appendNull()}. - * - * @param str the string to append - * @return this, to enable chaining - */ - public StrBuilder append(final String str) { - if (str == null) { - return appendNull(); - } - final int strLen = str.length(); - if (strLen > 0) { - final int len = length(); - ensureCapacity(len + strLen); - str.getChars(0, strLen, buffer, len); - size += strLen; - } - return this; - } - - /** - * Appends part of a string to this string builder. Appending null will call - * {@link #appendNull()}. - * - * @param str the string to append - * @param startIndex the start index, inclusive, must be valid - * @param length the length to append, must be valid - * @return this, to enable chaining - */ - public StrBuilder append(final String str, final int startIndex, final int length) { - if (str == null) { - return appendNull(); - } - if (startIndex < 0 || startIndex > str.length()) { - throw new StringIndexOutOfBoundsException("startIndex must be valid"); - } - if (length < 0 || (startIndex + length) > str.length()) { - throw new StringIndexOutOfBoundsException("length must be valid"); - } - if (length > 0) { - final int len = length(); - ensureCapacity(len + length); - str.getChars(startIndex, startIndex + length, buffer, len); - size += length; - } - return this; - } - - /** - * Calls {@link String#format(String, Object...)} and appends the result. - * - * @param format the format string - * @param objs the objects to use in the format string - * @return {@code this} to enable chaining - * @see String#format(String, Object...) - * @since 3.2 - */ - public StrBuilder append(final String format, final Object... objs) { - return append(HString.format(format, objs)); - } - - /** - * Appends the contents of a char buffer to this string builder. Appending null - * will call {@link #appendNull()}. - * - * @param buf the char buffer to append - * @return this, to enable chaining - * @since 3.4 - */ - public StrBuilder append(final CharBuffer buf) { - if (buf == null) { - return appendNull(); - } - if (buf.hasArray()) { - final int length = buf.remaining(); - final int len = length(); - ensureCapacity(len + length); - System.arraycopy(buf.array(), buf.arrayOffset() + buf.position(), buffer, len, length); - size += length; - } else { - append(buf.toString()); - } - return this; - } - - /** - * Appends the contents of a char buffer to this string builder. Appending null - * will call {@link #appendNull()}. - * - * @param buf the char buffer to append - * @param startIndex the start index, inclusive, must be valid - * @param length the length to append, must be valid - * @return this, to enable chaining - * @since 3.4 - */ - public StrBuilder append(final CharBuffer buf, final int startIndex, final int length) { - if (buf == null) { - return appendNull(); - } - if (buf.hasArray()) { - final int totalLength = buf.remaining(); - if (startIndex < 0 || startIndex > totalLength) { - throw new StringIndexOutOfBoundsException("startIndex must be valid"); - } - if (length < 0 || (startIndex + length) > totalLength) { - throw new StringIndexOutOfBoundsException("length must be valid"); - } - final int len = length(); - ensureCapacity(len + length); - System.arraycopy(buf.array(), buf.arrayOffset() + buf.position() + startIndex, buffer, len, length); - size += length; - } else { - append(buf.toString(), startIndex, length); - } - return this; - } - - /** - * Appends a string buffer to this string builder. Appending null will call - * {@link #appendNull()}. - * - * @param str the string buffer to append - * @return this, to enable chaining - */ - public StrBuilder append(final StringBuffer str) { - if (str == null) { - return appendNull(); - } - final int strLen = str.length(); - if (strLen > 0) { - final int len = length(); - ensureCapacity(len + strLen); - str.getChars(0, strLen, buffer, len); - size += strLen; - } - return this; - } - - /** - * Appends part of a string buffer to this string builder. Appending null will - * call {@link #appendNull()}. - * - * @param str the string to append - * @param startIndex the start index, inclusive, must be valid - * @param length the length to append, must be valid - * @return this, to enable chaining - */ - public StrBuilder append(final StringBuffer str, final int startIndex, final int length) { - if (str == null) { - return appendNull(); - } - if (startIndex < 0 || startIndex > str.length()) { - throw new StringIndexOutOfBoundsException("startIndex must be valid"); - } - if (length < 0 || (startIndex + length) > str.length()) { - throw new StringIndexOutOfBoundsException("length must be valid"); - } - if (length > 0) { - final int len = length(); - ensureCapacity(len + length); - str.getChars(startIndex, startIndex + length, buffer, len); - size += length; - } - return this; - } - - /** - * Appends a StringBuilder to this string builder. Appending null will call - * {@link #appendNull()}. - * - * @param str the StringBuilder to append - * @return this, to enable chaining - * @since 3.2 - */ - public StrBuilder append(final StringBuilder str) { - if (str == null) { - return appendNull(); - } - final int strLen = str.length(); - if (strLen > 0) { - final int len = length(); - ensureCapacity(len + strLen); - str.getChars(0, strLen, buffer, len); - size += strLen; - } - return this; - } - - /** - * Appends part of a StringBuilder to this string builder. Appending null will - * call {@link #appendNull()}. - * - * @param str the StringBuilder to append - * @param startIndex the start index, inclusive, must be valid - * @param length the length to append, must be valid - * @return this, to enable chaining - * @since 3.2 - */ - public StrBuilder append(final StringBuilder str, final int startIndex, final int length) { - if (str == null) { - return appendNull(); - } - if (startIndex < 0 || startIndex > str.length()) { - throw new StringIndexOutOfBoundsException("startIndex must be valid"); - } - if (length < 0 || (startIndex + length) > str.length()) { - throw new StringIndexOutOfBoundsException("length must be valid"); - } - if (length > 0) { - final int len = length(); - ensureCapacity(len + length); - str.getChars(startIndex, startIndex + length, buffer, len); - size += length; - } - return this; - } - - /** - * Appends another string builder to this string builder. Appending null will - * call {@link #appendNull()}. - * - * @param str the string builder to append - * @return this, to enable chaining - */ - public StrBuilder append(final StrBuilder str) { - if (str == null) { - return appendNull(); - } - final int strLen = str.length(); - if (strLen > 0) { - final int len = length(); - ensureCapacity(len + strLen); - System.arraycopy(str.buffer, 0, buffer, len, strLen); - size += strLen; - } - return this; - } - - /** - * Appends part of a string builder to this string builder. Appending null will - * call {@link #appendNull()}. - * - * @param str the string to append - * @param startIndex the start index, inclusive, must be valid - * @param length the length to append, must be valid - * @return this, to enable chaining - */ - public StrBuilder append(final StrBuilder str, final int startIndex, final int length) { - if (str == null) { - return appendNull(); - } - if (startIndex < 0 || startIndex > str.length()) { - throw new StringIndexOutOfBoundsException("startIndex must be valid"); - } - if (length < 0 || (startIndex + length) > str.length()) { - throw new StringIndexOutOfBoundsException("length must be valid"); - } - if (length > 0) { - final int len = length(); - ensureCapacity(len + length); - str.getChars(startIndex, startIndex + length, buffer, len); - size += length; - } - return this; - } - - /** - * Appends a char array to the string builder. Appending null will call - * {@link #appendNull()}. - * - * @param chars the char array to append - * @return this, to enable chaining - */ - public StrBuilder append(final char[] chars) { - if (chars == null) { - return appendNull(); - } - final int strLen = chars.length; - if (strLen > 0) { - final int len = length(); - ensureCapacity(len + strLen); - System.arraycopy(chars, 0, buffer, len, strLen); - size += strLen; - } - return this; - } - - /** - * Appends a char array to the string builder. Appending null will call - * {@link #appendNull()}. - * - * @param chars the char array to append - * @param startIndex the start index, inclusive, must be valid - * @param length the length to append, must be valid - * @return this, to enable chaining - */ - public StrBuilder append(final char[] chars, final int startIndex, final int length) { - if (chars == null) { - return appendNull(); - } - if (startIndex < 0 || startIndex > chars.length) { - throw new StringIndexOutOfBoundsException("Invalid startIndex: " + length); - } - if (length < 0 || (startIndex + length) > chars.length) { - throw new StringIndexOutOfBoundsException("Invalid length: " + length); - } - if (length > 0) { - final int len = length(); - ensureCapacity(len + length); - System.arraycopy(chars, startIndex, buffer, len, length); - size += length; - } - return this; - } - - /** - * Appends a boolean value to the string builder. - * - * @param value the value to append - * @return this, to enable chaining - */ - public StrBuilder append(final boolean value) { - if (value) { - ensureCapacity(size + 4); - buffer[size++] = 't'; - buffer[size++] = 'r'; - buffer[size++] = 'u'; - buffer[size++] = 'e'; - } else { - ensureCapacity(size + 5); - buffer[size++] = 'f'; - buffer[size++] = 'a'; - buffer[size++] = 'l'; - buffer[size++] = 's'; - buffer[size++] = 'e'; - } - return this; - } - - /** - * Appends a char value to the string builder. - * - * @param ch the value to append - * @return this, to enable chaining - * @since 3.0 - */ - @Override - public StrBuilder append(final char ch) { - final int len = length(); - ensureCapacity(len + 1); - buffer[size++] = ch; - return this; - } - - /** - * Appends an int value to the string builder using {@code String.valueOf}. - * - * @param value the value to append - * @return this, to enable chaining - */ - public StrBuilder append(final int value) { - return append(String.valueOf(value)); - } - - /** - * Appends a long value to the string builder using {@code String.valueOf}. - * - * @param value the value to append - * @return this, to enable chaining - */ - public StrBuilder append(final long value) { - return append(String.valueOf(value)); - } - - /** - * Appends a float value to the string builder using {@code String.valueOf}. - * - * @param value the value to append - * @return this, to enable chaining - */ - public StrBuilder append(final float value) { - return append(String.valueOf(value)); - } - - /** - * Appends a double value to the string builder using {@code String.valueOf}. - * - * @param value the value to append - * @return this, to enable chaining - */ - public StrBuilder append(final double value) { - return append(String.valueOf(value)); - } - - // ----------------------------------------------------------------------- - /** - * Appends an object followed by a new line to this string builder. Appending - * null will call {@link #appendNull()}. - * - * @param obj the object to append - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendln(final Object obj) { - return append(obj).appendNewLine(); - } - - /** - * Appends a string followed by a new line to this string builder. Appending - * null will call {@link #appendNull()}. - * - * @param str the string to append - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendln(final String str) { - return append(str).appendNewLine(); - } - - /** - * Appends part of a string followed by a new line to this string builder. - * Appending null will call {@link #appendNull()}. - * - * @param str the string to append - * @param startIndex the start index, inclusive, must be valid - * @param length the length to append, must be valid - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendln(final String str, final int startIndex, final int length) { - return append(str, startIndex, length).appendNewLine(); - } - - /** - * Calls {@link String#format(String, Object...)} and appends the result. - * - * @param format the format string - * @param objs the objects to use in the format string - * @return {@code this} to enable chaining - * @see String#format(String, Object...) - * @since 3.2 - */ - public StrBuilder appendln(final String format, final Object... objs) { - return append(format, objs).appendNewLine(); - } - - /** - * Appends a string buffer followed by a new line to this string builder. - * Appending null will call {@link #appendNull()}. - * - * @param str the string buffer to append - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendln(final StringBuffer str) { - return append(str).appendNewLine(); - } - - /** - * Appends a string builder followed by a new line to this string builder. - * Appending null will call {@link #appendNull()}. - * - * @param str the string builder to append - * @return this, to enable chaining - * @since 3.2 - */ - public StrBuilder appendln(final StringBuilder str) { - return append(str).appendNewLine(); - } - - /** - * Appends part of a string builder followed by a new line to this string - * builder. Appending null will call {@link #appendNull()}. - * - * @param str the string builder to append - * @param startIndex the start index, inclusive, must be valid - * @param length the length to append, must be valid - * @return this, to enable chaining - * @since 3.2 - */ - public StrBuilder appendln(final StringBuilder str, final int startIndex, final int length) { - return append(str, startIndex, length).appendNewLine(); - } - - /** - * Appends part of a string buffer followed by a new line to this string - * builder. Appending null will call {@link #appendNull()}. - * - * @param str the string to append - * @param startIndex the start index, inclusive, must be valid - * @param length the length to append, must be valid - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendln(final StringBuffer str, final int startIndex, final int length) { - return append(str, startIndex, length).appendNewLine(); - } - - /** - * Appends another string builder followed by a new line to this string builder. - * Appending null will call {@link #appendNull()}. - * - * @param str the string builder to append - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendln(final StrBuilder str) { - return append(str).appendNewLine(); - } - - /** - * Appends part of a string builder followed by a new line to this string - * builder. Appending null will call {@link #appendNull()}. - * - * @param str the string to append - * @param startIndex the start index, inclusive, must be valid - * @param length the length to append, must be valid - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendln(final StrBuilder str, final int startIndex, final int length) { - return append(str, startIndex, length).appendNewLine(); - } - - /** - * Appends a char array followed by a new line to the string builder. Appending - * null will call {@link #appendNull()}. - * - * @param chars the char array to append - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendln(final char[] chars) { - return append(chars).appendNewLine(); - } - - /** - * Appends a char array followed by a new line to the string builder. Appending - * null will call {@link #appendNull()}. - * - * @param chars the char array to append - * @param startIndex the start index, inclusive, must be valid - * @param length the length to append, must be valid - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendln(final char[] chars, final int startIndex, final int length) { - return append(chars, startIndex, length).appendNewLine(); - } - - /** - * Appends a boolean value followed by a new line to the string builder. - * - * @param value the value to append - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendln(final boolean value) { - return append(value).appendNewLine(); - } - - /** - * Appends a char value followed by a new line to the string builder. - * - * @param ch the value to append - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendln(final char ch) { - return append(ch).appendNewLine(); - } - - /** - * Appends an int value followed by a new line to the string builder using - * {@code String.valueOf}. - * - * @param value the value to append - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendln(final int value) { - return append(value).appendNewLine(); - } - - /** - * Appends a long value followed by a new line to the string builder using - * {@code String.valueOf}. - * - * @param value the value to append - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendln(final long value) { - return append(value).appendNewLine(); - } - - /** - * Appends a float value followed by a new line to the string builder using - * {@code String.valueOf}. - * - * @param value the value to append - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendln(final float value) { - return append(value).appendNewLine(); - } - - /** - * Appends a double value followed by a new line to the string builder using - * {@code String.valueOf}. - * - * @param value the value to append - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendln(final double value) { - return append(value).appendNewLine(); - } - - // ----------------------------------------------------------------------- - /** - * Appends each item in an array to the builder without any separators. - * Appending a null array will have no effect. Each object is appended using - * {@link #append(Object)}. - * - * @param the element type - * @param array the array to append - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendAll(@SuppressWarnings("unchecked") final T... array) { - /* - * @SuppressWarnings used to hide warning about vararg usage. We cannot - * use @SafeVarargs, since this method is not final. Using @SuppressWarnings is - * fine, because it isn't inherited by subclasses, so each subclass must vouch - * for itself whether its use of 'array' is safe. - */ - if (array.length > 0) { - for (final Object element : array) { - append(element); - } - } - return this; - } - - /** - * Appends each item in an iterable to the builder without any separators. - * Appending a null iterable will have no effect. Each object is appended using - * {@link #append(Object)}. - * - * @param iterable the iterable to append - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendAll(final Iterable iterable) { - if (iterable != null) { - for (final Object o : iterable) { - append(o); - } - } - return this; - } - - /** - * Appends each item in an iterator to the builder without any separators. - * Appending a null iterator will have no effect. Each object is appended using - * {@link #append(Object)}. - * - * @param it the iterator to append - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendAll(final Iterator it) { - if (it != null) { - while (it.hasNext()) { - append(it.next()); - } - } - return this; - } - - // ----------------------------------------------------------------------- - /** - * Appends an array placing separators between each value, but not before the - * first or after the last. Appending a null array will have no effect. Each - * object is appended using {@link #append(Object)}. - * - * @param array the array to append - * @param separator the separator to use, null means no separator - * @return this, to enable chaining - */ - public StrBuilder appendWithSeparators(final Object[] array, final String separator) { - if (array != null && array.length > 0) { - final String sep = Objects.toString(separator, ""); - append(array[0]); - for (int i = 1; i < array.length; i++) { - append(sep); - append(array[i]); - } - } - return this; - } - - /** - * Appends an iterable placing separators between each value, but not before the - * first or after the last. Appending a null iterable will have no effect. Each - * object is appended using {@link #append(Object)}. - * - * @param iterable the iterable to append - * @param separator the separator to use, null means no separator - * @return this, to enable chaining - */ - public StrBuilder appendWithSeparators(final Iterable iterable, final String separator) { - if (iterable != null) { - final String sep = Objects.toString(separator, ""); - final Iterator it = iterable.iterator(); - while (it.hasNext()) { - append(it.next()); - if (it.hasNext()) { - append(sep); - } - } - } - return this; - } - - /** - * Appends an iterator placing separators between each value, but not before the - * first or after the last. Appending a null iterator will have no effect. Each - * object is appended using {@link #append(Object)}. - * - * @param it the iterator to append - * @param separator the separator to use, null means no separator - * @return this, to enable chaining - */ - public StrBuilder appendWithSeparators(final Iterator it, final String separator) { - if (it != null) { - final String sep = Objects.toString(separator, ""); - while (it.hasNext()) { - append(it.next()); - if (it.hasNext()) { - append(sep); - } - } - } - return this; - } - - // ----------------------------------------------------------------------- - /** - * Appends a separator if the builder is currently non-empty. Appending a null - * separator will have no effect. The separator is appended using - * {@link #append(String)}. - *

- * This method is useful for adding a separator each time around the loop except - * the first. - * - *

-	 * for (Iterator it = list.iterator(); it.hasNext();) {
-	 * 	appendSeparator(",");
-	 * 	append(it.next());
-	 * }
-	 * 
- * - * Note that for this simple example, you should use - * {@link #appendWithSeparators(Iterable, String)}. - * - * @param separator the separator to use, null means no separator - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendSeparator(final String separator) { - return appendSeparator(separator, null); - } - - /** - * Appends one of both separators to the StrBuilder. If the builder is currently - * empty it will append the defaultIfEmpty-separator Otherwise it will append - * the standard-separator - * - * Appending a null separator will have no effect. The separator is appended - * using {@link #append(String)}. - *

- * This method is for example useful for constructing queries - * - *

-	 * StrBuilder whereClause = new StrBuilder();
-	 * if (searchCommand.getPriority() != null) {
-	 *  whereClause.appendSeparator(" and", " where");
-	 *  whereClause.append(" priority = ?")
-	 * }
-	 * if (searchCommand.getComponent() != null) {
-	 *  whereClause.appendSeparator(" and", " where");
-	 *  whereClause.append(" component = ?")
-	 * }
-	 * selectClause.append(whereClause)
-	 * 
- * - * @param standard the separator if builder is not empty, null means no - * separator - * @param defaultIfEmpty the separator if builder is empty, null means no - * separator - * @return this, to enable chaining - * @since 2.5 - */ - public StrBuilder appendSeparator(final String standard, final String defaultIfEmpty) { - final String str = isEmpty() ? defaultIfEmpty : standard; - if (str != null) { - append(str); - } - return this; - } - - /** - * Appends a separator if the builder is currently non-empty. The separator is - * appended using {@link #append(char)}. - *

- * This method is useful for adding a separator each time around the loop except - * the first. - * - *

-	 * for (Iterator it = list.iterator(); it.hasNext();) {
-	 * 	appendSeparator(',');
-	 * 	append(it.next());
-	 * }
-	 * 
- * - * Note that for this simple example, you should use - * {@link #appendWithSeparators(Iterable, String)}. - * - * @param separator the separator to use - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendSeparator(final char separator) { - if (isNotEmpty()) { - append(separator); - } - return this; - } - - /** - * Append one of both separators to the builder If the builder is currently - * empty it will append the defaultIfEmpty-separator Otherwise it will append - * the standard-separator - * - * The separator is appended using {@link #append(char)}. - * - * @param standard the separator if builder is not empty - * @param defaultIfEmpty the separator if builder is empty - * @return this, to enable chaining - * @since 2.5 - */ - public StrBuilder appendSeparator(final char standard, final char defaultIfEmpty) { - if (isNotEmpty()) { - append(standard); - } else { - append(defaultIfEmpty); - } - return this; - } - - /** - * Appends a separator to the builder if the loop index is greater than zero. - * Appending a null separator will have no effect. The separator is appended - * using {@link #append(String)}. - *

- * This method is useful for adding a separator each time around the loop except - * the first. - *

- * - *
-	 * for (int i = 0; i < list.size(); i++) {
-	 * 	appendSeparator(",", i);
-	 * 	append(list.get(i));
-	 * }
-	 * 
- * - * Note that for this simple example, you should use - * {@link #appendWithSeparators(Iterable, String)}. - * - * @param separator the separator to use, null means no separator - * @param loopIndex the loop index - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendSeparator(final String separator, final int loopIndex) { - if (separator != null && loopIndex > 0) { - append(separator); - } - return this; - } - - /** - * Appends a separator to the builder if the loop index is greater than zero. - * The separator is appended using {@link #append(char)}. - *

- * This method is useful for adding a separator each time around the loop except - * the first. - *

- * - *
-	 * for (int i = 0; i < list.size(); i++) {
-	 * 	appendSeparator(",", i);
-	 * 	append(list.get(i));
-	 * }
-	 * 
- * - * Note that for this simple example, you should use - * {@link #appendWithSeparators(Iterable, String)}. - * - * @param separator the separator to use - * @param loopIndex the loop index - * @return this, to enable chaining - * @since 2.3 - */ - public StrBuilder appendSeparator(final char separator, final int loopIndex) { - if (loopIndex > 0) { - append(separator); - } - return this; - } - - // ----------------------------------------------------------------------- - /** - * Appends the pad character to the builder the specified number of times. - * - * @param length the length to append, negative means no append - * @param padChar the character to append - * @return this, to enable chaining - */ - public StrBuilder appendPadding(final int length, final char padChar) { - if (length >= 0) { - ensureCapacity(size + length); - for (int i = 0; i < length; i++) { - buffer[size++] = padChar; - } - } - return this; - } - - // ----------------------------------------------------------------------- - /** - * Appends an object to the builder padding on the left to a fixed width. The - * {@code toString} of the object is used. If the object is larger than the - * length, the left hand side is lost. If the object is null, the null text - * value is used. - * - * @param obj the object to append, null uses null text - * @param width the fixed field width, zero or negative has no effect - * @param padChar the pad character to use - * @return this, to enable chaining - */ - public StrBuilder appendFixedWidthPadLeft(final Object obj, final int width, final char padChar) { - if (width > 0) { - ensureCapacity(size + width); - String str = (obj == null ? getNullText() : obj.toString()); - if (str == null) { - str = StringUtils.EMPTY; - } - final int strLen = str.length(); - if (strLen >= width) { - str.getChars(strLen - width, strLen, buffer, size); - } else { - final int padLen = width - strLen; - for (int i = 0; i < padLen; i++) { - buffer[size + i] = padChar; - } - str.getChars(0, strLen, buffer, size + padLen); - } - size += width; - } - return this; - } - - /** - * Appends an object to the builder padding on the left to a fixed width. The - * {@code String.valueOf} of the {@code int} value is used. If the formatted - * value is larger than the length, the left hand side is lost. - * - * @param value the value to append - * @param width the fixed field width, zero or negative has no effect - * @param padChar the pad character to use - * @return this, to enable chaining - */ - public StrBuilder appendFixedWidthPadLeft(final int value, final int width, final char padChar) { - return appendFixedWidthPadLeft(String.valueOf(value), width, padChar); - } - - /** - * Appends an object to the builder padding on the right to a fixed length. The - * {@code toString} of the object is used. If the object is larger than the - * length, the right hand side is lost. If the object is null, null text value - * is used. - * - * @param obj the object to append, null uses null text - * @param width the fixed field width, zero or negative has no effect - * @param padChar the pad character to use - * @return this, to enable chaining - */ - public StrBuilder appendFixedWidthPadRight(final Object obj, final int width, final char padChar) { - if (width > 0) { - ensureCapacity(size + width); - String str = (obj == null ? getNullText() : obj.toString()); - if (str == null) { - str = StringUtils.EMPTY; - } - final int strLen = str.length(); - if (strLen >= width) { - str.getChars(0, width, buffer, size); - } else { - final int padLen = width - strLen; - str.getChars(0, strLen, buffer, size); - for (int i = 0; i < padLen; i++) { - buffer[size + strLen + i] = padChar; - } - } - size += width; - } - return this; - } - - /** - * Appends an object to the builder padding on the right to a fixed length. The - * {@code String.valueOf} of the {@code int} value is used. If the object is - * larger than the length, the right hand side is lost. - * - * @param value the value to append - * @param width the fixed field width, zero or negative has no effect - * @param padChar the pad character to use - * @return this, to enable chaining - */ - public StrBuilder appendFixedWidthPadRight(final int value, final int width, final char padChar) { - return appendFixedWidthPadRight(String.valueOf(value), width, padChar); - } - - // ----------------------------------------------------------------------- - /** - * Inserts the string representation of an object into this builder. Inserting - * null will use the stored null text value. - * - * @param index the index to add at, must be valid - * @param obj the object to insert - * @return this, to enable chaining - * @throws IndexOutOfBoundsException if the index is invalid - */ - public StrBuilder insert(final int index, final Object obj) { - if (obj == null) { - return insert(index, nullText); - } - return insert(index, obj.toString()); - } - - /** - * Inserts the string into this builder. Inserting null will use the stored null - * text value. - * - * @param index the index to add at, must be valid - * @param str the string to insert - * @return this, to enable chaining - * @throws IndexOutOfBoundsException if the index is invalid - */ - public StrBuilder insert(final int index, String str) { - validateIndex(index); - if (str == null) { - str = nullText; - } - if (str != null) { - final int strLen = str.length(); - if (strLen > 0) { - final int newSize = size + strLen; - ensureCapacity(newSize); - System.arraycopy(buffer, index, buffer, index + strLen, size - index); - size = newSize; - str.getChars(0, strLen, buffer, index); - } - } - return this; - } - - /** - * Inserts the character array into this builder. Inserting null will use the - * stored null text value. - * - * @param index the index to add at, must be valid - * @param chars the char array to insert - * @return this, to enable chaining - * @throws IndexOutOfBoundsException if the index is invalid - */ - public StrBuilder insert(final int index, final char[] chars) { - validateIndex(index); - if (chars == null) { - return insert(index, nullText); - } - final int len = chars.length; - if (len > 0) { - ensureCapacity(size + len); - System.arraycopy(buffer, index, buffer, index + len, size - index); - System.arraycopy(chars, 0, buffer, index, len); - size += len; - } - return this; - } - - /** - * Inserts part of the character array into this builder. Inserting null will - * use the stored null text value. - * - * @param index the index to add at, must be valid - * @param chars the char array to insert - * @param offset the offset into the character array to start at, must be valid - * @param length the length of the character array part to copy, must be - * positive - * @return this, to enable chaining - * @throws IndexOutOfBoundsException if any index is invalid - */ - public StrBuilder insert(final int index, final char[] chars, final int offset, final int length) { - validateIndex(index); - if (chars == null) { - return insert(index, nullText); - } - if (offset < 0 || offset > chars.length) { - throw new StringIndexOutOfBoundsException("Invalid offset: " + offset); - } - if (length < 0 || offset + length > chars.length) { - throw new StringIndexOutOfBoundsException("Invalid length: " + length); - } - if (length > 0) { - ensureCapacity(size + length); - System.arraycopy(buffer, index, buffer, index + length, size - index); - System.arraycopy(chars, offset, buffer, index, length); - size += length; - } - return this; - } - - /** - * Inserts the value into this builder. - * - * @param index the index to add at, must be valid - * @param value the value to insert - * @return this, to enable chaining - * @throws IndexOutOfBoundsException if the index is invalid - */ - public StrBuilder insert(int index, final boolean value) { - validateIndex(index); - if (value) { - ensureCapacity(size + 4); - System.arraycopy(buffer, index, buffer, index + 4, size - index); - buffer[index++] = 't'; - buffer[index++] = 'r'; - buffer[index++] = 'u'; - buffer[index] = 'e'; - size += 4; - } else { - ensureCapacity(size + 5); - System.arraycopy(buffer, index, buffer, index + 5, size - index); - buffer[index++] = 'f'; - buffer[index++] = 'a'; - buffer[index++] = 'l'; - buffer[index++] = 's'; - buffer[index] = 'e'; - size += 5; - } - return this; - } - - /** - * Inserts the value into this builder. - * - * @param index the index to add at, must be valid - * @param value the value to insert - * @return this, to enable chaining - * @throws IndexOutOfBoundsException if the index is invalid - */ - public StrBuilder insert(final int index, final char value) { - validateIndex(index); - ensureCapacity(size + 1); - System.arraycopy(buffer, index, buffer, index + 1, size - index); - buffer[index] = value; - size++; - return this; - } - - /** - * Inserts the value into this builder. - * - * @param index the index to add at, must be valid - * @param value the value to insert - * @return this, to enable chaining - * @throws IndexOutOfBoundsException if the index is invalid - */ - public StrBuilder insert(final int index, final int value) { - return insert(index, String.valueOf(value)); - } - - /** - * Inserts the value into this builder. - * - * @param index the index to add at, must be valid - * @param value the value to insert - * @return this, to enable chaining - * @throws IndexOutOfBoundsException if the index is invalid - */ - public StrBuilder insert(final int index, final long value) { - return insert(index, String.valueOf(value)); - } - - /** - * Inserts the value into this builder. - * - * @param index the index to add at, must be valid - * @param value the value to insert - * @return this, to enable chaining - * @throws IndexOutOfBoundsException if the index is invalid - */ - public StrBuilder insert(final int index, final float value) { - return insert(index, String.valueOf(value)); - } - - /** - * Inserts the value into this builder. - * - * @param index the index to add at, must be valid - * @param value the value to insert - * @return this, to enable chaining - * @throws IndexOutOfBoundsException if the index is invalid - */ - public StrBuilder insert(final int index, final double value) { - return insert(index, String.valueOf(value)); - } - - // ----------------------------------------------------------------------- - /** - * Internal method to delete a range without validation. - * - * @param startIndex the start index, must be valid - * @param endIndex the end index (exclusive), must be valid - * @param len the length, must be valid - * @throws IndexOutOfBoundsException if any index is invalid - */ - private void deleteImpl(final int startIndex, final int endIndex, final int len) { - System.arraycopy(buffer, endIndex, buffer, startIndex, size - endIndex); - size -= len; - } - - /** - * Deletes the characters between the two specified indices. - * - * @param startIndex the start index, inclusive, must be valid - * @param endIndex the end index, exclusive, must be valid except that if too - * large it is treated as end of string - * @return this, to enable chaining - * @throws IndexOutOfBoundsException if the index is invalid - */ - public StrBuilder delete(final int startIndex, int endIndex) { - endIndex = validateRange(startIndex, endIndex); - final int len = endIndex - startIndex; - if (len > 0) { - deleteImpl(startIndex, endIndex, len); - } - return this; - } - - // ----------------------------------------------------------------------- - /** - * Deletes the character wherever it occurs in the builder. - * - * @param ch the character to delete - * @return this, to enable chaining - */ - public StrBuilder deleteAll(final char ch) { - for (int i = 0; i < size; i++) { - if (buffer[i] == ch) { - final int start = i; - while (++i < size) { - if (buffer[i] != ch) { - break; - } - } - final int len = i - start; - deleteImpl(start, i, len); - i -= len; - } - } - return this; - } - - /** - * Deletes the character wherever it occurs in the builder. - * - * @param ch the character to delete - * @return this, to enable chaining - */ - public StrBuilder deleteFirst(final char ch) { - for (int i = 0; i < size; i++) { - if (buffer[i] == ch) { - deleteImpl(i, i + 1, 1); - break; - } - } - return this; - } - - // ----------------------------------------------------------------------- - /** - * Deletes the string wherever it occurs in the builder. - * - * @param str the string to delete, null causes no action - * @return this, to enable chaining - */ - public StrBuilder deleteAll(final String str) { - final int len = (str == null ? 0 : str.length()); - if (len > 0) { - int index = indexOf(str, 0); - while (index >= 0) { - deleteImpl(index, index + len, len); - index = indexOf(str, index); - } - } - return this; - } - - /** - * Deletes the string wherever it occurs in the builder. - * - * @param str the string to delete, null causes no action - * @return this, to enable chaining - */ - public StrBuilder deleteFirst(final String str) { - final int len = (str == null ? 0 : str.length()); - if (len > 0) { - final int index = indexOf(str, 0); - if (index >= 0) { - deleteImpl(index, index + len, len); - } - } - return this; - } - - // ----------------------------------------------------------------------- - /** - * Deletes all parts of the builder that the matcher matches. - *

- * Matchers can be used to perform advanced deletion behavior. For example you - * could write a matcher to delete all occurrences where the character 'a' is - * followed by a number. - * - * @param matcher the matcher to use to find the deletion, null causes no action - * @return this, to enable chaining - */ - public StrBuilder deleteAll(final StrMatcher matcher) { - return replace(matcher, null, 0, size, -1); - } - - /** - * Deletes the first match within the builder using the specified matcher. - *

- * Matchers can be used to perform advanced deletion behavior. For example you - * could write a matcher to delete where the character 'a' is followed by a - * number. - * - * @param matcher the matcher to use to find the deletion, null causes no action - * @return this, to enable chaining - */ - public StrBuilder deleteFirst(final StrMatcher matcher) { - return replace(matcher, null, 0, size, 1); - } - - // ----------------------------------------------------------------------- - /** - * Internal method to delete a range without validation. - * - * @param startIndex the start index, must be valid - * @param endIndex the end index (exclusive), must be valid - * @param removeLen the length to remove (endIndex - startIndex), must be valid - * @param insertStr the string to replace with, null means delete range - * @param insertLen the length of the insert string, must be valid - * @throws IndexOutOfBoundsException if any index is invalid - */ - private void replaceImpl(final int startIndex, final int endIndex, final int removeLen, final String insertStr, - final int insertLen) { - final int newSize = size - removeLen + insertLen; - if (insertLen != removeLen) { - ensureCapacity(newSize); - System.arraycopy(buffer, endIndex, buffer, startIndex + insertLen, size - endIndex); - size = newSize; - } - if (insertLen > 0) { - insertStr.getChars(0, insertLen, buffer, startIndex); - } - } - - /** - * Replaces a portion of the string builder with another string. The length of - * the inserted string does not have to match the removed length. - * - * @param startIndex the start index, inclusive, must be valid - * @param endIndex the end index, exclusive, must be valid except that if too - * large it is treated as end of string - * @param replaceStr the string to replace with, null means delete range - * @return this, to enable chaining - * @throws IndexOutOfBoundsException if the index is invalid - */ - public StrBuilder replace(final int startIndex, int endIndex, final String replaceStr) { - endIndex = validateRange(startIndex, endIndex); - final int insertLen = (replaceStr == null ? 0 : replaceStr.length()); - replaceImpl(startIndex, endIndex, endIndex - startIndex, replaceStr, insertLen); - return this; - } - - // ----------------------------------------------------------------------- - /** - * Replaces the search character with the replace character throughout the - * builder. - * - * @param search the search character - * @param replace the replace character - * @return this, to enable chaining - */ - public StrBuilder replaceAll(final char search, final char replace) { - if (search != replace) { - for (int i = 0; i < size; i++) { - if (buffer[i] == search) { - buffer[i] = replace; - } - } - } - return this; - } - - /** - * Replaces the first instance of the search character with the replace - * character in the builder. - * - * @param search the search character - * @param replace the replace character - * @return this, to enable chaining - */ - public StrBuilder replaceFirst(final char search, final char replace) { - if (search != replace) { - for (int i = 0; i < size; i++) { - if (buffer[i] == search) { - buffer[i] = replace; - break; - } - } - } - return this; - } - - // ----------------------------------------------------------------------- - /** - * Replaces the search string with the replace string throughout the builder. - * - * @param searchStr the search string, null causes no action to occur - * @param replaceStr the replace string, null is equivalent to an empty string - * @return this, to enable chaining - */ - public StrBuilder replaceAll(final String searchStr, final String replaceStr) { - final int searchLen = (searchStr == null ? 0 : searchStr.length()); - if (searchLen > 0) { - final int replaceLen = (replaceStr == null ? 0 : replaceStr.length()); - int index = indexOf(searchStr, 0); - while (index >= 0) { - replaceImpl(index, index + searchLen, searchLen, replaceStr, replaceLen); - index = indexOf(searchStr, index + replaceLen); - } - } - return this; - } - - /** - * Replaces the first instance of the search string with the replace string. - * - * @param searchStr the search string, null causes no action to occur - * @param replaceStr the replace string, null is equivalent to an empty string - * @return this, to enable chaining - */ - public StrBuilder replaceFirst(final String searchStr, final String replaceStr) { - final int searchLen = (searchStr == null ? 0 : searchStr.length()); - if (searchLen > 0) { - final int index = indexOf(searchStr, 0); - if (index >= 0) { - final int replaceLen = (replaceStr == null ? 0 : replaceStr.length()); - replaceImpl(index, index + searchLen, searchLen, replaceStr, replaceLen); - } - } - return this; - } - - // ----------------------------------------------------------------------- - /** - * Replaces all matches within the builder with the replace string. - *

- * Matchers can be used to perform advanced replace behavior. For example you - * could write a matcher to replace all occurrences where the character 'a' is - * followed by a number. - * - * @param matcher the matcher to use to find the deletion, null causes no - * action - * @param replaceStr the replace string, null is equivalent to an empty string - * @return this, to enable chaining - */ - public StrBuilder replaceAll(final StrMatcher matcher, final String replaceStr) { - return replace(matcher, replaceStr, 0, size, -1); - } - - /** - * Replaces the first match within the builder with the replace string. - *

- * Matchers can be used to perform advanced replace behavior. For example you - * could write a matcher to replace where the character 'a' is followed by a - * number. - * - * @param matcher the matcher to use to find the deletion, null causes no - * action - * @param replaceStr the replace string, null is equivalent to an empty string - * @return this, to enable chaining - */ - public StrBuilder replaceFirst(final StrMatcher matcher, final String replaceStr) { - return replace(matcher, replaceStr, 0, size, 1); - } - - // ----------------------------------------------------------------------- - /** - * Advanced search and replaces within the builder using a matcher. - *

- * Matchers can be used to perform advanced behavior. For example you could - * write a matcher to delete all occurrences where the character 'a' is followed - * by a number. - * - * @param matcher the matcher to use to find the deletion, null causes no - * action - * @param replaceStr the string to replace the match with, null is a delete - * @param startIndex the start index, inclusive, must be valid - * @param endIndex the end index, exclusive, must be valid except that if - * too large it is treated as end of string - * @param replaceCount the number of times to replace, -1 for replace all - * @return this, to enable chaining - * @throws IndexOutOfBoundsException if start index is invalid - */ - public StrBuilder replace(final StrMatcher matcher, final String replaceStr, final int startIndex, int endIndex, - final int replaceCount) { - endIndex = validateRange(startIndex, endIndex); - return replaceImpl(matcher, replaceStr, startIndex, endIndex, replaceCount); - } - - /** - * Replaces within the builder using a matcher. - *

- * Matchers can be used to perform advanced behavior. For example you could - * write a matcher to delete all occurrences where the character 'a' is followed - * by a number. - * - * @param matcher the matcher to use to find the deletion, null causes no - * action - * @param replaceStr the string to replace the match with, null is a delete - * @param from the start index, must be valid - * @param to the end index (exclusive), must be valid - * @param replaceCount the number of times to replace, -1 for replace all - * @return this, to enable chaining - * @throws IndexOutOfBoundsException if any index is invalid - */ - private StrBuilder replaceImpl(final StrMatcher matcher, final String replaceStr, final int from, int to, - int replaceCount) { - if (matcher == null || size == 0) { - return this; - } - final int replaceLen = (replaceStr == null ? 0 : replaceStr.length()); - for (int i = from; i < to && replaceCount != 0; i++) { - final char[] buf = buffer; - final int removeLen = matcher.isMatch(buf, i, from, to); - if (removeLen > 0) { - replaceImpl(i, i + removeLen, removeLen, replaceStr, replaceLen); - to = to - removeLen + replaceLen; - i = i + replaceLen - 1; - if (replaceCount > 0) { - replaceCount--; - } - } - } - return this; - } - - // ----------------------------------------------------------------------- - /** - * Reverses the string builder placing each character in the opposite index. - * - * @return this, to enable chaining - */ - public StrBuilder reverse() { - if (size == 0) { - return this; - } - - final int half = size / 2; - final char[] buf = buffer; - for (int leftIdx = 0, rightIdx = size - 1; leftIdx < half; leftIdx++, rightIdx--) { - final char swap = buf[leftIdx]; - buf[leftIdx] = buf[rightIdx]; - buf[rightIdx] = swap; - } - return this; - } - - // ----------------------------------------------------------------------- - /** - * Trims the builder by removing characters less than or equal to a space from - * the beginning and end. - * - * @return this, to enable chaining - */ - public StrBuilder trim() { - if (size == 0) { - return this; - } - int len = size; - final char[] buf = buffer; - int pos = 0; - while (pos < len && buf[pos] <= ' ') { - pos++; - } - while (pos < len && buf[len - 1] <= ' ') { - len--; - } - if (len < size) { - delete(len, size); - } - if (pos > 0) { - delete(0, pos); - } - return this; - } - - // ----------------------------------------------------------------------- - /** - * Checks whether this builder starts with the specified string. - *

- * Note that this method handles null input quietly, unlike String. - * - * @param str the string to search for, null returns false - * @return true if the builder starts with the string - */ - public boolean startsWith(final String str) { - if (str == null) { - return false; - } - final int len = str.length(); - if (len == 0) { - return true; - } - if (len > size) { - return false; - } - for (int i = 0; i < len; i++) { - if (buffer[i] != str.charAt(i)) { - return false; - } - } - return true; - } - - /** - * Checks whether this builder ends with the specified string. - *

- * Note that this method handles null input quietly, unlike String. - * - * @param str the string to search for, null returns false - * @return true if the builder ends with the string - */ - public boolean endsWith(final String str) { - if (str == null) { - return false; - } - final int len = str.length(); - if (len == 0) { - return true; - } - if (len > size) { - return false; - } - int pos = size - len; - for (int i = 0; i < len; i++, pos++) { - if (buffer[pos] != str.charAt(i)) { - return false; - } - } - return true; - } - - // ----------------------------------------------------------------------- - /** - * {@inheritDoc} - * - * @since 3.0 - */ - @Override - public CharSequence subSequence(final int startIndex, final int endIndex) { - if (startIndex < 0) { - throw new StringIndexOutOfBoundsException(startIndex); - } - if (endIndex > size) { - throw new StringIndexOutOfBoundsException(endIndex); - } - if (startIndex > endIndex) { - throw new StringIndexOutOfBoundsException(endIndex - startIndex); - } - return substring(startIndex, endIndex); - } - - /** - * Extracts a portion of this string builder as a string. - * - * @param start the start index, inclusive, must be valid - * @return the new string - * @throws IndexOutOfBoundsException if the index is invalid - */ - public String substring(final int start) { - return substring(start, size); - } - - /** - * Extracts a portion of this string builder as a string. - *

- * Note: This method treats an endIndex greater than the length of the builder - * as equal to the length of the builder, and continues without error, unlike - * StringBuffer or String. - * - * @param startIndex the start index, inclusive, must be valid - * @param endIndex the end index, exclusive, must be valid except that if too - * large it is treated as end of string - * @return the new string - * @throws IndexOutOfBoundsException if the index is invalid - */ - public String substring(final int startIndex, int endIndex) { - endIndex = validateRange(startIndex, endIndex); - return new String(buffer, startIndex, endIndex - startIndex); - } - - /** - * Extracts the leftmost characters from the string builder without throwing an - * exception. - *

- * This method extracts the left {@code length} characters from the builder. If - * this many characters are not available, the whole builder is returned. Thus - * the returned string may be shorter than the length requested. - * - * @param length the number of characters to extract, negative returns empty - * string - * @return the new string - */ - public String leftString(final int length) { - if (length <= 0) { - return StringUtils.EMPTY; - } else if (length >= size) { - return new String(buffer, 0, size); - } else { - return new String(buffer, 0, length); - } - } - - /** - * Extracts the rightmost characters from the string builder without throwing an - * exception. - *

- * This method extracts the right {@code length} characters from the builder. If - * this many characters are not available, the whole builder is returned. Thus - * the returned string may be shorter than the length requested. - * - * @param length the number of characters to extract, negative returns empty - * string - * @return the new string - */ - public String rightString(final int length) { - if (length <= 0) { - return StringUtils.EMPTY; - } else if (length >= size) { - return new String(buffer, 0, size); - } else { - return new String(buffer, size - length, length); - } - } - - /** - * Extracts some characters from the middle of the string builder without - * throwing an exception. - *

- * This method extracts {@code length} characters from the builder at the - * specified index. If the index is negative it is treated as zero. If the index - * is greater than the builder size, it is treated as the builder size. If the - * length is negative, the empty string is returned. If insufficient characters - * are available in the builder, as much as possible is returned. Thus the - * returned string may be shorter than the length requested. - * - * @param index the index to start at, negative means zero - * @param length the number of characters to extract, negative returns empty - * string - * @return the new string - */ - public String midString(int index, final int length) { - if (index < 0) { - index = 0; - } - if (length <= 0 || index >= size) { - return StringUtils.EMPTY; - } - if (size <= index + length) { - return new String(buffer, index, size - index); - } - return new String(buffer, index, length); - } - - // ----------------------------------------------------------------------- - /** - * Checks if the string builder contains the specified char. - * - * @param ch the character to find - * @return true if the builder contains the character - */ - public boolean contains(final char ch) { - final char[] thisBuf = buffer; - for (int i = 0; i < this.size; i++) { - if (thisBuf[i] == ch) { - return true; - } - } - return false; - } - - /** - * Checks if the string builder contains the specified string. - * - * @param str the string to find - * @return true if the builder contains the string - */ - public boolean contains(final String str) { - return indexOf(str, 0) >= 0; - } - - /** - * Checks if the string builder contains a string matched using the specified - * matcher. - *

- * Matchers can be used to perform advanced searching behavior. For example you - * could write a matcher to search for the character 'a' followed by a number. - * - * @param matcher the matcher to use, null returns -1 - * @return true if the matcher finds a match in the builder - */ - public boolean contains(final StrMatcher matcher) { - return indexOf(matcher, 0) >= 0; - } - - // ----------------------------------------------------------------------- - /** - * Searches the string builder to find the first reference to the specified - * char. - * - * @param ch the character to find - * @return the first index of the character, or -1 if not found - */ - public int indexOf(final char ch) { - return indexOf(ch, 0); - } - - /** - * Searches the string builder to find the first reference to the specified - * char. - * - * @param ch the character to find - * @param startIndex the index to start at, invalid index rounded to edge - * @return the first index of the character, or -1 if not found - */ - public int indexOf(final char ch, int startIndex) { - startIndex = (Math.max(startIndex, 0)); - if (startIndex >= size) { - return -1; - } - final char[] thisBuf = buffer; - for (int i = startIndex; i < size; i++) { - if (thisBuf[i] == ch) { - return i; - } - } - return -1; - } - - /** - * Searches the string builder to find the first reference to the specified - * string. - *

- * Note that a null input string will return -1, whereas the JDK throws an - * exception. - * - * @param str the string to find, null returns -1 - * @return the first index of the string, or -1 if not found - */ - public int indexOf(final String str) { - return indexOf(str, 0); - } - - /** - * Searches the string builder to find the first reference to the specified - * string starting searching from the given index. - *

- * Note that a null input string will return -1, whereas the JDK throws an - * exception. - * - * @param str the string to find, null returns -1 - * @param startIndex the index to start at, invalid index rounded to edge - * @return the first index of the string, or -1 if not found - */ - public int indexOf(final String str, int startIndex) { - startIndex = (Math.max(startIndex, 0)); - if (str == null || startIndex >= size) { - return -1; - } - final int strLen = str.length(); - if (strLen == 1) { - return indexOf(str.charAt(0), startIndex); - } - if (strLen == 0) { - return startIndex; - } - if (strLen > size) { - return -1; - } - final char[] thisBuf = buffer; - final int len = size - strLen + 1; - outer: for (int i = startIndex; i < len; i++) { - for (int j = 0; j < strLen; j++) { - if (str.charAt(j) != thisBuf[i + j]) { - continue outer; - } - } - return i; - } - return -1; - } - - /** - * Searches the string builder using the matcher to find the first match. - *

- * Matchers can be used to perform advanced searching behavior. For example you - * could write a matcher to find the character 'a' followed by a number. - * - * @param matcher the matcher to use, null returns -1 - * @return the first index matched, or -1 if not found - */ - public int indexOf(final StrMatcher matcher) { - return indexOf(matcher, 0); - } - - /** - * Searches the string builder using the matcher to find the first match - * searching from the given index. - *

- * Matchers can be used to perform advanced searching behavior. For example you - * could write a matcher to find the character 'a' followed by a number. - * - * @param matcher the matcher to use, null returns -1 - * @param startIndex the index to start at, invalid index rounded to edge - * @return the first index matched, or -1 if not found - */ - public int indexOf(final StrMatcher matcher, int startIndex) { - startIndex = (Math.max(startIndex, 0)); - if (matcher == null || startIndex >= size) { - return -1; - } - final int len = size; - final char[] buf = buffer; - for (int i = startIndex; i < len; i++) { - if (matcher.isMatch(buf, i, startIndex, len) > 0) { - return i; - } - } - return -1; - } - - // ----------------------------------------------------------------------- - /** - * Searches the string builder to find the last reference to the specified char. - * - * @param ch the character to find - * @return the last index of the character, or -1 if not found - */ - public int lastIndexOf(final char ch) { - return lastIndexOf(ch, size - 1); - } - - /** - * Searches the string builder to find the last reference to the specified char. - * - * @param ch the character to find - * @param startIndex the index to start at, invalid index rounded to edge - * @return the last index of the character, or -1 if not found - */ - public int lastIndexOf(final char ch, int startIndex) { - startIndex = (startIndex >= size ? size - 1 : startIndex); - if (startIndex < 0) { - return -1; - } - for (int i = startIndex; i >= 0; i--) { - if (buffer[i] == ch) { - return i; - } - } - return -1; - } - - /** - * Searches the string builder to find the last reference to the specified - * string. - *

- * Note that a null input string will return -1, whereas the JDK throws an - * exception. - * - * @param str the string to find, null returns -1 - * @return the last index of the string, or -1 if not found - */ - public int lastIndexOf(final String str) { - return lastIndexOf(str, size - 1); - } - - /** - * Searches the string builder to find the last reference to the specified - * string starting searching from the given index. - *

- * Note that a null input string will return -1, whereas the JDK throws an - * exception. - * - * @param str the string to find, null returns -1 - * @param startIndex the index to start at, invalid index rounded to edge - * @return the last index of the string, or -1 if not found - */ - public int lastIndexOf(final String str, int startIndex) { - startIndex = (startIndex >= size ? size - 1 : startIndex); - if (str == null || startIndex < 0) { - return -1; - } - final int strLen = str.length(); - if (strLen > 0 && strLen <= size) { - if (strLen == 1) { - return lastIndexOf(str.charAt(0), startIndex); - } - - outer: for (int i = startIndex - strLen + 1; i >= 0; i--) { - for (int j = 0; j < strLen; j++) { - if (str.charAt(j) != buffer[i + j]) { - continue outer; - } - } - return i; - } - - } else if (strLen == 0) { - return startIndex; - } - return -1; - } - - /** - * Searches the string builder using the matcher to find the last match. - *

- * Matchers can be used to perform advanced searching behavior. For example you - * could write a matcher to find the character 'a' followed by a number. - * - * @param matcher the matcher to use, null returns -1 - * @return the last index matched, or -1 if not found - */ - public int lastIndexOf(final StrMatcher matcher) { - return lastIndexOf(matcher, size); - } - - /** - * Searches the string builder using the matcher to find the last match - * searching from the given index. - *

- * Matchers can be used to perform advanced searching behavior. For example you - * could write a matcher to find the character 'a' followed by a number. - * - * @param matcher the matcher to use, null returns -1 - * @param startIndex the index to start at, invalid index rounded to edge - * @return the last index matched, or -1 if not found - */ - public int lastIndexOf(final StrMatcher matcher, int startIndex) { - startIndex = (startIndex >= size ? size - 1 : startIndex); - if (matcher == null || startIndex < 0) { - return -1; - } - final char[] buf = buffer; - final int endIndex = startIndex + 1; - for (int i = startIndex; i >= 0; i--) { - if (matcher.isMatch(buf, i, 0, endIndex) > 0) { - return i; - } - } - return -1; - } - - // ----------------------------------------------------------------------- - /** - * Creates a tokenizer that can tokenize the contents of this builder. - *

- * This method allows the contents of this builder to be tokenized. The - * tokenizer will be setup by default to tokenize on space, tab, newline and - * formfeed (as per StringTokenizer). These values can be changed on the - * tokenizer class, before retrieving the tokens. - *

- * The returned tokenizer is linked to this builder. You may intermix calls to - * the builder and tokenizer within certain limits, however there is no - * synchronization. Once the tokenizer has been used once, it must be - * {@link StrTokenizer#reset() reset} to pickup the latest changes in the - * builder. For example: - * - *

-	 * StrBuilder b = new StrBuilder();
-	 * b.append("a b ");
-	 * StrTokenizer t = b.asTokenizer();
-	 * String[] tokens1 = t.getTokenArray(); // returns a,b
-	 * b.append("c d ");
-	 * String[] tokens2 = t.getTokenArray(); // returns a,b (c and d ignored)
-	 * t.reset(); // reset causes builder changes to be picked up
-	 * String[] tokens3 = t.getTokenArray(); // returns a,b,c,d
-	 * 
- * - * In addition to simply intermixing appends and tokenization, you can also call - * the set methods on the tokenizer to alter how it tokenizes. Just remember to - * call reset when you want to pickup builder changes. - *

- * Calling {@link StrTokenizer#reset(String)} or - * {@link StrTokenizer#reset(char[])} with a non-null value will break the link - * with the builder. - * - * @return a tokenizer that is linked to this builder - */ - public StrTokenizer asTokenizer() { - return new StrBuilderTokenizer(); - } - - // ----------------------------------------------------------------------- - /** - * Gets the contents of this builder as a Reader. - *

- * This method allows the contents of the builder to be read using any standard - * method that expects a Reader. - *

- * To use, simply create a {@code StrBuilder}, populate it with data, call - * {@code asReader}, and then read away. - *

- * The internal character array is shared between the builder and the reader. - * This allows you to append to the builder after creating the reader, and the - * changes will be picked up. Note however, that no synchronization occurs, so - * you must perform all operations with the builder and the reader in one - * thread. - *

- * The returned reader supports marking, and ignores the flush method. - * - * @return a reader that reads from this builder - */ - public Reader asReader() { - return new StrBuilderReader(); - } - - // ----------------------------------------------------------------------- - /** - * Gets this builder as a Writer that can be written to. - *

- * This method allows you to populate the contents of the builder using any - * standard method that takes a Writer. - *

- * To use, simply create a {@code StrBuilder}, call {@code asWriter}, and - * populate away. The data is available at any time using the methods of the - * {@code StrBuilder}. - *

- * The internal character array is shared between the builder and the writer. - * This allows you to intermix calls that append to the builder and write using - * the writer and the changes will be occur correctly. Note however, that no - * synchronization occurs, so you must perform all operations with the builder - * and the writer in one thread. - *

- * The returned writer ignores the close and flush methods. - * - * @return a writer that populates this builder - */ - public Writer asWriter() { - return new StrBuilderWriter(); - } - - /** - * Appends current contents of this {@code StrBuilder} to the provided - * {@link Appendable}. - *

- * This method tries to avoid doing any extra copies of contents. - * - * @param appendable the appendable to append data to - * @throws IOException if an I/O error occurs - * - * @since 3.4 - * @see #readFrom(Readable) - */ - public void appendTo(final Appendable appendable) throws IOException { - if (appendable instanceof Writer) { - ((Writer) appendable).write(buffer, 0, size); - } else if (appendable instanceof StringBuilder) { - ((StringBuilder) appendable).append(buffer, 0, size); - } else if (appendable instanceof StringBuffer) { - ((StringBuffer) appendable).append(buffer, 0, size); - } else if (appendable instanceof CharBuffer) { - ((CharBuffer) appendable).put(buffer, 0, size); - } else { - appendable.append(this); - } - } - - /** - * Checks the contents of this builder against another to see if they contain - * the same character content ignoring case. - * - * @param other the object to check, null returns false - * @return true if the builders contain the same characters in the same order - */ - public boolean equalsIgnoreCase(final StrBuilder other) { - if (this == other) { - return true; - } - if (this.size != other.size) { - return false; - } - final char[] thisBuf = this.buffer; - final char[] otherBuf = other.buffer; - for (int i = size - 1; i >= 0; i--) { - final char c1 = thisBuf[i]; - final char c2 = otherBuf[i]; - if (c1 != c2 && Character.toUpperCase(c1) != Character.toUpperCase(c2)) { - return false; - } - } - return true; - } - - /** - * Checks the contents of this builder against another to see if they contain - * the same character content. - * - * @param other the object to check, null returns false - * @return true if the builders contain the same characters in the same order - */ - public boolean equals(final StrBuilder other) { - if (this == other) { - return true; - } - if (other == null) { - return false; - } - if (this.size != other.size) { - return false; - } - final char[] thisBuf = this.buffer; - final char[] otherBuf = other.buffer; - for (int i = size - 1; i >= 0; i--) { - if (thisBuf[i] != otherBuf[i]) { - return false; - } - } - return true; - } - - /** - * Checks the contents of this builder against another to see if they contain - * the same character content. - * - * @param obj the object to check, null returns false - * @return true if the builders contain the same characters in the same order - */ - @Override - public boolean equals(final Object obj) { - return obj instanceof StrBuilder && equals((StrBuilder) obj); - } - - /** - * Gets a suitable hash code for this builder. - * - * @return a hash code - */ - @Override - public int hashCode() { - final char[] buf = buffer; - int hash = 0; - for (int i = size - 1; i >= 0; i--) { - hash = 31 * hash + buf[i]; - } - return hash; - } - - // ----------------------------------------------------------------------- - /** - * Gets a String version of the string builder, creating a new instance each - * time the method is called. - *

- * Note that unlike StringBuffer, the string version returned is independent of - * the string builder. - * - * @return the builder as a String - */ - @Override - public String toString() { - return new String(buffer, 0, size); - } - - /** - * Gets a StringBuffer version of the string builder, creating a new instance - * each time the method is called. - * - * @return the builder as a StringBuffer - */ - public StringBuffer toStringBuffer() { - return new StringBuffer(size).append(buffer, 0, size); - } - - /** - * Gets a StringBuilder version of the string builder, creating a new instance - * each time the method is called. - * - * @return the builder as a StringBuilder - * @since 3.2 - */ - public StringBuilder toStringBuilder() { - return new StringBuilder(size).append(buffer, 0, size); - } - - /** - * Implement the {@link Builder} interface. - * - * @return the builder as a String - * @since 3.2 - * @see #toString() - */ - @Override - public String build() { - return toString(); - } - - // ----------------------------------------------------------------------- - /** - * Validates parameters defining a range of the builder. - * - * @param startIndex the start index, inclusive, must be valid - * @param endIndex the end index, exclusive, must be valid except that if too - * large it is treated as end of string - * @return the new string - * @throws IndexOutOfBoundsException if the index is invalid - */ - protected int validateRange(final int startIndex, int endIndex) { - if (startIndex < 0) { - throw new StringIndexOutOfBoundsException(startIndex); - } - if (endIndex > size) { - endIndex = size; - } - if (startIndex > endIndex) { - throw new StringIndexOutOfBoundsException("end < start"); - } - return endIndex; - } - - /** - * Validates parameters defining a single index in the builder. - * - * @param index the index, must be valid - * @throws IndexOutOfBoundsException if the index is invalid - */ - protected void validateIndex(final int index) { - if (index < 0 || index > size) { - throw new StringIndexOutOfBoundsException(index); - } - } - - // ----------------------------------------------------------------------- - /** - * Inner class to allow StrBuilder to operate as a tokenizer. - */ - class StrBuilderTokenizer extends StrTokenizer { - - /** - * Default constructor. - */ - StrBuilderTokenizer() { - } - - /** {@inheritDoc} */ - @Override - protected List tokenize(final char[] chars, final int offset, final int count) { - if (chars == null) { - return super.tokenize(StrBuilder.this.buffer, 0, StrBuilder.this.size()); - } - return super.tokenize(chars, offset, count); - } - - /** {@inheritDoc} */ - @Override - public String getContent() { - final String str = super.getContent(); - if (str == null) { - return StrBuilder.this.toString(); - } - return str; - } - } - - // ----------------------------------------------------------------------- - /** - * Inner class to allow StrBuilder to operate as a reader. - */ - class StrBuilderReader extends Reader { - /** The current stream position. */ - private int pos; - /** The last mark position. */ - private int mark; - - /** - * Default constructor. - */ - StrBuilderReader() { - } - - /** {@inheritDoc} */ - @Override - public void close() { - // do nothing - } - - /** {@inheritDoc} */ - @Override - public int read() { - if (ready() == false) { - return -1; - } - return StrBuilder.this.charAt(pos++); - } - - /** {@inheritDoc} */ - @Override - public int read(final char[] b, final int off, int len) { - if (off < 0 || len < 0 || off > b.length || (off + len) > b.length || (off + len) < 0) { - throw new IndexOutOfBoundsException(); - } - if (len == 0) { - return 0; - } - if (pos >= StrBuilder.this.size()) { - return -1; - } - if (pos + len > size()) { - len = StrBuilder.this.size() - pos; - } - StrBuilder.this.getChars(pos, pos + len, b, off); - pos += len; - return len; - } - - /** {@inheritDoc} */ - @Override - public long skip(long n) { - if (pos + n > StrBuilder.this.size()) { - n = StrBuilder.this.size() - pos; - } - if (n < 0) { - return 0; - } - pos += n; - return n; - } - - /** {@inheritDoc} */ - @Override - public boolean ready() { - return pos < StrBuilder.this.size(); - } - - /** {@inheritDoc} */ - @Override - public boolean markSupported() { - return true; - } - - /** {@inheritDoc} */ - @Override - public void mark(final int readAheadLimit) { - mark = pos; - } - - /** {@inheritDoc} */ - @Override - public void reset() { - pos = mark; - } - } - - // ----------------------------------------------------------------------- - /** - * Inner class to allow StrBuilder to operate as a writer. - */ - class StrBuilderWriter extends Writer { - - /** - * Default constructor. - */ - StrBuilderWriter() { - } - - /** {@inheritDoc} */ - @Override - public void close() { - // do nothing - } - - /** {@inheritDoc} */ - @Override - public void flush() { - // do nothing - } - - /** {@inheritDoc} */ - @Override - public void write(final int c) { - StrBuilder.this.append((char) c); - } - - /** {@inheritDoc} */ - @Override - public void write(final char[] cbuf) { - StrBuilder.this.append(cbuf); - } - - /** {@inheritDoc} */ - @Override - public void write(final char[] cbuf, final int off, final int len) { - StrBuilder.this.append(cbuf, off, len); - } - - /** {@inheritDoc} */ - @Override - public void write(final String str) { - StrBuilder.this.append(str); - } - - /** {@inheritDoc} */ - @Override - public void write(final String str, final int off, final int len) { - StrBuilder.this.append(str, off, len); - } - } - -} diff --git a/src/main/java/org/apache/commons/lang3/text/StrLookup.java b/src/main/java/org/apache/commons/lang3/text/StrLookup.java deleted file mode 100755 index 0c7caecd..00000000 --- a/src/main/java/org/apache/commons/lang3/text/StrLookup.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.text; - -import java.util.Map; - -/** - * Lookup a String key to a String value. - *

- * This class represents the simplest form of a string to string map. It has a - * benefit over a map in that it can create the result on demand based on the - * key. - *

- * This class comes complete with various factory methods. If these do not - * suffice, you can subclass and implement your own matcher. - *

- * For example, it would be possible to implement a lookup that used the key as - * a primary key, and looked up the value on demand from the database. - * - * @param Unused. - * @since 2.2 - * @!deprecated as of 3.6, use commons-text - * StringLookupFactory instead - */ -//@Deprecated -public abstract class StrLookup { - - /** - * Lookup that always returns null. - */ - private static final StrLookup NONE_LOOKUP = new MapStrLookup<>(null); - - /** - * Lookup based on system properties. - */ - private static final StrLookup SYSTEM_PROPERTIES_LOOKUP = new SystemPropertiesStrLookup(); - - // ----------------------------------------------------------------------- - /** - * Returns a lookup which always returns null. - * - * @return a lookup that always returns null, not null - */ - public static StrLookup noneLookup() { - return NONE_LOOKUP; - } - - /** - * Returns a new lookup which uses a copy of the current - * {@link System#getProperties() System properties}. - *

- * If a security manager blocked access to system properties, then null will be - * returned from every lookup. - *

- * If a null key is used, this lookup will throw a NullPointerException. - * - * @return a lookup using system properties, not null - */ - public static StrLookup systemPropertiesLookup() { - return SYSTEM_PROPERTIES_LOOKUP; - } - - /** - * Returns a lookup which looks up values using a map. - *

- * If the map is null, then null will be returned from every lookup. The map - * result object is converted to a string using toString(). - * - * @param the type of the values supported by the lookup - * @param map the map of keys to values, may be null - * @return a lookup using the map, not null - */ - public static StrLookup mapLookup(final Map map) { - return new MapStrLookup<>(map); - } - - // ----------------------------------------------------------------------- - /** - * Constructor. - */ - protected StrLookup() { - } - - /** - * Looks up a String key to a String value. - *

- * The internal implementation may use any mechanism to return the value. The - * simplest implementation is to use a Map. However, virtually any - * implementation is possible. - *

- * For example, it would be possible to implement a lookup that used the key as - * a primary key, and looked up the value on demand from the database Or, a - * numeric based implementation could be created that treats the key as an - * integer, increments the value and return the result as a string - converting - * 1 to 2, 15 to 16 etc. - *

- * The {@link #lookup(String)} method always returns a String, regardless of the - * underlying data, by converting it as necessary. For example: - * - *

-	 * Map<String, Object> map = new HashMap<String, Object>();
-	 * map.put("number", Integer.valueOf(2));
-	 * assertEquals("2", StrLookup.mapLookup(map).lookup("number"));
-	 * 
- * - * @param key the key to be looked up, may be null - * @return the matching value, null if no match - */ - public abstract String lookup(String key); - - // ----------------------------------------------------------------------- - /** - * Lookup implementation that uses a Map. - */ - static class MapStrLookup extends StrLookup { - - /** Map keys are variable names and value. */ - private final Map map; - - /** - * Creates a new instance backed by a Map. - * - * @param map the map of keys to values, may be null - */ - MapStrLookup(final Map map) { - this.map = map; - } - - /** - * Looks up a String key to a String value using the map. - *

- * If the map is null, then null is returned. The map result object is converted - * to a string using toString(). - * - * @param key the key to be looked up, may be null - * @return the matching value, null if no match - */ - @Override - public String lookup(final String key) { - if (map == null) { - return null; - } - final Object obj = map.get(key); - if (obj == null) { - return null; - } - return obj.toString(); - } - } - - // ----------------------------------------------------------------------- - /** - * Lookup implementation based on system properties. - */ - private static class SystemPropertiesStrLookup extends StrLookup { - /** - * {@inheritDoc} This implementation directly accesses system properties. - */ - @Override - public String lookup(final String key) { - if (!key.isEmpty()) { - try { - return System.getProperty(key); - } catch (final SecurityException scex) { - // Squelched. All lookup(String) will return null. - } - } - return null; - } - } -} diff --git a/src/main/java/org/apache/commons/lang3/text/StrMatcher.java b/src/main/java/org/apache/commons/lang3/text/StrMatcher.java deleted file mode 100755 index c2f417b2..00000000 --- a/src/main/java/org/apache/commons/lang3/text/StrMatcher.java +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.text; - -import java.util.Arrays; - -import org.apache.commons.lang3.ArraySorter; -import org.apache.commons.lang3.StringUtils; - -/** - * A matcher class that can be queried to determine if a character array portion - * matches. - *

- * This class comes complete with various factory methods. If these do not - * suffice, you can subclass and implement your own matcher. - * - * @since 2.2 - * @!deprecated as of 3.6, use commons-text - * StringMatcherFactory instead - */ -//@Deprecated -public abstract class StrMatcher { - - /** - * Matches the comma character. - */ - private static final StrMatcher COMMA_MATCHER = new CharMatcher(','); - /** - * Matches the tab character. - */ - private static final StrMatcher TAB_MATCHER = new CharMatcher('\t'); - /** - * Matches the space character. - */ - private static final StrMatcher SPACE_MATCHER = new CharMatcher(' '); - /** - * Matches the same characters as StringTokenizer, namely space, tab, newline, - * formfeed. - */ - private static final StrMatcher SPLIT_MATCHER = new CharSetMatcher(" \t\n\r\f".toCharArray()); - /** - * Matches the String trim() whitespace characters. - */ - private static final StrMatcher TRIM_MATCHER = new TrimMatcher(); - /** - * Matches the double quote character. - */ - private static final StrMatcher SINGLE_QUOTE_MATCHER = new CharMatcher('\''); - /** - * Matches the double quote character. - */ - private static final StrMatcher DOUBLE_QUOTE_MATCHER = new CharMatcher('"'); - /** - * Matches the single or double quote character. - */ - private static final StrMatcher QUOTE_MATCHER = new CharSetMatcher("'\"".toCharArray()); - /** - * Matches no characters. - */ - private static final StrMatcher NONE_MATCHER = new NoMatcher(); - - // ----------------------------------------------------------------------- - - /** - * Returns a matcher which matches the comma character. - * - * @return a matcher for a comma - */ - public static StrMatcher commaMatcher() { - return COMMA_MATCHER; - } - - /** - * Returns a matcher which matches the tab character. - * - * @return a matcher for a tab - */ - public static StrMatcher tabMatcher() { - return TAB_MATCHER; - } - - /** - * Returns a matcher which matches the space character. - * - * @return a matcher for a space - */ - public static StrMatcher spaceMatcher() { - return SPACE_MATCHER; - } - - /** - * Matches the same characters as StringTokenizer, namely space, tab, newline - * and formfeed. - * - * @return the split matcher - */ - public static StrMatcher splitMatcher() { - return SPLIT_MATCHER; - } - - /** - * Matches the String trim() whitespace characters. - * - * @return the trim matcher - */ - public static StrMatcher trimMatcher() { - return TRIM_MATCHER; - } - - /** - * Returns a matcher which matches the single quote character. - * - * @return a matcher for a single quote - */ - public static StrMatcher singleQuoteMatcher() { - return SINGLE_QUOTE_MATCHER; - } - - /** - * Returns a matcher which matches the double quote character. - * - * @return a matcher for a double quote - */ - public static StrMatcher doubleQuoteMatcher() { - return DOUBLE_QUOTE_MATCHER; - } - - /** - * Returns a matcher which matches the single or double quote character. - * - * @return a matcher for a single or double quote - */ - public static StrMatcher quoteMatcher() { - return QUOTE_MATCHER; - } - - /** - * Matches no characters. - * - * @return a matcher that matches nothing - */ - public static StrMatcher noneMatcher() { - return NONE_MATCHER; - } - - /** - * Constructor that creates a matcher from a character. - * - * @param ch the character to match, must not be null - * @return a new Matcher for the given char - */ - public static StrMatcher charMatcher(final char ch) { - return new CharMatcher(ch); - } - - /** - * Constructor that creates a matcher from a set of characters. - * - * @param chars the characters to match, null or empty matches nothing - * @return a new matcher for the given char[] - */ - public static StrMatcher charSetMatcher(final char... chars) { - if (chars == null || chars.length == 0) { - return NONE_MATCHER; - } - if (chars.length == 1) { - return new CharMatcher(chars[0]); - } - return new CharSetMatcher(chars); - } - - /** - * Constructor that creates a matcher from a string representing a set of - * characters. - * - * @param chars the characters to match, null or empty matches nothing - * @return a new Matcher for the given characters - */ - public static StrMatcher charSetMatcher(final String chars) { - if (StringUtils.isEmpty(chars)) { - return NONE_MATCHER; - } - if (chars.length() == 1) { - return new CharMatcher(chars.charAt(0)); - } - return new CharSetMatcher(chars.toCharArray()); - } - - /** - * Constructor that creates a matcher from a string. - * - * @param str the string to match, null or empty matches nothing - * @return a new Matcher for the given String - */ - public static StrMatcher stringMatcher(final String str) { - if (StringUtils.isEmpty(str)) { - return NONE_MATCHER; - } - return new StringMatcher(str); - } - - // ----------------------------------------------------------------------- - /** - * Constructor. - */ - protected StrMatcher() { - } - - /** - * Returns the number of matching characters, zero for no match. - *

- * This method is called to check for a match. The parameter {@code pos} - * represents the current position to be checked in the string {@code buffer} (a - * character array which must not be changed). The API guarantees that - * {@code pos} is a valid index for {@code buffer}. - *

- * The character array may be larger than the active area to be matched. Only - * values in the buffer between the specified indices may be accessed. - *

- * The matching code may check one character or many. It may check characters - * preceding {@code pos} as well as those after, so long as no checks exceed the - * bounds specified. - *

- * It must return zero for no match, or a positive number if a match was found. - * The number indicates the number of characters that matched. - * - * @param buffer the text content to match against, do not change - * @param pos the starting position for the match, valid for buffer - * @param bufferStart the first active index in the buffer, valid for buffer - * @param bufferEnd the end index (exclusive) of the active buffer, valid for - * buffer - * @return the number of matching characters, zero for no match - */ - public abstract int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd); - - /** - * Returns the number of matching characters, zero for no match. - *

- * This method is called to check for a match. The parameter {@code pos} - * represents the current position to be checked in the string {@code buffer} (a - * character array which must not be changed). The API guarantees that - * {@code pos} is a valid index for {@code buffer}. - *

- * The matching code may check one character or many. It may check characters - * preceding {@code pos} as well as those after. - *

- * It must return zero for no match, or a positive number if a match was found. - * The number indicates the number of characters that matched. - * - * @param buffer the text content to match against, do not change - * @param pos the starting position for the match, valid for buffer - * @return the number of matching characters, zero for no match - * @since 2.4 - */ - public int isMatch(final char[] buffer, final int pos) { - return isMatch(buffer, pos, 0, buffer.length); - } - - // ----------------------------------------------------------------------- - /** - * Class used to define a set of characters for matching purposes. - */ - static final class CharSetMatcher extends StrMatcher { - /** The set of characters to match. */ - private final char[] chars; - - /** - * Constructor that creates a matcher from a character array. - * - * @param chars the characters to match, must not be null - */ - CharSetMatcher(final char[] chars) { - this.chars = ArraySorter.sort(chars.clone()); - } - - /** - * Returns whether or not the given character matches. - * - * @param buffer the text content to match against, do not change - * @param pos the starting position for the match, valid for buffer - * @param bufferStart the first active index in the buffer, valid for buffer - * @param bufferEnd the end index of the active buffer, valid for buffer - * @return the number of matching characters, zero for no match - */ - @Override - public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) { - return Arrays.binarySearch(chars, buffer[pos]) >= 0 ? 1 : 0; - } - } - - // ----------------------------------------------------------------------- - /** - * Class used to define a character for matching purposes. - */ - static final class CharMatcher extends StrMatcher { - /** The character to match. */ - private final char ch; - - /** - * Constructor that creates a matcher that matches a single character. - * - * @param ch the character to match - */ - CharMatcher(final char ch) { - this.ch = ch; - } - - /** - * Returns whether or not the given character matches. - * - * @param buffer the text content to match against, do not change - * @param pos the starting position for the match, valid for buffer - * @param bufferStart the first active index in the buffer, valid for buffer - * @param bufferEnd the end index of the active buffer, valid for buffer - * @return the number of matching characters, zero for no match - */ - @Override - public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) { - return ch == buffer[pos] ? 1 : 0; - } - } - - // ----------------------------------------------------------------------- - /** - * Class used to define a set of characters for matching purposes. - */ - static final class StringMatcher extends StrMatcher { - /** The string to match, as a character array. */ - private final char[] chars; - - /** - * Constructor that creates a matcher from a String. - * - * @param str the string to match, must not be null - */ - StringMatcher(final String str) { - chars = str.toCharArray(); - } - - /** - * Returns whether or not the given text matches the stored string. - * - * @param buffer the text content to match against, do not change - * @param pos the starting position for the match, valid for buffer - * @param bufferStart the first active index in the buffer, valid for buffer - * @param bufferEnd the end index of the active buffer, valid for buffer - * @return the number of matching characters, zero for no match - */ - @Override - public int isMatch(final char[] buffer, int pos, final int bufferStart, final int bufferEnd) { - final int len = chars.length; - if (pos + len > bufferEnd) { - return 0; - } - for (int i = 0; i < chars.length; i++, pos++) { - if (chars[i] != buffer[pos]) { - return 0; - } - } - return len; - } - - @Override - public String toString() { - return super.toString() + ' ' + Arrays.toString(chars); - } - - } - - // ----------------------------------------------------------------------- - /** - * Class used to match no characters. - */ - static final class NoMatcher extends StrMatcher { - - /** - * Constructs a new instance of {@code NoMatcher}. - */ - NoMatcher() { - } - - /** - * Always returns {@code false}. - * - * @param buffer the text content to match against, do not change - * @param pos the starting position for the match, valid for buffer - * @param bufferStart the first active index in the buffer, valid for buffer - * @param bufferEnd the end index of the active buffer, valid for buffer - * @return the number of matching characters, zero for no match - */ - @Override - public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) { - return 0; - } - } - - // ----------------------------------------------------------------------- - /** - * Class used to match whitespace as per trim(). - */ - static final class TrimMatcher extends StrMatcher { - - /** - * Constructs a new instance of {@code TrimMatcher}. - */ - TrimMatcher() { - } - - /** - * Returns whether or not the given character matches. - * - * @param buffer the text content to match against, do not change - * @param pos the starting position for the match, valid for buffer - * @param bufferStart the first active index in the buffer, valid for buffer - * @param bufferEnd the end index of the active buffer, valid for buffer - * @return the number of matching characters, zero for no match - */ - @Override - public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) { - return buffer[pos] <= 32 ? 1 : 0; - } - } - -} diff --git a/src/main/java/org/apache/commons/lang3/text/StrSubstitutor.java b/src/main/java/org/apache/commons/lang3/text/StrSubstitutor.java deleted file mode 100755 index 7c44d489..00000000 --- a/src/main/java/org/apache/commons/lang3/text/StrSubstitutor.java +++ /dev/null @@ -1,1262 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.text; - -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import org.apache.commons.lang3.StringUtils; - -/** - * Substitutes variables within a string by values. - *

- * This class takes a piece of text and substitutes all the variables within it. - * The default definition of a variable is {@code ${variableName}}. The prefix - * and suffix can be changed via constructors and set methods. - *

- * Variable values are typically resolved from a map, but could also be resolved - * from system properties, or by supplying a custom variable resolver. - *

- * The simplest example is to use this class to replace Java System properties. - * For example: - * - *

- * StrSubstitutor
- * 		.replaceSystemProperties("You are running with java.version = ${java.version} and os.name = ${os.name}.");
- * 
- *

- * Typical usage of this class follows the following pattern: First an instance - * is created and initialized with the map that contains the values for the - * available variables. If a prefix and/or suffix for variables should be used - * other than the default ones, the appropriate settings can be performed. After - * that the {@code replace()} method can be called passing in the source text - * for interpolation. In the returned text all variable references (as long as - * their values are known) will be resolved. The following example demonstrates - * this: - * - *

- * Map valuesMap = HashMap();
- * valuesMap.put("animal", "quick brown fox");
- * valuesMap.put("target", "lazy dog");
- * String templateString = "The ${animal} jumps over the ${target}.";
- * StrSubstitutor sub = new StrSubstitutor(valuesMap);
- * String resolvedString = sub.replace(templateString);
- * 
- * - * yielding: - * - *
- *      The quick brown fox jumps over the lazy dog.
- * 
- *

- * Also, this class allows to set a default value for unresolved variables. The - * default value for a variable can be appended to the variable name after the - * variable default value delimiter. The default value of the variable default - * value delimiter is ':-', as in bash and other *nix shells, as those are - * arguably where the default ${} delimiter set originated. The variable default - * value delimiter can be manually set by calling - * {@link #setValueDelimiterMatcher(StrMatcher)}, - * {@link #setValueDelimiter(char)} or {@link #setValueDelimiter(String)}. The - * following shows an example with variable default value settings: - * - *

- * Map valuesMap = HashMap();
- * valuesMap.put("animal", "quick brown fox");
- * valuesMap.put("target", "lazy dog");
- * String templateString = "The ${animal} jumps over the ${target}. ${undefined.number:-1234567890}.";
- * StrSubstitutor sub = new StrSubstitutor(valuesMap);
- * String resolvedString = sub.replace(templateString);
- * 
- * - * yielding: - * - *
- *      The quick brown fox jumps over the lazy dog. 1234567890.
- * 
- *

- * In addition to this usage pattern there are some static convenience methods - * that cover the most common use cases. These methods can be used without the - * need of manually creating an instance. However if multiple replace operations - * are to be performed, creating and reusing an instance of this class will be - * more efficient. - *

- * Variable replacement works in a recursive way. Thus, if a variable value - * contains a variable then that variable will also be replaced. Cyclic - * replacements are detected and will cause an exception to be thrown. - *

- * Sometimes the interpolation's result must contain a variable prefix. As an - * example take the following source text: - * - *

- *   The variable ${${name}} must be used.
- * 
- * - * Here only the variable's name referred to in the text should be replaced - * resulting in the text (assuming that the value of the {@code name} variable - * is {@code x}): - * - *
- *   The variable ${x} must be used.
- * 
- * - * To achieve this effect there are two possibilities: Either set a different - * prefix and suffix for variables which do not conflict with the result text - * you want to produce. The other possibility is to use the escape character, by - * default '$'. If this character is placed before a variable reference, this - * reference is ignored and won't be replaced. For example: - * - *
- *   The variable $${${name}} must be used.
- * 
- *

- * In some complex scenarios you might even want to perform substitution in the - * names of variables, for instance - * - *

- * ${jre-${java.specification.version}}
- * 
- * - * {@code StrSubstitutor} supports this recursive substitution in variable - * names, but it has to be enabled explicitly by setting the - * {@link #setEnableSubstitutionInVariables(boolean) - * enableSubstitutionInVariables} property to true. - *

- * This class is not thread safe. - *

- * - * @since 2.2 - * @!deprecated as of 3.6, use commons-text - * StringSubstitutor instead - */ -//@Deprecated -public class StrSubstitutor { - - /** - * Constant for the default escape character. - */ - public static final char DEFAULT_ESCAPE = '$'; - /** - * Constant for the default variable prefix. - */ - public static final StrMatcher DEFAULT_PREFIX = StrMatcher.stringMatcher("${"); - /** - * Constant for the default variable suffix. - */ - public static final StrMatcher DEFAULT_SUFFIX = StrMatcher.stringMatcher("}"); - /** - * Constant for the default value delimiter of a variable. - * - * @since 3.2 - */ - public static final StrMatcher DEFAULT_VALUE_DELIMITER = StrMatcher.stringMatcher(":-"); - - /** - * Stores the escape character. - */ - private char escapeChar; - /** - * Stores the variable prefix. - */ - private StrMatcher prefixMatcher; - /** - * Stores the variable suffix. - */ - private StrMatcher suffixMatcher; - /** - * Stores the default variable value delimiter - */ - private StrMatcher valueDelimiterMatcher; - /** - * Variable resolution is delegated to an implementor of VariableResolver. - */ - private StrLookup variableResolver; - /** - * The flag whether substitution in variable names is enabled. - */ - private boolean enableSubstitutionInVariables; - /** - * Whether escapes should be preserved. Default is false; - */ - private boolean preserveEscapes; - - // ----------------------------------------------------------------------- - /** - * Replaces all the occurrences of variables in the given source object with - * their matching values from the map. - * - * @param the type of the values in the map - * @param source the source text containing the variables to substitute, null - * returns null - * @param valueMap the map with the values, may be null - * @return the result of the replace operation - */ - public static String replace(final Object source, final Map valueMap) { - return new StrSubstitutor(valueMap).replace(source); - } - - /** - * Replaces all the occurrences of variables in the given source object with - * their matching values from the map. This method allows to specify a custom - * variable prefix and suffix - * - * @param the type of the values in the map - * @param source the source text containing the variables to substitute, null - * returns null - * @param valueMap the map with the values, may be null - * @param prefix the prefix of variables, not null - * @param suffix the suffix of variables, not null - * @return the result of the replace operation - * @throws IllegalArgumentException if the prefix or suffix is null - */ - public static String replace(final Object source, final Map valueMap, final String prefix, - final String suffix) { - return new StrSubstitutor(valueMap, prefix, suffix).replace(source); - } - - /** - * Replaces all the occurrences of variables in the given source object with - * their matching values from the properties. - * - * @param source the source text containing the variables to - * substitute, null returns null - * @param valueProperties the properties with values, may be null - * @return the result of the replace operation - */ - public static String replace(final Object source, final Properties valueProperties) { - if (valueProperties == null) { - return source.toString(); - } - final Map valueMap = new HashMap<>(); - final Enumeration propNames = valueProperties.propertyNames(); - while (propNames.hasMoreElements()) { - final String propName = (String) propNames.nextElement(); - final String propValue = valueProperties.getProperty(propName); - valueMap.put(propName, propValue); - } - return replace(source, valueMap); - } - - /** - * Replaces all the occurrences of variables in the given source object with - * their matching values from the system properties. - * - * @param source the source text containing the variables to substitute, null - * returns null - * @return the result of the replace operation - */ - public static String replaceSystemProperties(final Object source) { - return new StrSubstitutor(StrLookup.systemPropertiesLookup()).replace(source); - } - - // ----------------------------------------------------------------------- - /** - * Creates a new instance with defaults for variable prefix and suffix and the - * escaping character. - */ - public StrSubstitutor() { - this(null, DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ESCAPE); - } - - /** - * Creates a new instance and initializes it. Uses defaults for variable prefix - * and suffix and the escaping character. - * - * @param the type of the values in the map - * @param valueMap the map with the variables' values, may be null - */ - public StrSubstitutor(final Map valueMap) { - this(StrLookup.mapLookup(valueMap), DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ESCAPE); - } - - /** - * Creates a new instance and initializes it. Uses a default escaping character. - * - * @param the type of the values in the map - * @param valueMap the map with the variables' values, may be null - * @param prefix the prefix for variables, not null - * @param suffix the suffix for variables, not null - * @throws IllegalArgumentException if the prefix or suffix is null - */ - public StrSubstitutor(final Map valueMap, final String prefix, final String suffix) { - this(StrLookup.mapLookup(valueMap), prefix, suffix, DEFAULT_ESCAPE); - } - - /** - * Creates a new instance and initializes it. - * - * @param the type of the values in the map - * @param valueMap the map with the variables' values, may be null - * @param prefix the prefix for variables, not null - * @param suffix the suffix for variables, not null - * @param escape the escape character - * @throws IllegalArgumentException if the prefix or suffix is null - */ - public StrSubstitutor(final Map valueMap, final String prefix, final String suffix, - final char escape) { - this(StrLookup.mapLookup(valueMap), prefix, suffix, escape); - } - - /** - * Creates a new instance and initializes it. - * - * @param the type of the values in the map - * @param valueMap the map with the variables' values, may be null - * @param prefix the prefix for variables, not null - * @param suffix the suffix for variables, not null - * @param escape the escape character - * @param valueDelimiter the variable default value delimiter, may be null - * @throws IllegalArgumentException if the prefix or suffix is null - * @since 3.2 - */ - public StrSubstitutor(final Map valueMap, final String prefix, final String suffix, - final char escape, final String valueDelimiter) { - this(StrLookup.mapLookup(valueMap), prefix, suffix, escape, valueDelimiter); - } - - /** - * Creates a new instance and initializes it. - * - * @param variableResolver the variable resolver, may be null - */ - public StrSubstitutor(final StrLookup variableResolver) { - this(variableResolver, DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ESCAPE); - } - - /** - * Creates a new instance and initializes it. - * - * @param variableResolver the variable resolver, may be null - * @param prefix the prefix for variables, not null - * @param suffix the suffix for variables, not null - * @param escape the escape character - * @throws IllegalArgumentException if the prefix or suffix is null - */ - public StrSubstitutor(final StrLookup variableResolver, final String prefix, final String suffix, - final char escape) { - this.setVariableResolver(variableResolver); - this.setVariablePrefix(prefix); - this.setVariableSuffix(suffix); - this.setEscapeChar(escape); - this.setValueDelimiterMatcher(DEFAULT_VALUE_DELIMITER); - } - - /** - * Creates a new instance and initializes it. - * - * @param variableResolver the variable resolver, may be null - * @param prefix the prefix for variables, not null - * @param suffix the suffix for variables, not null - * @param escape the escape character - * @param valueDelimiter the variable default value delimiter string, may be - * null - * @throws IllegalArgumentException if the prefix or suffix is null - * @since 3.2 - */ - public StrSubstitutor(final StrLookup variableResolver, final String prefix, final String suffix, - final char escape, final String valueDelimiter) { - this.setVariableResolver(variableResolver); - this.setVariablePrefix(prefix); - this.setVariableSuffix(suffix); - this.setEscapeChar(escape); - this.setValueDelimiter(valueDelimiter); - } - - /** - * Creates a new instance and initializes it. - * - * @param variableResolver the variable resolver, may be null - * @param prefixMatcher the prefix for variables, not null - * @param suffixMatcher the suffix for variables, not null - * @param escape the escape character - * @throws IllegalArgumentException if the prefix or suffix is null - */ - public StrSubstitutor(final StrLookup variableResolver, final StrMatcher prefixMatcher, - final StrMatcher suffixMatcher, final char escape) { - this(variableResolver, prefixMatcher, suffixMatcher, escape, DEFAULT_VALUE_DELIMITER); - } - - /** - * Creates a new instance and initializes it. - * - * @param variableResolver the variable resolver, may be null - * @param prefixMatcher the prefix for variables, not null - * @param suffixMatcher the suffix for variables, not null - * @param escape the escape character - * @param valueDelimiterMatcher the variable default value delimiter matcher, - * may be null - * @throws IllegalArgumentException if the prefix or suffix is null - * @since 3.2 - */ - public StrSubstitutor(final StrLookup variableResolver, final StrMatcher prefixMatcher, - final StrMatcher suffixMatcher, final char escape, final StrMatcher valueDelimiterMatcher) { - this.setVariableResolver(variableResolver); - this.setVariablePrefixMatcher(prefixMatcher); - this.setVariableSuffixMatcher(suffixMatcher); - this.setEscapeChar(escape); - this.setValueDelimiterMatcher(valueDelimiterMatcher); - } - - // ----------------------------------------------------------------------- - /** - * Replaces all the occurrences of variables with their matching values from the - * resolver using the given source string as a template. - * - * @param source the string to replace in, null returns null - * @return the result of the replace operation - */ - public String replace(final String source) { - if (source == null) { - return null; - } - final StrBuilder buf = new StrBuilder(source); - if (substitute(buf, 0, source.length()) == false) { - return source; - } - return buf.toString(); - } - - /** - * Replaces all the occurrences of variables with their matching values from the - * resolver using the given source string as a template. - *

- * Only the specified portion of the string will be processed. The rest of the - * string is not processed, and is not returned. - * - * @param source the string to replace in, null returns null - * @param offset the start offset within the array, must be valid - * @param length the length within the array to be processed, must be valid - * @return the result of the replace operation - */ - public String replace(final String source, final int offset, final int length) { - if (source == null) { - return null; - } - final StrBuilder buf = new StrBuilder(length).append(source, offset, length); - if (substitute(buf, 0, length) == false) { - return source.substring(offset, offset + length); - } - return buf.toString(); - } - - // ----------------------------------------------------------------------- - /** - * Replaces all the occurrences of variables with their matching values from the - * resolver using the given source array as a template. The array is not altered - * by this method. - * - * @param source the character array to replace in, not altered, null returns - * null - * @return the result of the replace operation - */ - public String replace(final char[] source) { - if (source == null) { - return null; - } - final StrBuilder buf = new StrBuilder(source.length).append(source); - substitute(buf, 0, source.length); - return buf.toString(); - } - - /** - * Replaces all the occurrences of variables with their matching values from the - * resolver using the given source array as a template. The array is not altered - * by this method. - *

- * Only the specified portion of the array will be processed. The rest of the - * array is not processed, and is not returned. - * - * @param source the character array to replace in, not altered, null returns - * null - * @param offset the start offset within the array, must be valid - * @param length the length within the array to be processed, must be valid - * @return the result of the replace operation - */ - public String replace(final char[] source, final int offset, final int length) { - if (source == null) { - return null; - } - final StrBuilder buf = new StrBuilder(length).append(source, offset, length); - substitute(buf, 0, length); - return buf.toString(); - } - - // ----------------------------------------------------------------------- - /** - * Replaces all the occurrences of variables with their matching values from the - * resolver using the given source buffer as a template. The buffer is not - * altered by this method. - * - * @param source the buffer to use as a template, not changed, null returns null - * @return the result of the replace operation - */ - public String replace(final StringBuffer source) { - if (source == null) { - return null; - } - final StrBuilder buf = new StrBuilder(source.length()).append(source); - substitute(buf, 0, buf.length()); - return buf.toString(); - } - - /** - * Replaces all the occurrences of variables with their matching values from the - * resolver using the given source buffer as a template. The buffer is not - * altered by this method. - *

- * Only the specified portion of the buffer will be processed. The rest of the - * buffer is not processed, and is not returned. - * - * @param source the buffer to use as a template, not changed, null returns null - * @param offset the start offset within the array, must be valid - * @param length the length within the array to be processed, must be valid - * @return the result of the replace operation - */ - public String replace(final StringBuffer source, final int offset, final int length) { - if (source == null) { - return null; - } - final StrBuilder buf = new StrBuilder(length).append(source, offset, length); - substitute(buf, 0, length); - return buf.toString(); - } - - /** - * Replaces all the occurrences of variables with their matching values from the - * resolver using the given source as a template. The source is not altered by - * this method. - * - * @param source the buffer to use as a template, not changed, null returns null - * @return the result of the replace operation - * @since 3.2 - */ - public String replace(final CharSequence source) { - if (source == null) { - return null; - } - return replace(source, 0, source.length()); - } - - /** - * Replaces all the occurrences of variables with their matching values from the - * resolver using the given source as a template. The source is not altered by - * this method. - *

- * Only the specified portion of the buffer will be processed. The rest of the - * buffer is not processed, and is not returned. - * - * @param source the buffer to use as a template, not changed, null returns null - * @param offset the start offset within the array, must be valid - * @param length the length within the array to be processed, must be valid - * @return the result of the replace operation - * @since 3.2 - */ - public String replace(final CharSequence source, final int offset, final int length) { - if (source == null) { - return null; - } - final StrBuilder buf = new StrBuilder(length).append(source, offset, length); - substitute(buf, 0, length); - return buf.toString(); - } - - // ----------------------------------------------------------------------- - /** - * Replaces all the occurrences of variables with their matching values from the - * resolver using the given source builder as a template. The builder is not - * altered by this method. - * - * @param source the builder to use as a template, not changed, null returns - * null - * @return the result of the replace operation - */ - public String replace(final StrBuilder source) { - if (source == null) { - return null; - } - final StrBuilder buf = new StrBuilder(source.length()).append(source); - substitute(buf, 0, buf.length()); - return buf.toString(); - } - - /** - * Replaces all the occurrences of variables with their matching values from the - * resolver using the given source builder as a template. The builder is not - * altered by this method. - *

- * Only the specified portion of the builder will be processed. The rest of the - * builder is not processed, and is not returned. - * - * @param source the builder to use as a template, not changed, null returns - * null - * @param offset the start offset within the array, must be valid - * @param length the length within the array to be processed, must be valid - * @return the result of the replace operation - */ - public String replace(final StrBuilder source, final int offset, final int length) { - if (source == null) { - return null; - } - final StrBuilder buf = new StrBuilder(length).append(source, offset, length); - substitute(buf, 0, length); - return buf.toString(); - } - - // ----------------------------------------------------------------------- - /** - * Replaces all the occurrences of variables in the given source object with - * their matching values from the resolver. The input source object is converted - * to a string using {@code toString} and is not altered. - * - * @param source the source to replace in, null returns null - * @return the result of the replace operation - */ - public String replace(final Object source) { - if (source == null) { - return null; - } - final StrBuilder buf = new StrBuilder().append(source); - substitute(buf, 0, buf.length()); - return buf.toString(); - } - - // ----------------------------------------------------------------------- - /** - * Replaces all the occurrences of variables within the given source buffer with - * their matching values from the resolver. The buffer is updated with the - * result. - * - * @param source the buffer to replace in, updated, null returns zero - * @return true if altered - */ - public boolean replaceIn(final StringBuffer source) { - if (source == null) { - return false; - } - return replaceIn(source, 0, source.length()); - } - - /** - * Replaces all the occurrences of variables within the given source buffer with - * their matching values from the resolver. The buffer is updated with the - * result. - *

- * Only the specified portion of the buffer will be processed. The rest of the - * buffer is not processed, but it is not deleted. - * - * @param source the buffer to replace in, updated, null returns zero - * @param offset the start offset within the array, must be valid - * @param length the length within the buffer to be processed, must be valid - * @return true if altered - */ - public boolean replaceIn(final StringBuffer source, final int offset, final int length) { - if (source == null) { - return false; - } - final StrBuilder buf = new StrBuilder(length).append(source, offset, length); - if (substitute(buf, 0, length) == false) { - return false; - } - source.replace(offset, offset + length, buf.toString()); - return true; - } - - // ----------------------------------------------------------------------- - /** - * Replaces all the occurrences of variables within the given source buffer with - * their matching values from the resolver. The buffer is updated with the - * result. - * - * @param source the buffer to replace in, updated, null returns zero - * @return true if altered - * @since 3.2 - */ - public boolean replaceIn(final StringBuilder source) { - if (source == null) { - return false; - } - return replaceIn(source, 0, source.length()); - } - - /** - * Replaces all the occurrences of variables within the given source builder - * with their matching values from the resolver. The builder is updated with the - * result. - *

- * Only the specified portion of the buffer will be processed. The rest of the - * buffer is not processed, but it is not deleted. - * - * @param source the buffer to replace in, updated, null returns zero - * @param offset the start offset within the array, must be valid - * @param length the length within the buffer to be processed, must be valid - * @return true if altered - * @since 3.2 - */ - public boolean replaceIn(final StringBuilder source, final int offset, final int length) { - if (source == null) { - return false; - } - final StrBuilder buf = new StrBuilder(length).append(source, offset, length); - if (substitute(buf, 0, length) == false) { - return false; - } - source.replace(offset, offset + length, buf.toString()); - return true; - } - - // ----------------------------------------------------------------------- - /** - * Replaces all the occurrences of variables within the given source builder - * with their matching values from the resolver. - * - * @param source the builder to replace in, updated, null returns zero - * @return true if altered - */ - public boolean replaceIn(final StrBuilder source) { - if (source == null) { - return false; - } - return substitute(source, 0, source.length()); - } - - /** - * Replaces all the occurrences of variables within the given source builder - * with their matching values from the resolver. - *

- * Only the specified portion of the builder will be processed. The rest of the - * builder is not processed, but it is not deleted. - * - * @param source the builder to replace in, null returns zero - * @param offset the start offset within the array, must be valid - * @param length the length within the builder to be processed, must be valid - * @return true if altered - */ - public boolean replaceIn(final StrBuilder source, final int offset, final int length) { - if (source == null) { - return false; - } - return substitute(source, offset, length); - } - - // ----------------------------------------------------------------------- - /** - * Internal method that substitutes the variables. - *

- * Most users of this class do not need to call this method. This method will be - * called automatically by another (public) method. - *

- * Writers of subclasses can override this method if they need access to the - * substitution process at the start or end. - * - * @param buf the string builder to substitute into, not null - * @param offset the start offset within the builder, must be valid - * @param length the length within the builder to be processed, must be valid - * @return true if altered - */ - protected boolean substitute(final StrBuilder buf, final int offset, final int length) { - return substitute(buf, offset, length, null) > 0; - } - - /** - * Recursive handler for multiple levels of interpolation. This is the main - * interpolation method, which resolves the values of all variable references - * contained in the passed in text. - * - * @param buf the string builder to substitute into, not null - * @param offset the start offset within the builder, must be valid - * @param length the length within the builder to be processed, must be - * valid - * @param priorVariables the stack keeping track of the replaced variables, may - * be null - * @return the length change that occurs, unless priorVariables is null when the - * int represents a boolean flag as to whether any change occurred. - */ - private int substitute(final StrBuilder buf, final int offset, final int length, List priorVariables) { - final StrMatcher pfxMatcher = getVariablePrefixMatcher(); - final StrMatcher suffMatcher = getVariableSuffixMatcher(); - final char escape = getEscapeChar(); - final StrMatcher valueDelimMatcher = getValueDelimiterMatcher(); - final boolean substitutionInVariablesEnabled = isEnableSubstitutionInVariables(); - - final boolean top = priorVariables == null; - boolean altered = false; - int lengthChange = 0; - char[] chars = buf.buffer; - int bufEnd = offset + length; - int pos = offset; - while (pos < bufEnd) { - final int startMatchLen = pfxMatcher.isMatch(chars, pos, offset, bufEnd); - if (startMatchLen == 0) { - pos++; - } else // found variable start marker - if (pos > offset && chars[pos - 1] == escape) { - // escaped - if (preserveEscapes) { - pos++; - continue; - } - buf.deleteCharAt(pos - 1); - chars = buf.buffer; // in case buffer was altered - lengthChange--; - altered = true; - bufEnd--; - } else { - // find suffix - final int startPos = pos; - pos += startMatchLen; - int endMatchLen = 0; - int nestedVarCount = 0; - while (pos < bufEnd) { - if (substitutionInVariablesEnabled - && (endMatchLen = pfxMatcher.isMatch(chars, pos, offset, bufEnd)) != 0) { - // found a nested variable start - nestedVarCount++; - pos += endMatchLen; - continue; - } - - endMatchLen = suffMatcher.isMatch(chars, pos, offset, bufEnd); - if (endMatchLen == 0) { - pos++; - } else { - // found variable end marker - if (nestedVarCount == 0) { - String varNameExpr = new String(chars, startPos + startMatchLen, - pos - startPos - startMatchLen); - if (substitutionInVariablesEnabled) { - final StrBuilder bufName = new StrBuilder(varNameExpr); - substitute(bufName, 0, bufName.length()); - varNameExpr = bufName.toString(); - } - pos += endMatchLen; - final int endPos = pos; - - String varName = varNameExpr; - String varDefaultValue = null; - - if (valueDelimMatcher != null) { - final char[] varNameExprChars = varNameExpr.toCharArray(); - int valueDelimiterMatchLen = 0; - for (int i = 0; i < varNameExprChars.length; i++) { - // if there's any nested variable when nested variable substitution disabled, - // then stop resolving name and default value. - if (!substitutionInVariablesEnabled && pfxMatcher.isMatch(varNameExprChars, i, i, - varNameExprChars.length) != 0) { - break; - } - if ((valueDelimiterMatchLen = valueDelimMatcher.isMatch(varNameExprChars, - i)) != 0) { - varName = varNameExpr.substring(0, i); - varDefaultValue = varNameExpr.substring(i + valueDelimiterMatchLen); - break; - } - } - } - - // on the first call initialize priorVariables - if (priorVariables == null) { - priorVariables = new ArrayList<>(); - priorVariables.add(new String(chars, offset, length)); - } - - // handle cyclic substitution - checkCyclicSubstitution(varName, priorVariables); - priorVariables.add(varName); - - // resolve the variable - String varValue = resolveVariable(varName, buf, startPos, endPos); - if (varValue == null) { - varValue = varDefaultValue; - } - if (varValue != null) { - // recursive replace - final int varLen = varValue.length(); - buf.replace(startPos, endPos, varValue); - altered = true; - int change = substitute(buf, startPos, varLen, priorVariables); - change = change + varLen - (endPos - startPos); - pos += change; - bufEnd += change; - lengthChange += change; - chars = buf.buffer; // in case buffer was - // altered - } - - // remove variable from the cyclic stack - priorVariables.remove(priorVariables.size() - 1); - break; - } - nestedVarCount--; - pos += endMatchLen; - } - } - } - } - if (top) { - return altered ? 1 : 0; - } - return lengthChange; - } - - /** - * Checks if the specified variable is already in the stack (list) of variables. - * - * @param varName the variable name to check - * @param priorVariables the list of prior variables - */ - private void checkCyclicSubstitution(final String varName, final List priorVariables) { - if (priorVariables.contains(varName) == false) { - return; - } - final StrBuilder buf = new StrBuilder(256); - buf.append("Infinite loop in property interpolation of "); - buf.append(priorVariables.remove(0)); - buf.append(": "); - buf.appendWithSeparators(priorVariables, "->"); - throw new IllegalStateException(buf.toString()); - } - - /** - * Internal method that resolves the value of a variable. - *

- * Most users of this class do not need to call this method. This method is - * called automatically by the substitution process. - *

- * Writers of subclasses can override this method if they need to alter how each - * substitution occurs. The method is passed the variable's name and must return - * the corresponding value. This implementation uses the - * {@link #getVariableResolver()} with the variable's name as the key. - * - * @param variableName the name of the variable, not null - * @param buf the buffer where the substitution is occurring, not null - * @param startPos the start position of the variable including the prefix, - * valid - * @param endPos the end position of the variable including the suffix, - * valid - * @return the variable's value or null if the variable is unknown - */ - protected String resolveVariable(final String variableName, final StrBuilder buf, final int startPos, - final int endPos) { - final StrLookup resolver = getVariableResolver(); - if (resolver == null) { - return null; - } - return resolver.lookup(variableName); - } - - // Escape - // ----------------------------------------------------------------------- - /** - * Returns the escape character. - * - * @return the character used for escaping variable references - */ - public char getEscapeChar() { - return this.escapeChar; - } - - /** - * Sets the escape character. If this character is placed before a variable - * reference in the source text, this variable will be ignored. - * - * @param escapeCharacter the escape character (0 for disabling escaping) - */ - public void setEscapeChar(final char escapeCharacter) { - this.escapeChar = escapeCharacter; - } - - // Prefix - // ----------------------------------------------------------------------- - /** - * Gets the variable prefix matcher currently in use. - *

- * The variable prefix is the character or characters that identify the start of - * a variable. This prefix is expressed in terms of a matcher allowing advanced - * prefix matches. - * - * @return the prefix matcher in use - */ - public StrMatcher getVariablePrefixMatcher() { - return prefixMatcher; - } - - /** - * Sets the variable prefix matcher currently in use. - *

- * The variable prefix is the character or characters that identify the start of - * a variable. This prefix is expressed in terms of a matcher allowing advanced - * prefix matches. - * - * @param prefixMatcher the prefix matcher to use, null ignored - * @return this, to enable chaining - * @throws IllegalArgumentException if the prefix matcher is null - */ - public StrSubstitutor setVariablePrefixMatcher(final StrMatcher prefixMatcher) { - if (prefixMatcher == null) { - throw new IllegalArgumentException("Variable prefix matcher must not be null."); - } - this.prefixMatcher = prefixMatcher; - return this; - } - - /** - * Sets the variable prefix to use. - *

- * The variable prefix is the character or characters that identify the start of - * a variable. This method allows a single character prefix to be easily set. - * - * @param prefix the prefix character to use - * @return this, to enable chaining - */ - public StrSubstitutor setVariablePrefix(final char prefix) { - return setVariablePrefixMatcher(StrMatcher.charMatcher(prefix)); - } - - /** - * Sets the variable prefix to use. - *

- * The variable prefix is the character or characters that identify the start of - * a variable. This method allows a string prefix to be easily set. - * - * @param prefix the prefix for variables, not null - * @return this, to enable chaining - * @throws IllegalArgumentException if the prefix is null - */ - public StrSubstitutor setVariablePrefix(final String prefix) { - if (prefix == null) { - throw new IllegalArgumentException("Variable prefix must not be null."); - } - return setVariablePrefixMatcher(StrMatcher.stringMatcher(prefix)); - } - - // Suffix - // ----------------------------------------------------------------------- - /** - * Gets the variable suffix matcher currently in use. - *

- * The variable suffix is the character or characters that identify the end of a - * variable. This suffix is expressed in terms of a matcher allowing advanced - * suffix matches. - * - * @return the suffix matcher in use - */ - public StrMatcher getVariableSuffixMatcher() { - return suffixMatcher; - } - - /** - * Sets the variable suffix matcher currently in use. - *

- * The variable suffix is the character or characters that identify the end of a - * variable. This suffix is expressed in terms of a matcher allowing advanced - * suffix matches. - * - * @param suffixMatcher the suffix matcher to use, null ignored - * @return this, to enable chaining - * @throws IllegalArgumentException if the suffix matcher is null - */ - public StrSubstitutor setVariableSuffixMatcher(final StrMatcher suffixMatcher) { - if (suffixMatcher == null) { - throw new IllegalArgumentException("Variable suffix matcher must not be null."); - } - this.suffixMatcher = suffixMatcher; - return this; - } - - /** - * Sets the variable suffix to use. - *

- * The variable suffix is the character or characters that identify the end of a - * variable. This method allows a single character suffix to be easily set. - * - * @param suffix the suffix character to use - * @return this, to enable chaining - */ - public StrSubstitutor setVariableSuffix(final char suffix) { - return setVariableSuffixMatcher(StrMatcher.charMatcher(suffix)); - } - - /** - * Sets the variable suffix to use. - *

- * The variable suffix is the character or characters that identify the end of a - * variable. This method allows a string suffix to be easily set. - * - * @param suffix the suffix for variables, not null - * @return this, to enable chaining - * @throws IllegalArgumentException if the suffix is null - */ - public StrSubstitutor setVariableSuffix(final String suffix) { - if (suffix == null) { - throw new IllegalArgumentException("Variable suffix must not be null."); - } - return setVariableSuffixMatcher(StrMatcher.stringMatcher(suffix)); - } - - // Variable Default Value Delimiter - // ----------------------------------------------------------------------- - /** - * Gets the variable default value delimiter matcher currently in use. - *

- * The variable default value delimiter is the character or characters that - * delimit the variable name and the variable default value. This delimiter is - * expressed in terms of a matcher allowing advanced variable default value - * delimiter matches. - *

- * If it returns null, then the variable default value resolution is disabled. - * - * @return the variable default value delimiter matcher in use, may be null - * @since 3.2 - */ - public StrMatcher getValueDelimiterMatcher() { - return valueDelimiterMatcher; - } - - /** - * Sets the variable default value delimiter matcher to use. - *

- * The variable default value delimiter is the character or characters that - * delimit the variable name and the variable default value. This delimiter is - * expressed in terms of a matcher allowing advanced variable default value - * delimiter matches. - *

- * If the {@code valueDelimiterMatcher} is null, then the variable default value - * resolution becomes disabled. - * - * @param valueDelimiterMatcher variable default value delimiter matcher to use, - * may be null - * @return this, to enable chaining - * @since 3.2 - */ - public StrSubstitutor setValueDelimiterMatcher(final StrMatcher valueDelimiterMatcher) { - this.valueDelimiterMatcher = valueDelimiterMatcher; - return this; - } - - /** - * Sets the variable default value delimiter to use. - *

- * The variable default value delimiter is the character or characters that - * delimit the variable name and the variable default value. This method allows - * a single character variable default value delimiter to be easily set. - * - * @param valueDelimiter the variable default value delimiter character to use - * @return this, to enable chaining - * @since 3.2 - */ - public StrSubstitutor setValueDelimiter(final char valueDelimiter) { - return setValueDelimiterMatcher(StrMatcher.charMatcher(valueDelimiter)); - } - - /** - * Sets the variable default value delimiter to use. - *

- * The variable default value delimiter is the character or characters that - * delimit the variable name and the variable default value. This method allows - * a string variable default value delimiter to be easily set. - *

- * If the {@code valueDelimiter} is null or empty string, then the variable - * default value resolution becomes disabled. - * - * @param valueDelimiter the variable default value delimiter string to use, may - * be null or empty - * @return this, to enable chaining - * @since 3.2 - */ - public StrSubstitutor setValueDelimiter(final String valueDelimiter) { - if (StringUtils.isEmpty(valueDelimiter)) { - setValueDelimiterMatcher(null); - return this; - } - return setValueDelimiterMatcher(StrMatcher.stringMatcher(valueDelimiter)); - } - - // Resolver - // ----------------------------------------------------------------------- - /** - * Gets the VariableResolver that is used to lookup variables. - * - * @return the VariableResolver - */ - public StrLookup getVariableResolver() { - return this.variableResolver; - } - - /** - * Sets the VariableResolver that is used to lookup variables. - * - * @param variableResolver the VariableResolver - */ - public void setVariableResolver(final StrLookup variableResolver) { - this.variableResolver = variableResolver; - } - - // Substitution support in variable names - // ----------------------------------------------------------------------- - /** - * Returns a flag whether substitution is done in variable names. - * - * @return the substitution in variable names flag - * @since 3.0 - */ - public boolean isEnableSubstitutionInVariables() { - return enableSubstitutionInVariables; - } - - /** - * Sets a flag whether substitution is done in variable names. If set to - * true, the names of variables can contain other variables which are - * processed first before the original variable is evaluated, e.g. - * {@code ${jre-${java.version}}}. The default value is false. - * - * @param enableSubstitutionInVariables the new value of the flag - * @since 3.0 - */ - public void setEnableSubstitutionInVariables(final boolean enableSubstitutionInVariables) { - this.enableSubstitutionInVariables = enableSubstitutionInVariables; - } - - /** - * Returns the flag controlling whether escapes are preserved during - * substitution. - * - * @return the preserve escape flag - * @since 3.5 - */ - public boolean isPreserveEscapes() { - return preserveEscapes; - } - - /** - * Sets a flag controlling whether escapes are preserved during substitution. If - * set to true, the escape character is retained during substitution - * (e.g. {@code $${this-is-escaped}} remains {@code $${this-is-escaped}}). If - * set to false, the escape character is removed during substitution - * (e.g. {@code $${this-is-escaped}} becomes {@code ${this-is-escaped}}). The - * default value is false - * - * @param preserveEscapes true if escapes are to be preserved - * @since 3.5 - */ - public void setPreserveEscapes(final boolean preserveEscapes) { - this.preserveEscapes = preserveEscapes; - } -} diff --git a/src/main/java/org/apache/commons/lang3/text/StrTokenizer.java b/src/main/java/org/apache/commons/lang3/text/StrTokenizer.java deleted file mode 100755 index 04785568..00000000 --- a/src/main/java/org/apache/commons/lang3/text/StrTokenizer.java +++ /dev/null @@ -1,1126 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.text; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.ListIterator; -import java.util.NoSuchElementException; - -import org.apache.commons.lang3.StringUtils; - -/** - * Tokenizes a string based on delimiters (separators) and supporting quoting - * and ignored character concepts. - *

- * This class can split a String into many smaller strings. It aims to do a - * similar job to {@link java.util.StringTokenizer StringTokenizer}, however it - * offers much more control and flexibility including implementing the - * {@code ListIterator} interface. By default, it is set up like - * {@code StringTokenizer}. - *

- * The input String is split into a number of tokens. Each token is - * separated from the next String by a delimiter. One or more delimiter - * characters must be specified. - *

- * Each token may be surrounded by quotes. The quote matcher specifies - * the quote character(s). A quote may be escaped within a quoted section by - * duplicating itself. - *

- * Between each token and the delimiter are potentially characters that need - * trimming. The trimmer matcher specifies these characters. One usage - * might be to trim whitespace characters. - *

- * At any point outside the quotes there might potentially be invalid - * characters. The ignored matcher specifies these characters to be - * removed. One usage might be to remove new line characters. - *

- * Empty tokens may be removed or returned as null. - * - *

- * "a,b,c"         - Three tokens "a","b","c"   (comma delimiter)
- * " a, b , c "    - Three tokens "a","b","c"   (default CSV processing trims whitespace)
- * "a, ", b ,", c" - Three tokens "a, " , " b ", ", c" (quoted text untouched)
- * 
- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
StrTokenizer properties and options
PropertyTypeDefault
delimCharSetMatcher{ \t\n\r\f}
quoteNoneMatcher{}
ignoreNoneMatcher{}
emptyTokenAsNullbooleanfalse
ignoreEmptyTokensbooleantrue
- * - * @since 2.2 - * @!deprecated as of 3.6, use commons-text - * StringTokenizer instead - */ -//@Deprecated -public class StrTokenizer implements ListIterator, Cloneable { - - private static final StrTokenizer CSV_TOKENIZER_PROTOTYPE; - private static final StrTokenizer TSV_TOKENIZER_PROTOTYPE; - static { - CSV_TOKENIZER_PROTOTYPE = new StrTokenizer(); - CSV_TOKENIZER_PROTOTYPE.setDelimiterMatcher(StrMatcher.commaMatcher()); - CSV_TOKENIZER_PROTOTYPE.setQuoteMatcher(StrMatcher.doubleQuoteMatcher()); - CSV_TOKENIZER_PROTOTYPE.setIgnoredMatcher(StrMatcher.noneMatcher()); - CSV_TOKENIZER_PROTOTYPE.setTrimmerMatcher(StrMatcher.trimMatcher()); - CSV_TOKENIZER_PROTOTYPE.setEmptyTokenAsNull(false); - CSV_TOKENIZER_PROTOTYPE.setIgnoreEmptyTokens(false); - - TSV_TOKENIZER_PROTOTYPE = new StrTokenizer(); - TSV_TOKENIZER_PROTOTYPE.setDelimiterMatcher(StrMatcher.tabMatcher()); - TSV_TOKENIZER_PROTOTYPE.setQuoteMatcher(StrMatcher.doubleQuoteMatcher()); - TSV_TOKENIZER_PROTOTYPE.setIgnoredMatcher(StrMatcher.noneMatcher()); - TSV_TOKENIZER_PROTOTYPE.setTrimmerMatcher(StrMatcher.trimMatcher()); - TSV_TOKENIZER_PROTOTYPE.setEmptyTokenAsNull(false); - TSV_TOKENIZER_PROTOTYPE.setIgnoreEmptyTokens(false); - } - - /** The text to work on. */ - private char[] chars; - /** The parsed tokens */ - private String[] tokens; - /** The current iteration position */ - private int tokenPos; - - /** The delimiter matcher */ - private StrMatcher delimMatcher = StrMatcher.splitMatcher(); - /** The quote matcher */ - private StrMatcher quoteMatcher = StrMatcher.noneMatcher(); - /** The ignored matcher */ - private StrMatcher ignoredMatcher = StrMatcher.noneMatcher(); - /** The trimmer matcher */ - private StrMatcher trimmerMatcher = StrMatcher.noneMatcher(); - - /** Whether to return empty tokens as null */ - private boolean emptyAsNull; - /** Whether to ignore empty tokens */ - private boolean ignoreEmptyTokens = true; - - // ----------------------------------------------------------------------- - - /** - * Returns a clone of {@code CSV_TOKENIZER_PROTOTYPE}. - * - * @return a clone of {@code CSV_TOKENIZER_PROTOTYPE}. - */ - private static StrTokenizer getCSVClone() { - return (StrTokenizer) CSV_TOKENIZER_PROTOTYPE.clone(); - } - - /** - * Gets a new tokenizer instance which parses Comma Separated Value strings - * initializing it with the given input. The default for CSV processing will be - * trim whitespace from both ends (which can be overridden with the setTrimmer - * method). - *

- * You must call a "reset" method to set the string which you want to parse. - * - * @return a new tokenizer instance which parses Comma Separated Value strings - */ - public static StrTokenizer getCSVInstance() { - return getCSVClone(); - } - - /** - * Gets a new tokenizer instance which parses Comma Separated Value strings - * initializing it with the given input. The default for CSV processing will be - * trim whitespace from both ends (which can be overridden with the setTrimmer - * method). - * - * @param input the text to parse - * @return a new tokenizer instance which parses Comma Separated Value strings - */ - public static StrTokenizer getCSVInstance(final String input) { - final StrTokenizer tok = getCSVClone(); - tok.reset(input); - return tok; - } - - /** - * Gets a new tokenizer instance which parses Comma Separated Value strings - * initializing it with the given input. The default for CSV processing will be - * trim whitespace from both ends (which can be overridden with the setTrimmer - * method). - * - * @param input the text to parse - * @return a new tokenizer instance which parses Comma Separated Value strings - */ - public static StrTokenizer getCSVInstance(final char[] input) { - final StrTokenizer tok = getCSVClone(); - tok.reset(input); - return tok; - } - - /** - * Returns a clone of {@code TSV_TOKENIZER_PROTOTYPE}. - * - * @return a clone of {@code TSV_TOKENIZER_PROTOTYPE}. - */ - private static StrTokenizer getTSVClone() { - return (StrTokenizer) TSV_TOKENIZER_PROTOTYPE.clone(); - } - - /** - * Gets a new tokenizer instance which parses Tab Separated Value strings. The - * default for CSV processing will be trim whitespace from both ends (which can - * be overridden with the setTrimmer method). - *

- * You must call a "reset" method to set the string which you want to parse. - * - * @return a new tokenizer instance which parses Tab Separated Value strings. - */ - public static StrTokenizer getTSVInstance() { - return getTSVClone(); - } - - /** - * Gets a new tokenizer instance which parses Tab Separated Value strings. The - * default for CSV processing will be trim whitespace from both ends (which can - * be overridden with the setTrimmer method). - * - * @param input the string to parse - * @return a new tokenizer instance which parses Tab Separated Value strings. - */ - public static StrTokenizer getTSVInstance(final String input) { - final StrTokenizer tok = getTSVClone(); - tok.reset(input); - return tok; - } - - /** - * Gets a new tokenizer instance which parses Tab Separated Value strings. The - * default for CSV processing will be trim whitespace from both ends (which can - * be overridden with the setTrimmer method). - * - * @param input the string to parse - * @return a new tokenizer instance which parses Tab Separated Value strings. - */ - public static StrTokenizer getTSVInstance(final char[] input) { - final StrTokenizer tok = getTSVClone(); - tok.reset(input); - return tok; - } - - // ----------------------------------------------------------------------- - /** - * Constructs a tokenizer splitting on space, tab, newline and formfeed as per - * StringTokenizer, but with no text to tokenize. - *

- * This constructor is normally used with {@link #reset(String)}. - */ - public StrTokenizer() { - this.chars = null; - } - - /** - * Constructs a tokenizer splitting on space, tab, newline and formfeed as per - * StringTokenizer. - * - * @param input the string which is to be parsed - */ - public StrTokenizer(final String input) { - if (input != null) { - chars = input.toCharArray(); - } else { - chars = null; - } - } - - /** - * Constructs a tokenizer splitting on the specified delimiter character. - * - * @param input the string which is to be parsed - * @param delim the field delimiter character - */ - public StrTokenizer(final String input, final char delim) { - this(input); - setDelimiterChar(delim); - } - - /** - * Constructs a tokenizer splitting on the specified delimiter string. - * - * @param input the string which is to be parsed - * @param delim the field delimiter string - */ - public StrTokenizer(final String input, final String delim) { - this(input); - setDelimiterString(delim); - } - - /** - * Constructs a tokenizer splitting using the specified delimiter matcher. - * - * @param input the string which is to be parsed - * @param delim the field delimiter matcher - */ - public StrTokenizer(final String input, final StrMatcher delim) { - this(input); - setDelimiterMatcher(delim); - } - - /** - * Constructs a tokenizer splitting on the specified delimiter character and - * handling quotes using the specified quote character. - * - * @param input the string which is to be parsed - * @param delim the field delimiter character - * @param quote the field quoted string character - */ - public StrTokenizer(final String input, final char delim, final char quote) { - this(input, delim); - setQuoteChar(quote); - } - - /** - * Constructs a tokenizer splitting using the specified delimiter matcher and - * handling quotes using the specified quote matcher. - * - * @param input the string which is to be parsed - * @param delim the field delimiter matcher - * @param quote the field quoted string matcher - */ - public StrTokenizer(final String input, final StrMatcher delim, final StrMatcher quote) { - this(input, delim); - setQuoteMatcher(quote); - } - - /** - * Constructs a tokenizer splitting on space, tab, newline and formfeed as per - * StringTokenizer. - * - * @param input the string which is to be parsed, not cloned - */ - public StrTokenizer(final char[] input) { - this.chars = new String(input).toCharArray(); - } - - /** - * Constructs a tokenizer splitting on the specified character. - * - * @param input the string which is to be parsed, not cloned - * @param delim the field delimiter character - */ - public StrTokenizer(final char[] input, final char delim) { - this(input); - setDelimiterChar(delim); - } - - /** - * Constructs a tokenizer splitting on the specified string. - * - * @param input the string which is to be parsed, not cloned - * @param delim the field delimiter string - */ - public StrTokenizer(final char[] input, final String delim) { - this(input); - setDelimiterString(delim); - } - - /** - * Constructs a tokenizer splitting using the specified delimiter matcher. - * - * @param input the string which is to be parsed, not cloned - * @param delim the field delimiter matcher - */ - public StrTokenizer(final char[] input, final StrMatcher delim) { - this(input); - setDelimiterMatcher(delim); - } - - /** - * Constructs a tokenizer splitting on the specified delimiter character and - * handling quotes using the specified quote character. - * - * @param input the string which is to be parsed, not cloned - * @param delim the field delimiter character - * @param quote the field quoted string character - */ - public StrTokenizer(final char[] input, final char delim, final char quote) { - this(input, delim); - setQuoteChar(quote); - } - - /** - * Constructs a tokenizer splitting using the specified delimiter matcher and - * handling quotes using the specified quote matcher. - * - * @param input the string which is to be parsed, not cloned - * @param delim the field delimiter character - * @param quote the field quoted string character - */ - public StrTokenizer(final char[] input, final StrMatcher delim, final StrMatcher quote) { - this(input, delim); - setQuoteMatcher(quote); - } - - // API - // ----------------------------------------------------------------------- - /** - * Gets the number of tokens found in the String. - * - * @return the number of matched tokens - */ - public int size() { - checkTokenized(); - return tokens.length; - } - - /** - * Gets the next token from the String. Equivalent to {@link #next()} except it - * returns null rather than throwing {@link NoSuchElementException} when no - * tokens remain. - * - * @return the next sequential token, or null when no more tokens are found - */ - public String nextToken() { - if (hasNext()) { - return tokens[tokenPos++]; - } - return null; - } - - /** - * Gets the previous token from the String. - * - * @return the previous sequential token, or null when no more tokens are found - */ - public String previousToken() { - if (hasPrevious()) { - return tokens[--tokenPos]; - } - return null; - } - - /** - * Gets a copy of the full token list as an independent modifiable array. - * - * @return the tokens as a String array - */ - public String[] getTokenArray() { - checkTokenized(); - return tokens.clone(); - } - - /** - * Gets a copy of the full token list as an independent modifiable list. - * - * @return the tokens as a String array - */ - public List getTokenList() { - checkTokenized(); - final List list = new ArrayList<>(tokens.length); - list.addAll(Arrays.asList(tokens)); - return list; - } - - /** - * Resets this tokenizer, forgetting all parsing and iteration already - * completed. - *

- * This method allows the same tokenizer to be reused for the same String. - * - * @return this, to enable chaining - */ - public StrTokenizer reset() { - tokenPos = 0; - tokens = null; - return this; - } - - /** - * Reset this tokenizer, giving it a new input string to parse. In this manner - * you can re-use a tokenizer with the same settings on multiple input lines. - * - * @param input the new string to tokenize, null sets no text to parse - * @return this, to enable chaining - */ - public StrTokenizer reset(final String input) { - reset(); - if (input != null) { - this.chars = input.toCharArray(); - } else { - this.chars = null; - } - return this; - } - - /** - * Reset this tokenizer, giving it a new input string to parse. In this manner - * you can re-use a tokenizer with the same settings on multiple input lines. - * - * @param input the new character array to tokenize, not cloned, null sets no - * text to parse - * @return this, to enable chaining - */ - public StrTokenizer reset(final char[] input) { - reset(); - this.chars = new String(input).toCharArray(); - return this; - } - - // ListIterator - // ----------------------------------------------------------------------- - /** - * Checks whether there are any more tokens. - * - * @return true if there are more tokens - */ - @Override - public boolean hasNext() { - checkTokenized(); - return tokenPos < tokens.length; - } - - /** - * Gets the next token. - * - * @return the next String token - * @throws NoSuchElementException if there are no more elements - */ - @Override - public String next() { - if (hasNext()) { - return tokens[tokenPos++]; - } - throw new NoSuchElementException(); - } - - /** - * Gets the index of the next token to return. - * - * @return the next token index - */ - @Override - public int nextIndex() { - return tokenPos; - } - - /** - * Checks whether there are any previous tokens that can be iterated to. - * - * @return true if there are previous tokens - */ - @Override - public boolean hasPrevious() { - checkTokenized(); - return tokenPos > 0; - } - - /** - * Gets the token previous to the last returned token. - * - * @return the previous token - */ - @Override - public String previous() { - if (hasPrevious()) { - return tokens[--tokenPos]; - } - throw new NoSuchElementException(); - } - - /** - * Gets the index of the previous token. - * - * @return the previous token index - */ - @Override - public int previousIndex() { - return tokenPos - 1; - } - - /** - * Unsupported ListIterator operation. - * - * @throws UnsupportedOperationException always - */ - @Override - public void remove() { - throw new UnsupportedOperationException("remove() is unsupported"); - } - - /** - * Unsupported ListIterator operation. - * - * @param obj this parameter ignored. - * @throws UnsupportedOperationException always - */ - @Override - public void set(final String obj) { - throw new UnsupportedOperationException("set() is unsupported"); - } - - /** - * Unsupported ListIterator operation. - * - * @param obj this parameter ignored. - * @throws UnsupportedOperationException always - */ - @Override - public void add(final String obj) { - throw new UnsupportedOperationException("add() is unsupported"); - } - - // Implementation - // ----------------------------------------------------------------------- - /** - * Checks if tokenization has been done, and if not then do it. - */ - private void checkTokenized() { - if (tokens == null) { - if (chars == null) { - // still call tokenize as subclass may do some work - final List split = tokenize(null, 0, 0); - tokens = split.toArray(new String[0]); - } else { - final List split = tokenize(chars, 0, chars.length); - tokens = split.toArray(new String[0]); - } - } - } - - /** - * Internal method to performs the tokenization. - *

- * Most users of this class do not need to call this method. This method will be - * called automatically by other (public) methods when required. - *

- * This method exists to allow subclasses to add code before or after the - * tokenization. For example, a subclass could alter the character array, offset - * or count to be parsed, or call the tokenizer multiple times on multiple - * strings. It is also be possible to filter the results. - *

- * {@code StrTokenizer} will always pass a zero offset and a count equal to the - * length of the array to this method, however a subclass may pass other values, - * or even an entirely different array. - * - * @param srcChars the character array being tokenized, may be null - * @param offset the start position within the character array, must be valid - * @param count the number of characters to tokenize, must be valid - * @return the modifiable list of String tokens, unmodifiable if null array or - * zero count - */ - protected List tokenize(final char[] srcChars, final int offset, final int count) { - if (srcChars == null || count == 0) { - return Collections.emptyList(); - } - final StrBuilder buf = new StrBuilder(); - final List tokenList = new ArrayList<>(); - int pos = offset; - - // loop around the entire buffer - while (pos >= 0 && pos < count) { - // find next token - pos = readNextToken(srcChars, pos, count, buf, tokenList); - - // handle case where end of string is a delimiter - if (pos >= count) { - addToken(tokenList, StringUtils.EMPTY); - } - } - return tokenList; - } - - /** - * Adds a token to a list, paying attention to the parameters we've set. - * - * @param list the list to add to - * @param tok the token to add - */ - private void addToken(final List list, String tok) { - if (StringUtils.isEmpty(tok)) { - if (isIgnoreEmptyTokens()) { - return; - } - if (isEmptyTokenAsNull()) { - tok = null; - } - } - list.add(tok); - } - - /** - * Reads character by character through the String to get the next token. - * - * @param srcChars the character array being tokenized - * @param start the first character of field - * @param len the length of the character array being tokenized - * @param workArea a temporary work area - * @param tokenList the list of parsed tokens - * @return the starting position of the next field (the character immediately - * after the delimiter), or -1 if end of string found - */ - private int readNextToken(final char[] srcChars, int start, final int len, final StrBuilder workArea, - final List tokenList) { - // skip all leading whitespace, unless it is the - // field delimiter or the quote character - while (start < len) { - final int removeLen = Math.max(getIgnoredMatcher().isMatch(srcChars, start, start, len), - getTrimmerMatcher().isMatch(srcChars, start, start, len)); - if (removeLen == 0 || getDelimiterMatcher().isMatch(srcChars, start, start, len) > 0 - || getQuoteMatcher().isMatch(srcChars, start, start, len) > 0) { - break; - } - start += removeLen; - } - - // handle reaching end - if (start >= len) { - addToken(tokenList, StringUtils.EMPTY); - return -1; - } - - // handle empty token - final int delimLen = getDelimiterMatcher().isMatch(srcChars, start, start, len); - if (delimLen > 0) { - addToken(tokenList, StringUtils.EMPTY); - return start + delimLen; - } - - // handle found token - final int quoteLen = getQuoteMatcher().isMatch(srcChars, start, start, len); - if (quoteLen > 0) { - return readWithQuotes(srcChars, start + quoteLen, len, workArea, tokenList, start, quoteLen); - } - return readWithQuotes(srcChars, start, len, workArea, tokenList, 0, 0); - } - - /** - * Reads a possibly quoted string token. - * - * @param srcChars the character array being tokenized - * @param start the first character of field - * @param len the length of the character array being tokenized - * @param workArea a temporary work area - * @param tokenList the list of parsed tokens - * @param quoteStart the start position of the matched quote, 0 if no quoting - * @param quoteLen the length of the matched quote, 0 if no quoting - * @return the starting position of the next field (the character immediately - * after the delimiter, or if end of string found, then the length of - * string - */ - private int readWithQuotes(final char[] srcChars, final int start, final int len, final StrBuilder workArea, - final List tokenList, final int quoteStart, final int quoteLen) { - // Loop until we've found the end of the quoted - // string or the end of the input - workArea.clear(); - int pos = start; - boolean quoting = quoteLen > 0; - int trimStart = 0; - - while (pos < len) { - // quoting mode can occur several times throughout a string - // we must switch between quoting and non-quoting until we - // encounter a non-quoted delimiter, or end of string - if (quoting) { - // In quoting mode - - // If we've found a quote character, see if it's - // followed by a second quote. If so, then we need - // to actually put the quote character into the token - // rather than end the token. - if (isQuote(srcChars, pos, len, quoteStart, quoteLen)) { - if (isQuote(srcChars, pos + quoteLen, len, quoteStart, quoteLen)) { - // matched pair of quotes, thus an escaped quote - workArea.append(srcChars, pos, quoteLen); - pos += quoteLen * 2; - trimStart = workArea.size(); - continue; - } - - // end of quoting - quoting = false; - pos += quoteLen; - continue; - } - - // copy regular character from inside quotes - workArea.append(srcChars[pos++]); - trimStart = workArea.size(); - - } else { - // Not in quoting mode - - // check for delimiter, and thus end of token - final int delimLen = getDelimiterMatcher().isMatch(srcChars, pos, start, len); - if (delimLen > 0) { - // return condition when end of token found - addToken(tokenList, workArea.substring(0, trimStart)); - return pos + delimLen; - } - - // check for quote, and thus back into quoting mode - if (quoteLen > 0 && isQuote(srcChars, pos, len, quoteStart, quoteLen)) { - quoting = true; - pos += quoteLen; - continue; - } - - // check for ignored (outside quotes), and ignore - final int ignoredLen = getIgnoredMatcher().isMatch(srcChars, pos, start, len); - if (ignoredLen > 0) { - pos += ignoredLen; - continue; - } - - // check for trimmed character - // don't yet know if its at the end, so copy to workArea - // use trimStart to keep track of trim at the end - final int trimmedLen = getTrimmerMatcher().isMatch(srcChars, pos, start, len); - if (trimmedLen > 0) { - workArea.append(srcChars, pos, trimmedLen); - pos += trimmedLen; - continue; - } - - // copy regular character from outside quotes - workArea.append(srcChars[pos++]); - trimStart = workArea.size(); - } - } - - // return condition when end of string found - addToken(tokenList, workArea.substring(0, trimStart)); - return -1; - } - - /** - * Checks if the characters at the index specified match the quote already - * matched in readNextToken(). - * - * @param srcChars the character array being tokenized - * @param pos the position to check for a quote - * @param len the length of the character array being tokenized - * @param quoteStart the start position of the matched quote, 0 if no quoting - * @param quoteLen the length of the matched quote, 0 if no quoting - * @return true if a quote is matched - */ - private boolean isQuote(final char[] srcChars, final int pos, final int len, final int quoteStart, - final int quoteLen) { - for (int i = 0; i < quoteLen; i++) { - if (pos + i >= len || srcChars[pos + i] != srcChars[quoteStart + i]) { - return false; - } - } - return true; - } - - // Delimiter - // ----------------------------------------------------------------------- - /** - * Gets the field delimiter matcher. - * - * @return the delimiter matcher in use - */ - public StrMatcher getDelimiterMatcher() { - return this.delimMatcher; - } - - /** - * Sets the field delimiter matcher. - *

- * The delimiter is used to separate one token from another. - * - * @param delim the delimiter matcher to use - * @return this, to enable chaining - */ - public StrTokenizer setDelimiterMatcher(final StrMatcher delim) { - if (delim == null) { - this.delimMatcher = StrMatcher.noneMatcher(); - } else { - this.delimMatcher = delim; - } - return this; - } - - /** - * Sets the field delimiter character. - * - * @param delim the delimiter character to use - * @return this, to enable chaining - */ - public StrTokenizer setDelimiterChar(final char delim) { - return setDelimiterMatcher(StrMatcher.charMatcher(delim)); - } - - /** - * Sets the field delimiter string. - * - * @param delim the delimiter string to use - * @return this, to enable chaining - */ - public StrTokenizer setDelimiterString(final String delim) { - return setDelimiterMatcher(StrMatcher.stringMatcher(delim)); - } - - // Quote - // ----------------------------------------------------------------------- - /** - * Gets the quote matcher currently in use. - *

- * The quote character is used to wrap data between the tokens. This enables - * delimiters to be entered as data. The default value is '"' (double quote). - * - * @return the quote matcher in use - */ - public StrMatcher getQuoteMatcher() { - return quoteMatcher; - } - - /** - * Set the quote matcher to use. - *

- * The quote character is used to wrap data between the tokens. This enables - * delimiters to be entered as data. - * - * @param quote the quote matcher to use, null ignored - * @return this, to enable chaining - */ - public StrTokenizer setQuoteMatcher(final StrMatcher quote) { - if (quote != null) { - this.quoteMatcher = quote; - } - return this; - } - - /** - * Sets the quote character to use. - *

- * The quote character is used to wrap data between the tokens. This enables - * delimiters to be entered as data. - * - * @param quote the quote character to use - * @return this, to enable chaining - */ - public StrTokenizer setQuoteChar(final char quote) { - return setQuoteMatcher(StrMatcher.charMatcher(quote)); - } - - // Ignored - // ----------------------------------------------------------------------- - /** - * Gets the ignored character matcher. - *

- * These characters are ignored when parsing the String, unless they are within - * a quoted region. The default value is not to ignore anything. - * - * @return the ignored matcher in use - */ - public StrMatcher getIgnoredMatcher() { - return ignoredMatcher; - } - - /** - * Set the matcher for characters to ignore. - *

- * These characters are ignored when parsing the String, unless they are within - * a quoted region. - * - * @param ignored the ignored matcher to use, null ignored - * @return this, to enable chaining - */ - public StrTokenizer setIgnoredMatcher(final StrMatcher ignored) { - if (ignored != null) { - this.ignoredMatcher = ignored; - } - return this; - } - - /** - * Set the character to ignore. - *

- * This character is ignored when parsing the String, unless it is within a - * quoted region. - * - * @param ignored the ignored character to use - * @return this, to enable chaining - */ - public StrTokenizer setIgnoredChar(final char ignored) { - return setIgnoredMatcher(StrMatcher.charMatcher(ignored)); - } - - // Trimmer - // ----------------------------------------------------------------------- - /** - * Gets the trimmer character matcher. - *

- * These characters are trimmed off on each side of the delimiter until the - * token or quote is found. The default value is not to trim anything. - * - * @return the trimmer matcher in use - */ - public StrMatcher getTrimmerMatcher() { - return trimmerMatcher; - } - - /** - * Sets the matcher for characters to trim. - *

- * These characters are trimmed off on each side of the delimiter until the - * token or quote is found. - * - * @param trimmer the trimmer matcher to use, null ignored - * @return this, to enable chaining - */ - public StrTokenizer setTrimmerMatcher(final StrMatcher trimmer) { - if (trimmer != null) { - this.trimmerMatcher = trimmer; - } - return this; - } - - // ----------------------------------------------------------------------- - /** - * Gets whether the tokenizer currently returns empty tokens as null. The - * default for this property is false. - * - * @return true if empty tokens are returned as null - */ - public boolean isEmptyTokenAsNull() { - return this.emptyAsNull; - } - - /** - * Sets whether the tokenizer should return empty tokens as null. The default - * for this property is false. - * - * @param emptyAsNull whether empty tokens are returned as null - * @return this, to enable chaining - */ - public StrTokenizer setEmptyTokenAsNull(final boolean emptyAsNull) { - this.emptyAsNull = emptyAsNull; - return this; - } - - // ----------------------------------------------------------------------- - /** - * Gets whether the tokenizer currently ignores empty tokens. The default for - * this property is true. - * - * @return true if empty tokens are not returned - */ - public boolean isIgnoreEmptyTokens() { - return ignoreEmptyTokens; - } - - /** - * Sets whether the tokenizer should ignore and not return empty tokens. The - * default for this property is true. - * - * @param ignoreEmptyTokens whether empty tokens are not returned - * @return this, to enable chaining - */ - public StrTokenizer setIgnoreEmptyTokens(final boolean ignoreEmptyTokens) { - this.ignoreEmptyTokens = ignoreEmptyTokens; - return this; - } - - // ----------------------------------------------------------------------- - /** - * Gets the String content that the tokenizer is parsing. - * - * @return the string content being parsed - */ - public String getContent() { - if (chars == null) { - return null; - } - return new String(chars); - } - - // ----------------------------------------------------------------------- - /** - * Creates a new instance of this Tokenizer. The new instance is reset so that - * it will be at the start of the token list. If a - * {@link CloneNotSupportedException} is caught, return {@code null}. - * - * @return a new instance of this Tokenizer which has been reset. - */ - @Override - public Object clone() { - try { - return cloneReset(); - } catch (final CloneNotSupportedException ex) { - return null; - } - } - - /** - * Creates a new instance of this Tokenizer. The new instance is reset so that - * it will be at the start of the token list. - * - * @return a new instance of this Tokenizer which has been reset. - * @throws CloneNotSupportedException if there is a problem cloning - */ - Object cloneReset() throws CloneNotSupportedException { - // this method exists to enable 100% test coverage - final StrTokenizer cloned = (StrTokenizer) super.clone(); - if (cloned.chars != null) { - cloned.chars = cloned.chars.clone(); - } - cloned.reset(); - return cloned; - } - - // ----------------------------------------------------------------------- - /** - * Gets the String content that the tokenizer is parsing. - * - * @return the string content being parsed - */ - @Override - public String toString() { - if (tokens == null) { - return "StrTokenizer[not tokenized yet]"; - } - return "StrTokenizer" + getTokenList(); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/text/WordUtils.java b/src/main/java/org/apache/commons/lang3/text/WordUtils.java deleted file mode 100755 index b8e834e3..00000000 --- a/src/main/java/org/apache/commons/lang3/text/WordUtils.java +++ /dev/null @@ -1,833 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.text; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.commons.lang3.StringUtils; - -/** - *

- * Operations on Strings that contain words. - *

- * - *

- * This class tries to handle {@code null} input gracefully. An exception will - * not be thrown for a {@code null} input. Each method documents its behavior in - * more detail. - *

- * - * @since 2.0 - * @!deprecated as of 3.6, use commons-text - * WordUtils instead - */ -//@Deprecated -public class WordUtils { - - /** - *

- * {@code WordUtils} instances should NOT be constructed in standard - * programming. Instead, the class should be used as - * {@code WordUtils.wrap("foo bar", 20);}. - *

- * - *

- * This constructor is public to permit tools that require a JavaBean instance - * to operate. - *

- */ - public WordUtils() { - } - - // Wrapping - // -------------------------------------------------------------------------- - /** - *

- * Wraps a single line of text, identifying words by {@code ' '}. - *

- * - *

- * New lines will be separated by the system property line separator. Very long - * words, such as URLs will not be wrapped. - *

- * - *

- * Leading spaces on a new line are stripped. Trailing spaces are not stripped. - *

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Examples
inputwrapLengthresult
null*null
""*""
"Here is one line of text that is going to be wrapped after 20 - * columns."20"Here is one line of\ntext that is going\nto be wrapped after\n20 - * columns."
"Click here to jump to the commons website - - * https://commons.apache.org"20"Click here to jump\nto the commons\nwebsite - * -\nhttps://commons.apache.org"
"Click here, https://commons.apache.org, to jump to the commons - * website"20"Click here,\nhttps://commons.apache.org,\nto jump to the\ncommons - * website"
- * - * (assuming that '\n' is the systems line separator) - * - * @param str the String to be word wrapped, may be null - * @param wrapLength the column to wrap the words at, less than 1 is treated as - * 1 - * @return a line with newlines inserted, {@code null} if null input - */ - public static String wrap(final String str, final int wrapLength) { - return wrap(str, wrapLength, null, false); - } - - /** - *

- * Wraps a single line of text, identifying words by {@code ' '}. - *

- * - *

- * Leading spaces on a new line are stripped. Trailing spaces are not stripped. - *

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Examples
inputwrapLengthnewLineStringwrapLongWordsresult
null**true/falsenull
""**true/false""
"Here is one line of text that is going to be wrapped after 20 - * columns."20"\n"true/false"Here is one line of\ntext that is going\nto be wrapped after\n20 - * columns."
"Here is one line of text that is going to be wrapped after 20 - * columns."20"<br />"true/false"Here is one line of<br />text that is going<br />to be - * wrapped after<br />20 columns."
"Here is one line of text that is going to be wrapped after 20 - * columns."20nulltrue/false"Here is one line of" + systemNewLine + "text that is going" + - * systemNewLine + "to be wrapped after" + systemNewLine + "20 columns."
"Click here to jump to the commons website - - * https://commons.apache.org"20"\n"false"Click here to jump\nto the commons\nwebsite - * -\nhttps://commons.apache.org"
"Click here to jump to the commons website - - * https://commons.apache.org"20"\n"true"Click here to jump\nto the commons\nwebsite - * -\nhttp://commons.apach\ne.org"
- * - * @param str the String to be word wrapped, may be null - * @param wrapLength the column to wrap the words at, less than 1 is treated - * as 1 - * @param newLineStr the string to insert for a new line, {@code null} uses - * the system property line separator - * @param wrapLongWords true if long words (such as URLs) should be wrapped - * @return a line with newlines inserted, {@code null} if null input - */ - public static String wrap(final String str, final int wrapLength, final String newLineStr, - final boolean wrapLongWords) { - return wrap(str, wrapLength, newLineStr, wrapLongWords, " "); - } - - /** - *

- * Wraps a single line of text, identifying words by {@code wrapOn}. - *

- * - *

- * Leading spaces on a new line are stripped. Trailing spaces are not stripped. - *

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Examples
inputwrapLengthnewLineStringwrapLongWordswrapOnresult
null**true/false*null
""**true/false*""
"Here is one line of text that is going to be wrapped after 20 - * columns."20"\n"true/false" ""Here is one line of\ntext that is going\nto be wrapped after\n20 - * columns."
"Here is one line of text that is going to be wrapped after 20 - * columns."20"<br />"true/false" ""Here is one line of<br />text that is going<br />to be - * wrapped after<br />20 columns."
"Here is one line of text that is going to be wrapped after 20 - * columns."20nulltrue/false" ""Here is one line of" + systemNewLine + "text that is going" + - * systemNewLine + "to be wrapped after" + systemNewLine + "20 columns."
"Click here to jump to the commons website - - * https://commons.apache.org"20"\n"false" ""Click here to jump\nto the commons\nwebsite - * -\nhttps://commons.apache.org"
"Click here to jump to the commons website - - * https://commons.apache.org"20"\n"true" ""Click here to jump\nto the commons\nwebsite - * -\nhttp://commons.apach\ne.org"
"flammable/inflammable"20"\n"true"/""flammable\ninflammable"
- * - * @param str the String to be word wrapped, may be null - * @param wrapLength the column to wrap the words at, less than 1 is treated - * as 1 - * @param newLineStr the string to insert for a new line, {@code null} uses - * the system property line separator - * @param wrapLongWords true if long words (such as URLs) should be wrapped - * @param wrapOn regex expression to be used as a breakable characters, - * if blank string is provided a space character will be - * used - * @return a line with newlines inserted, {@code null} if null input - */ - public static String wrap(final String str, int wrapLength, String newLineStr, final boolean wrapLongWords, - String wrapOn) { - if (str == null) { - return null; - } - if (newLineStr == null) { - newLineStr = System.lineSeparator(); - } - if (wrapLength < 1) { - wrapLength = 1; - } - if (StringUtils.isBlank(wrapOn)) { - wrapOn = " "; - } - final Pattern patternToWrapOn = Pattern.compile(wrapOn); - final int inputLineLength = str.length(); - int offset = 0; - final StringBuilder wrappedLine = new StringBuilder(inputLineLength + 32); - - while (offset < inputLineLength) { - int spaceToWrapAt = -1; - Matcher matcher = patternToWrapOn.matcher(str.substring(offset, - Math.min((int) Math.min(Integer.MAX_VALUE, offset + wrapLength + 1L), inputLineLength))); - if (matcher.find()) { - if (matcher.start() == 0) { - offset += matcher.end(); - continue; - } - spaceToWrapAt = matcher.start() + offset; - } - - // only last line without leading spaces is left - if (inputLineLength - offset <= wrapLength) { - break; - } - - while (matcher.find()) { - spaceToWrapAt = matcher.start() + offset; - } - - if (spaceToWrapAt >= offset) { - // normal case - wrappedLine.append(str, offset, spaceToWrapAt); - wrappedLine.append(newLineStr); - offset = spaceToWrapAt + 1; - - } else // really long word or URL - if (wrapLongWords) { - // wrap really long word one line at a time - wrappedLine.append(str, offset, wrapLength + offset); - wrappedLine.append(newLineStr); - offset += wrapLength; - } else { - // do not wrap really long word, just extend beyond limit - matcher = patternToWrapOn.matcher(str.substring(offset + wrapLength)); - if (matcher.find()) { - spaceToWrapAt = matcher.start() + offset + wrapLength; - } - - if (spaceToWrapAt >= 0) { - wrappedLine.append(str, offset, spaceToWrapAt); - wrappedLine.append(newLineStr); - offset = spaceToWrapAt + 1; - } else { - wrappedLine.append(str, offset, str.length()); - offset = inputLineLength; - } - } - } - - // Whatever is left in line is short enough to just pass through - wrappedLine.append(str, offset, str.length()); - - return wrappedLine.toString(); - } - - // Capitalizing - // ----------------------------------------------------------------------- - /** - *

- * Capitalizes all the whitespace separated words in a String. Only the first - * character of each word is changed. To convert the rest of each word to - * lowercase at the same time, use {@link #capitalizeFully(String)}. - *

- * - *

- * Whitespace is defined by {@link Character#isWhitespace(char)}. A {@code null} - * input String returns {@code null}. Capitalization uses the Unicode title - * case, normally equivalent to upper case. - *

- * - *
-	 * WordUtils.capitalize(null)        = null
-	 * WordUtils.capitalize("")          = ""
-	 * WordUtils.capitalize("i am FINE") = "I Am FINE"
-	 * 
- * - * @param str the String to capitalize, may be null - * @return capitalized String, {@code null} if null String input - * @see #uncapitalize(String) - * @see #capitalizeFully(String) - */ - public static String capitalize(final String str) { - return capitalize(str, null); - } - - /** - *

- * Capitalizes all the delimiter separated words in a String. Only the first - * character of each word is changed. To convert the rest of each word to - * lowercase at the same time, use {@link #capitalizeFully(String, char[])}. - *

- * - *

- * The delimiters represent a set of characters understood to separate words. - * The first string character and the first non-delimiter character after a - * delimiter will be capitalized. - *

- * - *

- * A {@code null} input String returns {@code null}. Capitalization uses the - * Unicode title case, normally equivalent to upper case. - *

- * - *
-	 * WordUtils.capitalize(null, *)            = null
-	 * WordUtils.capitalize("", *)              = ""
-	 * WordUtils.capitalize(*, new char[0])     = *
-	 * WordUtils.capitalize("i am fine", null)  = "I Am Fine"
-	 * WordUtils.capitalize("i aM.fine", {'.'}) = "I aM.Fine"
-	 * 
- * - * @param str the String to capitalize, may be null - * @param delimiters set of characters to determine capitalization, null means - * whitespace - * @return capitalized String, {@code null} if null String input - * @see #uncapitalize(String) - * @see #capitalizeFully(String) - * @since 2.1 - */ - public static String capitalize(final String str, final char... delimiters) { - final int delimLen = delimiters == null ? -1 : delimiters.length; - if (StringUtils.isEmpty(str) || delimLen == 0) { - return str; - } - final char[] buffer = str.toCharArray(); - boolean capitalizeNext = true; - for (int i = 0; i < buffer.length; i++) { - final char ch = buffer[i]; - if (isDelimiter(ch, delimiters)) { - capitalizeNext = true; - } else if (capitalizeNext) { - buffer[i] = Character.toTitleCase(ch); - capitalizeNext = false; - } - } - return new String(buffer); - } - - // ----------------------------------------------------------------------- - /** - *

- * Converts all the whitespace separated words in a String into capitalized - * words, that is each word is made up of a titlecase character and then a - * series of lowercase characters. - *

- * - *

- * Whitespace is defined by {@link Character#isWhitespace(char)}. A {@code null} - * input String returns {@code null}. Capitalization uses the Unicode title - * case, normally equivalent to upper case. - *

- * - *
-	 * WordUtils.capitalizeFully(null)        = null
-	 * WordUtils.capitalizeFully("")          = ""
-	 * WordUtils.capitalizeFully("i am FINE") = "I Am Fine"
-	 * 
- * - * @param str the String to capitalize, may be null - * @return capitalized String, {@code null} if null String input - */ - public static String capitalizeFully(final String str) { - return capitalizeFully(str, null); - } - - /** - *

- * Converts all the delimiter separated words in a String into capitalized - * words, that is each word is made up of a titlecase character and then a - * series of lowercase characters. - *

- * - *

- * The delimiters represent a set of characters understood to separate words. - * The first string character and the first non-delimiter character after a - * delimiter will be capitalized. - *

- * - *

- * A {@code null} input String returns {@code null}. Capitalization uses the - * Unicode title case, normally equivalent to upper case. - *

- * - *
-	 * WordUtils.capitalizeFully(null, *)            = null
-	 * WordUtils.capitalizeFully("", *)              = ""
-	 * WordUtils.capitalizeFully(*, null)            = *
-	 * WordUtils.capitalizeFully(*, new char[0])     = *
-	 * WordUtils.capitalizeFully("i aM.fine", {'.'}) = "I am.Fine"
-	 * 
- * - * @param str the String to capitalize, may be null - * @param delimiters set of characters to determine capitalization, null means - * whitespace - * @return capitalized String, {@code null} if null String input - * @since 2.1 - */ - public static String capitalizeFully(String str, final char... delimiters) { - final int delimLen = delimiters == null ? -1 : delimiters.length; - if (StringUtils.isEmpty(str) || delimLen == 0) { - return str; - } - str = str.toLowerCase(); - return capitalize(str, delimiters); - } - - // ----------------------------------------------------------------------- - /** - *

- * Uncapitalizes all the whitespace separated words in a String. Only the first - * character of each word is changed. - *

- * - *

- * Whitespace is defined by {@link Character#isWhitespace(char)}. A {@code null} - * input String returns {@code null}. - *

- * - *
-	 * WordUtils.uncapitalize(null)        = null
-	 * WordUtils.uncapitalize("")          = ""
-	 * WordUtils.uncapitalize("I Am FINE") = "i am fINE"
-	 * 
- * - * @param str the String to uncapitalize, may be null - * @return uncapitalized String, {@code null} if null String input - * @see #capitalize(String) - */ - public static String uncapitalize(final String str) { - return uncapitalize(str, null); - } - - /** - *

- * Uncapitalizes all the whitespace separated words in a String. Only the first - * character of each word is changed. - *

- * - *

- * The delimiters represent a set of characters understood to separate words. - * The first string character and the first non-delimiter character after a - * delimiter will be uncapitalized. - *

- * - *

- * Whitespace is defined by {@link Character#isWhitespace(char)}. A {@code null} - * input String returns {@code null}. - *

- * - *
-	 * WordUtils.uncapitalize(null, *)            = null
-	 * WordUtils.uncapitalize("", *)              = ""
-	 * WordUtils.uncapitalize(*, null)            = *
-	 * WordUtils.uncapitalize(*, new char[0])     = *
-	 * WordUtils.uncapitalize("I AM.FINE", {'.'}) = "i AM.fINE"
-	 * 
- * - * @param str the String to uncapitalize, may be null - * @param delimiters set of characters to determine uncapitalization, null means - * whitespace - * @return uncapitalized String, {@code null} if null String input - * @see #capitalize(String) - * @since 2.1 - */ - public static String uncapitalize(final String str, final char... delimiters) { - final int delimLen = delimiters == null ? -1 : delimiters.length; - if (StringUtils.isEmpty(str) || delimLen == 0) { - return str; - } - final char[] buffer = str.toCharArray(); - boolean uncapitalizeNext = true; - for (int i = 0; i < buffer.length; i++) { - final char ch = buffer[i]; - if (isDelimiter(ch, delimiters)) { - uncapitalizeNext = true; - } else if (uncapitalizeNext) { - buffer[i] = Character.toLowerCase(ch); - uncapitalizeNext = false; - } - } - return new String(buffer); - } - - // ----------------------------------------------------------------------- - /** - *

- * Swaps the case of a String using a word based algorithm. - *

- * - *
    - *
  • Upper case character converts to Lower case
  • - *
  • Title case character converts to Lower case
  • - *
  • Lower case character after Whitespace or at start converts to Title - * case
  • - *
  • Other Lower case character converts to Upper case
  • - *
- * - *

- * Whitespace is defined by {@link Character#isWhitespace(char)}. A {@code null} - * input String returns {@code null}. - *

- * - *
-	 * StringUtils.swapCase(null)                 = null
-	 * StringUtils.swapCase("")                   = ""
-	 * StringUtils.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
-	 * 
- * - * @param str the String to swap case, may be null - * @return the changed String, {@code null} if null String input - */ - public static String swapCase(final String str) { - if (StringUtils.isEmpty(str)) { - return str; - } - final char[] buffer = str.toCharArray(); - - boolean whitespace = true; - - for (int i = 0; i < buffer.length; i++) { - final char ch = buffer[i]; - if (Character.isUpperCase(ch) || Character.isTitleCase(ch)) { - buffer[i] = Character.toLowerCase(ch); - whitespace = false; - } else if (Character.isLowerCase(ch)) { - if (whitespace) { - buffer[i] = Character.toTitleCase(ch); - whitespace = false; - } else { - buffer[i] = Character.toUpperCase(ch); - } - } else { - whitespace = Character.isWhitespace(ch); - } - } - return new String(buffer); - } - - // ----------------------------------------------------------------------- - /** - *

- * Extracts the initial characters from each word in the String. - *

- * - *

- * All first characters after whitespace are returned as a new string. Their - * case is not changed. - *

- * - *

- * Whitespace is defined by {@link Character#isWhitespace(char)}. A {@code null} - * input String returns {@code null}. - *

- * - *
-	 * WordUtils.initials(null)             = null
-	 * WordUtils.initials("")               = ""
-	 * WordUtils.initials("Ben John Lee")   = "BJL"
-	 * WordUtils.initials("Ben J.Lee")      = "BJ"
-	 * 
- * - * @param str the String to get initials from, may be null - * @return String of initial letters, {@code null} if null String input - * @see #initials(String,char[]) - * @since 2.2 - */ - public static String initials(final String str) { - return initials(str, null); - } - - /** - *

- * Extracts the initial characters from each word in the String. - *

- * - *

- * All first characters after the defined delimiters are returned as a new - * string. Their case is not changed. - *

- * - *

- * If the delimiters array is null, then Whitespace is used. Whitespace is - * defined by {@link Character#isWhitespace(char)}. A {@code null} input String - * returns {@code null}. An empty delimiter array returns an empty String. - *

- * - *
-	 * WordUtils.initials(null, *)                = null
-	 * WordUtils.initials("", *)                  = ""
-	 * WordUtils.initials("Ben John Lee", null)   = "BJL"
-	 * WordUtils.initials("Ben J.Lee", null)      = "BJ"
-	 * WordUtils.initials("Ben J.Lee", [' ','.']) = "BJL"
-	 * WordUtils.initials(*, new char[0])         = ""
-	 * 
- * - * @param str the String to get initials from, may be null - * @param delimiters set of characters to determine words, null means whitespace - * @return String of initial characters, {@code null} if null String input - * @see #initials(String) - * @since 2.2 - */ - public static String initials(final String str, final char... delimiters) { - if (StringUtils.isEmpty(str)) { - return str; - } - if (delimiters != null && delimiters.length == 0) { - return StringUtils.EMPTY; - } - final int strLen = str.length(); - final char[] buf = new char[strLen / 2 + 1]; - int count = 0; - boolean lastWasGap = true; - for (int i = 0; i < strLen; i++) { - final char ch = str.charAt(i); - - if (isDelimiter(ch, delimiters)) { - lastWasGap = true; - } else if (lastWasGap) { - buf[count++] = ch; - lastWasGap = false; - } else { - continue; // ignore ch - } - } - return new String(buf, 0, count); - } - - // ----------------------------------------------------------------------- - /** - *

- * Checks if the String contains all words in the given array. - *

- * - *

- * A {@code null} String will return {@code false}. A {@code null}, zero length - * search array or if one element of array is null will return {@code false}. - *

- * - *
-	 * WordUtils.containsAllWords(null, *)            = false
-	 * WordUtils.containsAllWords("", *)              = false
-	 * WordUtils.containsAllWords(*, null)            = false
-	 * WordUtils.containsAllWords(*, [])              = false
-	 * WordUtils.containsAllWords("abcd", "ab", "cd") = false
-	 * WordUtils.containsAllWords("abc def", "def", "abc") = true
-	 * 
- * - * - * @param word The CharSequence to check, may be null - * @param words The array of String words to search for, may be null - * @return {@code true} if all search words are found, {@code false} otherwise - * @since 3.5 - */ - public static boolean containsAllWords(final CharSequence word, final CharSequence... words) { - if (StringUtils.isEmpty(word) || words.length == 0) { - return false; - } - for (final CharSequence w : words) { - if (StringUtils.isBlank(w)) { - return false; - } - final Pattern p = Pattern.compile(".*\\b" + w + "\\b.*"); - if (!p.matcher(word).matches()) { - return false; - } - } - return true; - } - - // ----------------------------------------------------------------------- - /** - * Is the character a delimiter. - * - * @param ch the character to check - * @param delimiters the delimiters - * @return true if it is a delimiter - */ - private static boolean isDelimiter(final char ch, final char[] delimiters) { - if (delimiters == null) { - return Character.isWhitespace(ch); - } - for (final char delimiter : delimiters) { - if (ch == delimiter) { - return true; - } - } - return false; - } - -} diff --git a/src/main/java/org/apache/commons/lang3/text/package-info.java b/src/main/java/org/apache/commons/lang3/text/package-info.java deleted file mode 100755 index 9a9c1aea..00000000 --- a/src/main/java/org/apache/commons/lang3/text/package-info.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - *

- * Provides classes for handling and manipulating text, partly as an extension - * to {@link java.text}. The classes in this package are, for the most part, - * intended to be instantiated (i.e. they are not utility classes with lots of - * static methods). - *

- * - *

- * Amongst other classes, the text package provides a replacement for - * {@link java.lang.StringBuffer} named - * {@link org.apache.commons.lang3.text.StrBuilder}, a class for substituting - * variables within a String named - * {@link org.apache.commons.lang3.text.StrSubstitutor} and a replacement for - * {@link java.util.StringTokenizer} named - * {@link org.apache.commons.lang3.text.StrTokenizer}. While somewhat ungainly, - * the {@code Str} prefix has been used to ensure we don't clash with any - * current or future standard Java classes. - *

- * - * @since 2.1 - */ -package org.apache.commons.lang3.text; diff --git a/src/main/java/org/apache/commons/lang3/text/translate/AggregateTranslator.java b/src/main/java/org/apache/commons/lang3/text/translate/AggregateTranslator.java deleted file mode 100755 index 63f2caf2..00000000 --- a/src/main/java/org/apache/commons/lang3/text/translate/AggregateTranslator.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.text.translate; - -import java.io.IOException; -import java.io.Writer; - -/** - * Executes a sequence of translators one after the other. Execution ends - * whenever the first translator consumes codepoints from the input. - * - * @since 3.0 - * @version $Id: AggregateTranslator.java 1436770 2013-01-22 07:09:45Z ggregory - * $ - */ -public class AggregateTranslator extends CharSequenceTranslator { - - private final CharSequenceTranslator[] translators; - - /** - * Specify the translators to be used at creation time. - * - * @param translators CharSequenceTranslator array to aggregate - */ - public AggregateTranslator(final CharSequenceTranslator... translators) { - this.translators = new CharSequenceTranslator[translators.length]; - System.arraycopy(translators, 0, this.translators, 0, translators.length); - } - - /** - * The first translator to consume codepoints from the input is the 'winner'. - * Execution stops with the number of consumed codepoints being returned. - * {@inheritDoc} - */ - @Override - public int translate(final CharSequence input, final int index, final Writer out) throws IOException { - for (final CharSequenceTranslator translator : translators) { - final int consumed = translator.translate(input, index, out); - if (consumed != 0) { - return consumed; - } - } - return 0; - } - -} diff --git a/src/main/java/org/apache/commons/lang3/text/translate/CharSequenceTranslator.java b/src/main/java/org/apache/commons/lang3/text/translate/CharSequenceTranslator.java deleted file mode 100755 index 1243fa60..00000000 --- a/src/main/java/org/apache/commons/lang3/text/translate/CharSequenceTranslator.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.text.translate; - -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; -import java.util.Locale; - -/** - * An API for translating text. Its core use is to escape and unescape text. - * Because escaping and unescaping is completely contextual, the API does not - * present two separate signatures. - * - * @since 3.0 - * @version $Id: CharSequenceTranslator.java 1568612 2014-02-15 10:35:35Z - * britter $ - */ -public abstract class CharSequenceTranslator { - - /** - * Translate a set of codepoints, represented by an int index into a - * CharSequence, into another set of codepoints. The number of codepoints - * consumed must be returned, and the only IOExceptions thrown must be from - * interacting with the Writer so that the top level API may reliably ignore - * StringWriter IOExceptions. - * - * @param input CharSequence that is being translated - * @param index int representing the current point of translation - * @param out Writer to translate the text to - * @return int count of codepoints consumed - * @throws IOException if and only if the Writer produces an IOException - */ - public abstract int translate(CharSequence input, int index, Writer out) throws IOException; - - /** - * Helper for non-Writer usage. - * - * @param input CharSequence to be translated - * @return String output of translation - */ - public final String translate(final CharSequence input) { - if (input == null) { - return null; - } - try { - final StringWriter writer = new StringWriter(input.length() * 2); - translate(input, writer); - return writer.toString(); - } catch (final IOException ioe) { - // this should never ever happen while writing to a StringWriter - throw new RuntimeException(ioe); - } - } - - /** - * Translate an input onto a Writer. This is intentionally final as its - * algorithm is tightly coupled with the abstract method of this class. - * - * @param input CharSequence that is being translated - * @param out Writer to translate the text to - * @throws IOException if and only if the Writer produces an IOException - */ - public final void translate(final CharSequence input, final Writer out) throws IOException { - if (out == null) { - throw new IllegalArgumentException("The Writer must not be null"); - } - if (input == null) { - return; - } - int pos = 0; - final int len = input.length(); - while (pos < len) { - final int consumed = translate(input, pos, out); - if (consumed == 0) { - final char[] c = Character.toChars(Character.codePointAt(input, pos)); - out.write(c); - pos += c.length; - continue; - } - // contract with translators is that they have to understand codepoints - // and they just took care of a surrogate pair - for (int pt = 0; pt < consumed; pt++) { - pos += Character.charCount(Character.codePointAt(input, pos)); - } - } - } - - /** - * Helper method to create a merger of this translator with another set of - * translators. Useful in customizing the standard functionality. - * - * @param translators CharSequenceTranslator array of translators to merge with - * this one - * @return CharSequenceTranslator merging this translator with the others - */ - public final CharSequenceTranslator with(final CharSequenceTranslator... translators) { - final CharSequenceTranslator[] newArray = new CharSequenceTranslator[translators.length + 1]; - newArray[0] = this; - System.arraycopy(translators, 0, newArray, 1, translators.length); - return new AggregateTranslator(newArray); - } - - /** - *

- * Returns an upper case hexadecimal String for the given - * character. - *

- * - * @param codepoint The codepoint to convert. - * @return An upper case hexadecimal String - */ - public static String hex(final int codepoint) { - return Integer.toHexString(codepoint).toUpperCase(Locale.ENGLISH); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/text/translate/CodePointTranslator.java b/src/main/java/org/apache/commons/lang3/text/translate/CodePointTranslator.java deleted file mode 100755 index 6b839e9e..00000000 --- a/src/main/java/org/apache/commons/lang3/text/translate/CodePointTranslator.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.text.translate; - -import java.io.IOException; -import java.io.Writer; - -/** - * Helper subclass to CharSequenceTranslator to allow for translations that will - * replace up to one character at a time. - * - * @since 3.0 - * @version $Id: CodePointTranslator.java 1553931 2013-12-28 21:24:44Z ggregory - * $ - */ -public abstract class CodePointTranslator extends CharSequenceTranslator { - - /** - * Implementation of translate that maps onto the abstract translate(int, - * Writer) method. {@inheritDoc} - */ - @Override - public final int translate(final CharSequence input, final int index, final Writer out) throws IOException { - final int codepoint = Character.codePointAt(input, index); - final boolean consumed = translate(codepoint, out); - return consumed ? 1 : 0; - } - - /** - * Translate the specified codepoint into another. - * - * @param codepoint int character input to translate - * @param out Writer to optionally push the translated output to - * @return boolean as to whether translation occurred or not - * @throws IOException if and only if the Writer produces an IOException - */ - public abstract boolean translate(int codepoint, Writer out) throws IOException; - -} diff --git a/src/main/java/org/apache/commons/lang3/text/translate/EntityArrays.java b/src/main/java/org/apache/commons/lang3/text/translate/EntityArrays.java deleted file mode 100755 index ee20a67f..00000000 --- a/src/main/java/org/apache/commons/lang3/text/translate/EntityArrays.java +++ /dev/null @@ -1,461 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.text.translate; - -/** - * Class holding various entity data for HTML and XML - generally for use with - * the LookupTranslator. All arrays are of length [*][2]. - * - * @since 3.0 - * @version $Id: EntityArrays.java 1436770 2013-01-22 07:09:45Z ggregory $ - */ -public class EntityArrays { - - /** - * Mapping to escape ISO-8859-1 - * characters to their named HTML 3.x equivalents. - * - * @return the mapping table - */ - public static String[][] ISO8859_1_ESCAPE() { - return ISO8859_1_ESCAPE.clone(); - } - - private static final String[][] ISO8859_1_ESCAPE = { { "\u00A0", " " }, // non-breaking space - { "\u00A1", "¡" }, // inverted exclamation mark - { "\u00A2", "¢" }, // cent sign - { "\u00A3", "£" }, // pound sign - { "\u00A4", "¤" }, // currency sign - { "\u00A5", "¥" }, // yen sign = yuan sign - { "\u00A6", "¦" }, // broken bar = broken vertical bar - { "\u00A7", "§" }, // section sign - { "\u00A8", "¨" }, // diaeresis = spacing diaeresis - { "\u00A9", "©" }, // � - copyright sign - { "\u00AA", "ª" }, // feminine ordinal indicator - { "\u00AB", "«" }, // left-pointing double angle quotation mark = left pointing guillemet - { "\u00AC", "¬" }, // not sign - { "\u00AD", "­" }, // soft hyphen = discretionary hyphen - { "\u00AE", "®" }, // � - registered trademark sign - { "\u00AF", "¯" }, // macron = spacing macron = overline = APL overbar - { "\u00B0", "°" }, // degree sign - { "\u00B1", "±" }, // plus-minus sign = plus-or-minus sign - { "\u00B2", "²" }, // superscript two = superscript digit two = squared - { "\u00B3", "³" }, // superscript three = superscript digit three = cubed - { "\u00B4", "´" }, // acute accent = spacing acute - { "\u00B5", "µ" }, // micro sign - { "\u00B6", "¶" }, // pilcrow sign = paragraph sign - { "\u00B7", "·" }, // middle dot = Georgian comma = Greek middle dot - { "\u00B8", "¸" }, // cedilla = spacing cedilla - { "\u00B9", "¹" }, // superscript one = superscript digit one - { "\u00BA", "º" }, // masculine ordinal indicator - { "\u00BB", "»" }, // right-pointing double angle quotation mark = right pointing guillemet - { "\u00BC", "¼" }, // vulgar fraction one quarter = fraction one quarter - { "\u00BD", "½" }, // vulgar fraction one half = fraction one half - { "\u00BE", "¾" }, // vulgar fraction three quarters = fraction three quarters - { "\u00BF", "¿" }, // inverted question mark = turned question mark - { "\u00C0", "À" }, // � - uppercase A, grave accent - { "\u00C1", "Á" }, // � - uppercase A, acute accent - { "\u00C2", "Â" }, // � - uppercase A, circumflex accent - { "\u00C3", "Ã" }, // � - uppercase A, tilde - { "\u00C4", "Ä" }, // � - uppercase A, umlaut - { "\u00C5", "Å" }, // � - uppercase A, ring - { "\u00C6", "Æ" }, // � - uppercase AE - { "\u00C7", "Ç" }, // � - uppercase C, cedilla - { "\u00C8", "È" }, // � - uppercase E, grave accent - { "\u00C9", "É" }, // � - uppercase E, acute accent - { "\u00CA", "Ê" }, // � - uppercase E, circumflex accent - { "\u00CB", "Ë" }, // � - uppercase E, umlaut - { "\u00CC", "Ì" }, // � - uppercase I, grave accent - { "\u00CD", "Í" }, // � - uppercase I, acute accent - { "\u00CE", "Î" }, // � - uppercase I, circumflex accent - { "\u00CF", "Ï" }, // � - uppercase I, umlaut - { "\u00D0", "Ð" }, // � - uppercase Eth, Icelandic - { "\u00D1", "Ñ" }, // � - uppercase N, tilde - { "\u00D2", "Ò" }, // � - uppercase O, grave accent - { "\u00D3", "Ó" }, // � - uppercase O, acute accent - { "\u00D4", "Ô" }, // � - uppercase O, circumflex accent - { "\u00D5", "Õ" }, // � - uppercase O, tilde - { "\u00D6", "Ö" }, // � - uppercase O, umlaut - { "\u00D7", "×" }, // multiplication sign - { "\u00D8", "Ø" }, // � - uppercase O, slash - { "\u00D9", "Ù" }, // � - uppercase U, grave accent - { "\u00DA", "Ú" }, // � - uppercase U, acute accent - { "\u00DB", "Û" }, // � - uppercase U, circumflex accent - { "\u00DC", "Ü" }, // � - uppercase U, umlaut - { "\u00DD", "Ý" }, // � - uppercase Y, acute accent - { "\u00DE", "Þ" }, // � - uppercase THORN, Icelandic - { "\u00DF", "ß" }, // � - lowercase sharps, German - { "\u00E0", "à" }, // � - lowercase a, grave accent - { "\u00E1", "á" }, // � - lowercase a, acute accent - { "\u00E2", "â" }, // � - lowercase a, circumflex accent - { "\u00E3", "ã" }, // � - lowercase a, tilde - { "\u00E4", "ä" }, // � - lowercase a, umlaut - { "\u00E5", "å" }, // � - lowercase a, ring - { "\u00E6", "æ" }, // � - lowercase ae - { "\u00E7", "ç" }, // � - lowercase c, cedilla - { "\u00E8", "è" }, // � - lowercase e, grave accent - { "\u00E9", "é" }, // � - lowercase e, acute accent - { "\u00EA", "ê" }, // � - lowercase e, circumflex accent - { "\u00EB", "ë" }, // � - lowercase e, umlaut - { "\u00EC", "ì" }, // � - lowercase i, grave accent - { "\u00ED", "í" }, // � - lowercase i, acute accent - { "\u00EE", "î" }, // � - lowercase i, circumflex accent - { "\u00EF", "ï" }, // � - lowercase i, umlaut - { "\u00F0", "ð" }, // � - lowercase eth, Icelandic - { "\u00F1", "ñ" }, // � - lowercase n, tilde - { "\u00F2", "ò" }, // � - lowercase o, grave accent - { "\u00F3", "ó" }, // � - lowercase o, acute accent - { "\u00F4", "ô" }, // � - lowercase o, circumflex accent - { "\u00F5", "õ" }, // � - lowercase o, tilde - { "\u00F6", "ö" }, // � - lowercase o, umlaut - { "\u00F7", "÷" }, // division sign - { "\u00F8", "ø" }, // � - lowercase o, slash - { "\u00F9", "ù" }, // � - lowercase u, grave accent - { "\u00FA", "ú" }, // � - lowercase u, acute accent - { "\u00FB", "û" }, // � - lowercase u, circumflex accent - { "\u00FC", "ü" }, // � - lowercase u, umlaut - { "\u00FD", "ý" }, // � - lowercase y, acute accent - { "\u00FE", "þ" }, // � - lowercase thorn, Icelandic - { "\u00FF", "ÿ" }, // � - lowercase y, umlaut - }; - - /** - * Reverse of {@link #ISO8859_1_ESCAPE()} for unescaping purposes. - * - * @return the mapping table - */ - public static String[][] ISO8859_1_UNESCAPE() { - return ISO8859_1_UNESCAPE.clone(); - } - - private static final String[][] ISO8859_1_UNESCAPE = invert(ISO8859_1_ESCAPE); - - /** - * Mapping to escape additional - * character entity - * references. Note that this must be used with {@link #ISO8859_1_ESCAPE()} - * to get the full list of HTML 4.0 character entities. - * - * @return the mapping table - */ - public static String[][] HTML40_EXTENDED_ESCAPE() { - return HTML40_EXTENDED_ESCAPE.clone(); - } - - private static final String[][] HTML40_EXTENDED_ESCAPE = { - // - { "\u0192", "ƒ" }, // latin small f with hook = function= florin, U+0192 ISOtech --> - // - { "\u0391", "Α" }, // greek capital letter alpha, U+0391 --> - { "\u0392", "Β" }, // greek capital letter beta, U+0392 --> - { "\u0393", "Γ" }, // greek capital letter gamma,U+0393 ISOgrk3 --> - { "\u0394", "Δ" }, // greek capital letter delta,U+0394 ISOgrk3 --> - { "\u0395", "Ε" }, // greek capital letter epsilon, U+0395 --> - { "\u0396", "Ζ" }, // greek capital letter zeta, U+0396 --> - { "\u0397", "Η" }, // greek capital letter eta, U+0397 --> - { "\u0398", "Θ" }, // greek capital letter theta,U+0398 ISOgrk3 --> - { "\u0399", "Ι" }, // greek capital letter iota, U+0399 --> - { "\u039A", "Κ" }, // greek capital letter kappa, U+039A --> - { "\u039B", "Λ" }, // greek capital letter lambda,U+039B ISOgrk3 --> - { "\u039C", "Μ" }, // greek capital letter mu, U+039C --> - { "\u039D", "Ν" }, // greek capital letter nu, U+039D --> - { "\u039E", "Ξ" }, // greek capital letter xi, U+039E ISOgrk3 --> - { "\u039F", "Ο" }, // greek capital letter omicron, U+039F --> - { "\u03A0", "Π" }, // greek capital letter pi, U+03A0 ISOgrk3 --> - { "\u03A1", "Ρ" }, // greek capital letter rho, U+03A1 --> - // - { "\u03A3", "Σ" }, // greek capital letter sigma,U+03A3 ISOgrk3 --> - { "\u03A4", "Τ" }, // greek capital letter tau, U+03A4 --> - { "\u03A5", "Υ" }, // greek capital letter upsilon,U+03A5 ISOgrk3 --> - { "\u03A6", "Φ" }, // greek capital letter phi,U+03A6 ISOgrk3 --> - { "\u03A7", "Χ" }, // greek capital letter chi, U+03A7 --> - { "\u03A8", "Ψ" }, // greek capital letter psi,U+03A8 ISOgrk3 --> - { "\u03A9", "Ω" }, // greek capital letter omega,U+03A9 ISOgrk3 --> - { "\u03B1", "α" }, // greek small letter alpha,U+03B1 ISOgrk3 --> - { "\u03B2", "β" }, // greek small letter beta, U+03B2 ISOgrk3 --> - { "\u03B3", "γ" }, // greek small letter gamma,U+03B3 ISOgrk3 --> - { "\u03B4", "δ" }, // greek small letter delta,U+03B4 ISOgrk3 --> - { "\u03B5", "ε" }, // greek small letter epsilon,U+03B5 ISOgrk3 --> - { "\u03B6", "ζ" }, // greek small letter zeta, U+03B6 ISOgrk3 --> - { "\u03B7", "η" }, // greek small letter eta, U+03B7 ISOgrk3 --> - { "\u03B8", "θ" }, // greek small letter theta,U+03B8 ISOgrk3 --> - { "\u03B9", "ι" }, // greek small letter iota, U+03B9 ISOgrk3 --> - { "\u03BA", "κ" }, // greek small letter kappa,U+03BA ISOgrk3 --> - { "\u03BB", "λ" }, // greek small letter lambda,U+03BB ISOgrk3 --> - { "\u03BC", "μ" }, // greek small letter mu, U+03BC ISOgrk3 --> - { "\u03BD", "ν" }, // greek small letter nu, U+03BD ISOgrk3 --> - { "\u03BE", "ξ" }, // greek small letter xi, U+03BE ISOgrk3 --> - { "\u03BF", "ο" }, // greek small letter omicron, U+03BF NEW --> - { "\u03C0", "π" }, // greek small letter pi, U+03C0 ISOgrk3 --> - { "\u03C1", "ρ" }, // greek small letter rho, U+03C1 ISOgrk3 --> - { "\u03C2", "ς" }, // greek small letter final sigma,U+03C2 ISOgrk3 --> - { "\u03C3", "σ" }, // greek small letter sigma,U+03C3 ISOgrk3 --> - { "\u03C4", "τ" }, // greek small letter tau, U+03C4 ISOgrk3 --> - { "\u03C5", "υ" }, // greek small letter upsilon,U+03C5 ISOgrk3 --> - { "\u03C6", "φ" }, // greek small letter phi, U+03C6 ISOgrk3 --> - { "\u03C7", "χ" }, // greek small letter chi, U+03C7 ISOgrk3 --> - { "\u03C8", "ψ" }, // greek small letter psi, U+03C8 ISOgrk3 --> - { "\u03C9", "ω" }, // greek small letter omega,U+03C9 ISOgrk3 --> - { "\u03D1", "ϑ" }, // greek small letter theta symbol,U+03D1 NEW --> - { "\u03D2", "ϒ" }, // greek upsilon with hook symbol,U+03D2 NEW --> - { "\u03D6", "ϖ" }, // greek pi symbol, U+03D6 ISOgrk3 --> - // - { "\u2022", "•" }, // bullet = black small circle,U+2022 ISOpub --> - // - { "\u2026", "…" }, // horizontal ellipsis = three dot leader,U+2026 ISOpub --> - { "\u2032", "′" }, // prime = minutes = feet, U+2032 ISOtech --> - { "\u2033", "″" }, // double prime = seconds = inches,U+2033 ISOtech --> - { "\u203E", "‾" }, // overline = spacing overscore,U+203E NEW --> - { "\u2044", "⁄" }, // fraction slash, U+2044 NEW --> - // - { "\u2118", "℘" }, // script capital P = power set= Weierstrass p, U+2118 ISOamso --> - { "\u2111", "ℑ" }, // blackletter capital I = imaginary part,U+2111 ISOamso --> - { "\u211C", "ℜ" }, // blackletter capital R = real part symbol,U+211C ISOamso --> - { "\u2122", "™" }, // trade mark sign, U+2122 ISOnum --> - { "\u2135", "ℵ" }, // alef symbol = first transfinite cardinal,U+2135 NEW --> - // - // - { "\u2190", "←" }, // leftwards arrow, U+2190 ISOnum --> - { "\u2191", "↑" }, // upwards arrow, U+2191 ISOnum--> - { "\u2192", "→" }, // rightwards arrow, U+2192 ISOnum --> - { "\u2193", "↓" }, // downwards arrow, U+2193 ISOnum --> - { "\u2194", "↔" }, // left right arrow, U+2194 ISOamsa --> - { "\u21B5", "↵" }, // downwards arrow with corner leftwards= carriage return, U+21B5 NEW --> - { "\u21D0", "⇐" }, // leftwards double arrow, U+21D0 ISOtech --> - // - { "\u21D1", "⇑" }, // upwards double arrow, U+21D1 ISOamsa --> - { "\u21D2", "⇒" }, // rightwards double arrow,U+21D2 ISOtech --> - // - { "\u21D3", "⇓" }, // downwards double arrow, U+21D3 ISOamsa --> - { "\u21D4", "⇔" }, // left right double arrow,U+21D4 ISOamsa --> - // - { "\u2200", "∀" }, // for all, U+2200 ISOtech --> - { "\u2202", "∂" }, // partial differential, U+2202 ISOtech --> - { "\u2203", "∃" }, // there exists, U+2203 ISOtech --> - { "\u2205", "∅" }, // empty set = null set = diameter,U+2205 ISOamso --> - { "\u2207", "∇" }, // nabla = backward difference,U+2207 ISOtech --> - { "\u2208", "∈" }, // element of, U+2208 ISOtech --> - { "\u2209", "∉" }, // not an element of, U+2209 ISOtech --> - { "\u220B", "∋" }, // contains as member, U+220B ISOtech --> - // - { "\u220F", "∏" }, // n-ary product = product sign,U+220F ISOamsb --> - // - { "\u2211", "∑" }, // n-ary summation, U+2211 ISOamsb --> - // - { "\u2212", "−" }, // minus sign, U+2212 ISOtech --> - { "\u2217", "∗" }, // asterisk operator, U+2217 ISOtech --> - { "\u221A", "√" }, // square root = radical sign,U+221A ISOtech --> - { "\u221D", "∝" }, // proportional to, U+221D ISOtech --> - { "\u221E", "∞" }, // infinity, U+221E ISOtech --> - { "\u2220", "∠" }, // angle, U+2220 ISOamso --> - { "\u2227", "∧" }, // logical and = wedge, U+2227 ISOtech --> - { "\u2228", "∨" }, // logical or = vee, U+2228 ISOtech --> - { "\u2229", "∩" }, // intersection = cap, U+2229 ISOtech --> - { "\u222A", "∪" }, // union = cup, U+222A ISOtech --> - { "\u222B", "∫" }, // integral, U+222B ISOtech --> - { "\u2234", "∴" }, // therefore, U+2234 ISOtech --> - { "\u223C", "∼" }, // tilde operator = varies with = similar to,U+223C ISOtech --> - // - { "\u2245", "≅" }, // approximately equal to, U+2245 ISOtech --> - { "\u2248", "≈" }, // almost equal to = asymptotic to,U+2248 ISOamsr --> - { "\u2260", "≠" }, // not equal to, U+2260 ISOtech --> - { "\u2261", "≡" }, // identical to, U+2261 ISOtech --> - { "\u2264", "≤" }, // less-than or equal to, U+2264 ISOtech --> - { "\u2265", "≥" }, // greater-than or equal to,U+2265 ISOtech --> - { "\u2282", "⊂" }, // subset of, U+2282 ISOtech --> - { "\u2283", "⊃" }, // superset of, U+2283 ISOtech --> - // - { "\u2286", "⊆" }, // subset of or equal to, U+2286 ISOtech --> - { "\u2287", "⊇" }, // superset of or equal to,U+2287 ISOtech --> - { "\u2295", "⊕" }, // circled plus = direct sum,U+2295 ISOamsb --> - { "\u2297", "⊗" }, // circled times = vector product,U+2297 ISOamsb --> - { "\u22A5", "⊥" }, // up tack = orthogonal to = perpendicular,U+22A5 ISOtech --> - { "\u22C5", "⋅" }, // dot operator, U+22C5 ISOamsb --> - // - // - { "\u2308", "⌈" }, // left ceiling = apl upstile,U+2308 ISOamsc --> - { "\u2309", "⌉" }, // right ceiling, U+2309 ISOamsc --> - { "\u230A", "⌊" }, // left floor = apl downstile,U+230A ISOamsc --> - { "\u230B", "⌋" }, // right floor, U+230B ISOamsc --> - { "\u2329", "⟨" }, // left-pointing angle bracket = bra,U+2329 ISOtech --> - // - { "\u232A", "⟩" }, // right-pointing angle bracket = ket,U+232A ISOtech --> - // - // - { "\u25CA", "◊" }, // lozenge, U+25CA ISOpub --> - // - { "\u2660", "♠" }, // black spade suit, U+2660 ISOpub --> - // - { "\u2663", "♣" }, // black club suit = shamrock,U+2663 ISOpub --> - { "\u2665", "♥" }, // black heart suit = valentine,U+2665 ISOpub --> - { "\u2666", "♦" }, // black diamond suit, U+2666 ISOpub --> - - // - { "\u0152", "Œ" }, // -- latin capital ligature OE,U+0152 ISOlat2 --> - { "\u0153", "œ" }, // -- latin small ligature oe, U+0153 ISOlat2 --> - // - { "\u0160", "Š" }, // -- latin capital letter S with caron,U+0160 ISOlat2 --> - { "\u0161", "š" }, // -- latin small letter s with caron,U+0161 ISOlat2 --> - { "\u0178", "Ÿ" }, // -- latin capital letter Y with diaeresis,U+0178 ISOlat2 --> - // - { "\u02C6", "ˆ" }, // -- modifier letter circumflex accent,U+02C6 ISOpub --> - { "\u02DC", "˜" }, // small tilde, U+02DC ISOdia --> - // - { "\u2002", " " }, // en space, U+2002 ISOpub --> - { "\u2003", " " }, // em space, U+2003 ISOpub --> - { "\u2009", " " }, // thin space, U+2009 ISOpub --> - { "\u200C", "‌" }, // zero width non-joiner,U+200C NEW RFC 2070 --> - { "\u200D", "‍" }, // zero width joiner, U+200D NEW RFC 2070 --> - { "\u200E", "‎" }, // left-to-right mark, U+200E NEW RFC 2070 --> - { "\u200F", "‏" }, // right-to-left mark, U+200F NEW RFC 2070 --> - { "\u2013", "–" }, // en dash, U+2013 ISOpub --> - { "\u2014", "—" }, // em dash, U+2014 ISOpub --> - { "\u2018", "‘" }, // left single quotation mark,U+2018 ISOnum --> - { "\u2019", "’" }, // right single quotation mark,U+2019 ISOnum --> - { "\u201A", "‚" }, // single low-9 quotation mark, U+201A NEW --> - { "\u201C", "“" }, // left double quotation mark,U+201C ISOnum --> - { "\u201D", "”" }, // right double quotation mark,U+201D ISOnum --> - { "\u201E", "„" }, // double low-9 quotation mark, U+201E NEW --> - { "\u2020", "†" }, // dagger, U+2020 ISOpub --> - { "\u2021", "‡" }, // double dagger, U+2021 ISOpub --> - { "\u2030", "‰" }, // per mille sign, U+2030 ISOtech --> - { "\u2039", "‹" }, // single left-pointing angle quotation mark,U+2039 ISO proposed --> - // - { "\u203A", "›" }, // single right-pointing angle quotation mark,U+203A ISO proposed --> - // - { "\u20AC", "€" }, // -- euro sign, U+20AC NEW --> - }; - - /** - * Reverse of {@link #HTML40_EXTENDED_ESCAPE()} for unescaping purposes. - * - * @return the mapping table - */ - public static String[][] HTML40_EXTENDED_UNESCAPE() { - return HTML40_EXTENDED_UNESCAPE.clone(); - } - - private static final String[][] HTML40_EXTENDED_UNESCAPE = invert(HTML40_EXTENDED_ESCAPE); - - /** - * Mapping to escape the basic XML and HTML character entities. - * - * Namely: {@code " & < >} - * - * @return the mapping table - */ - public static String[][] BASIC_ESCAPE() { - return BASIC_ESCAPE.clone(); - } - - private static final String[][] BASIC_ESCAPE = { { "\"", """ }, // " - double-quote - { "&", "&" }, // & - ampersand - { "<", "<" }, // < - less-than - { ">", ">" }, // > - greater-than - }; - - /** - * Reverse of {@link #BASIC_ESCAPE()} for unescaping purposes. - * - * @return the mapping table - */ - public static String[][] BASIC_UNESCAPE() { - return BASIC_UNESCAPE.clone(); - } - - private static final String[][] BASIC_UNESCAPE = invert(BASIC_ESCAPE); - - /** - * Mapping to escape the apostrophe character to its XML character entity. - * - * @return the mapping table - */ - public static String[][] APOS_ESCAPE() { - return APOS_ESCAPE.clone(); - } - - private static final String[][] APOS_ESCAPE = { { "'", "'" }, // XML apostrophe - }; - - /** - * Reverse of {@link #APOS_ESCAPE()} for unescaping purposes. - * - * @return the mapping table - */ - public static String[][] APOS_UNESCAPE() { - return APOS_UNESCAPE.clone(); - } - - private static final String[][] APOS_UNESCAPE = invert(APOS_ESCAPE); - - /** - * Mapping to escape the Java control characters. - * - * Namely: {@code \b \n \t \f \r} - * - * @return the mapping table - */ - public static String[][] JAVA_CTRL_CHARS_ESCAPE() { - return JAVA_CTRL_CHARS_ESCAPE.clone(); - } - - private static final String[][] JAVA_CTRL_CHARS_ESCAPE = { { "\b", "\\b" }, { "\n", "\\n" }, { "\t", "\\t" }, - { "\f", "\\f" }, { "\r", "\\r" } }; - - /** - * Reverse of {@link #JAVA_CTRL_CHARS_ESCAPE()} for unescaping purposes. - * - * @return the mapping table - */ - public static String[][] JAVA_CTRL_CHARS_UNESCAPE() { - return JAVA_CTRL_CHARS_UNESCAPE.clone(); - } - - private static final String[][] JAVA_CTRL_CHARS_UNESCAPE = invert(JAVA_CTRL_CHARS_ESCAPE); - - /** - * Used to invert an escape array into an unescape array - * - * @param array String[][] to be inverted - * @return String[][] inverted array - */ - public static String[][] invert(final String[][] array) { - final String[][] newarray = new String[array.length][2]; - for (int i = 0; i < array.length; i++) { - newarray[i][0] = array[i][1]; - newarray[i][1] = array[i][0]; - } - return newarray; - } - -} diff --git a/src/main/java/org/apache/commons/lang3/text/translate/JavaUnicodeEscaper.java b/src/main/java/org/apache/commons/lang3/text/translate/JavaUnicodeEscaper.java deleted file mode 100755 index d372dc56..00000000 --- a/src/main/java/org/apache/commons/lang3/text/translate/JavaUnicodeEscaper.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.text.translate; - -/** - * Translates codepoints to their Unicode escaped value suitable for Java - * source. - * - * @since 3.2 - * @version $Id: JavaUnicodeEscaper.java 1451550 2013-03-01 10:06:13Z olamy $ - */ -public class JavaUnicodeEscaper extends UnicodeEscaper { - - /** - *

- * Constructs a JavaUnicodeEscaper above the specified value - * (exclusive). - *

- * - * @param codepoint above which to escape - * @return the newly created {@code UnicodeEscaper} instance - */ - public static JavaUnicodeEscaper above(final int codepoint) { - return outsideOf(0, codepoint); - } - - /** - *

- * Constructs a JavaUnicodeEscaper below the specified value - * (exclusive). - *

- * - * @param codepoint below which to escape - * @return the newly created {@code UnicodeEscaper} instance - */ - public static JavaUnicodeEscaper below(final int codepoint) { - return outsideOf(codepoint, Integer.MAX_VALUE); - } - - /** - *

- * Constructs a JavaUnicodeEscaper between the specified values - * (inclusive). - *

- * - * @param codepointLow above which to escape - * @param codepointHigh below which to escape - * @return the newly created {@code UnicodeEscaper} instance - */ - public static JavaUnicodeEscaper between(final int codepointLow, final int codepointHigh) { - return new JavaUnicodeEscaper(codepointLow, codepointHigh, true); - } - - /** - *

- * Constructs a JavaUnicodeEscaper outside of the specified values - * (exclusive). - *

- * - * @param codepointLow below which to escape - * @param codepointHigh above which to escape - * @return the newly created {@code UnicodeEscaper} instance - */ - public static JavaUnicodeEscaper outsideOf(final int codepointLow, final int codepointHigh) { - return new JavaUnicodeEscaper(codepointLow, codepointHigh, false); - } - - /** - *

- * Constructs a JavaUnicodeEscaper for the specified range. This is - * the underlying method for the other constructors/builders. The - * below and above boundaries are inclusive when - * between is true and exclusive when it is - * false. - *

- * - * @param below int value representing the lowest codepoint boundary - * @param above int value representing the highest codepoint boundary - * @param between whether to escape between the boundaries or outside them - */ - public JavaUnicodeEscaper(final int below, final int above, final boolean between) { - super(below, above, between); - } - - /** - * Converts the given codepoint to a hex string of the form - * {@code "\\uXXXX\\uXXXX"} - * - * @param codepoint a Unicode code point - * @return the hex string for the given codepoint - */ - @Override - protected String toUtf16Escape(final int codepoint) { - final char[] surrogatePair = Character.toChars(codepoint); - return "\\u" + hex(surrogatePair[0]) + "\\u" + hex(surrogatePair[1]); - } - -} diff --git a/src/main/java/org/apache/commons/lang3/text/translate/LookupTranslator.java b/src/main/java/org/apache/commons/lang3/text/translate/LookupTranslator.java deleted file mode 100755 index 0b70df65..00000000 --- a/src/main/java/org/apache/commons/lang3/text/translate/LookupTranslator.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.text.translate; - -import java.io.IOException; -import java.io.Writer; -import java.util.HashMap; - -/** - * Translates a value using a lookup table. - * - * @since 3.0 - * @version $Id: LookupTranslator.java 1470822 2013-04-23 06:00:41Z bayard $ - */ -public class LookupTranslator extends CharSequenceTranslator { - - private final HashMap lookupMap; - private final int shortest; - private final int longest; - - /** - * Define the lookup table to be used in translation - * - * Note that, as of Lang 3.1, the key to the lookup table is converted to a - * java.lang.String, while the value remains as a java.lang.CharSequence. This - * is because we need the key to support hashCode and equals(Object), allowing - * it to be the key for a HashMap. See LANG-882. - * - * @param lookup CharSequence[][] table of size [*][2] - */ - public LookupTranslator(final CharSequence[]... lookup) { - lookupMap = new HashMap(); - int _shortest = Integer.MAX_VALUE; - int _longest = 0; - if (lookup != null) { - for (final CharSequence[] seq : lookup) { - this.lookupMap.put(seq[0].toString(), seq[1]); - final int sz = seq[0].length(); - if (sz < _shortest) { - _shortest = sz; - } - if (sz > _longest) { - _longest = sz; - } - } - } - shortest = _shortest; - longest = _longest; - } - - /** - * {@inheritDoc} - */ - @Override - public int translate(final CharSequence input, final int index, final Writer out) throws IOException { - int max = longest; - if (index + longest > input.length()) { - max = input.length() - index; - } - // descend so as to get a greedy algorithm - for (int i = max; i >= shortest; i--) { - final CharSequence subSeq = input.subSequence(index, index + i); - final CharSequence result = lookupMap.get(subSeq.toString()); - if (result != null) { - out.write(result.toString()); - return i; - } - } - return 0; - } -} diff --git a/src/main/java/org/apache/commons/lang3/text/translate/NumericEntityEscaper.java b/src/main/java/org/apache/commons/lang3/text/translate/NumericEntityEscaper.java deleted file mode 100755 index 62925ae8..00000000 --- a/src/main/java/org/apache/commons/lang3/text/translate/NumericEntityEscaper.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.text.translate; - -import java.io.IOException; -import java.io.Writer; - -/** - * Translates codepoints to their XML numeric entity escaped value. - * - * @since 3.0 - * @version $Id: NumericEntityEscaper.java 1436768 2013-01-22 07:07:42Z ggregory - * $ - */ -public class NumericEntityEscaper extends CodePointTranslator { - - private final int below; - private final int above; - private final boolean between; - - /** - *

- * Constructs a NumericEntityEscaper for the specified range. This - * is the underlying method for the other constructors/builders. The - * below and above boundaries are inclusive when - * between is true and exclusive when it is - * false. - *

- * - * @param below int value representing the lowest codepoint boundary - * @param above int value representing the highest codepoint boundary - * @param between whether to escape between the boundaries or outside them - */ - private NumericEntityEscaper(final int below, final int above, final boolean between) { - this.below = below; - this.above = above; - this.between = between; - } - - /** - *

- * Constructs a NumericEntityEscaper for all characters. - *

- */ - public NumericEntityEscaper() { - this(0, Integer.MAX_VALUE, true); - } - - /** - *

- * Constructs a NumericEntityEscaper below the specified value - * (exclusive). - *

- * - * @param codepoint below which to escape - * @return the newly created {@code NumericEntityEscaper} instance - */ - public static NumericEntityEscaper below(final int codepoint) { - return outsideOf(codepoint, Integer.MAX_VALUE); - } - - /** - *

- * Constructs a NumericEntityEscaper above the specified value - * (exclusive). - *

- * - * @param codepoint above which to escape - * @return the newly created {@code NumericEntityEscaper} instance - */ - public static NumericEntityEscaper above(final int codepoint) { - return outsideOf(0, codepoint); - } - - /** - *

- * Constructs a NumericEntityEscaper between the specified values - * (inclusive). - *

- * - * @param codepointLow above which to escape - * @param codepointHigh below which to escape - * @return the newly created {@code NumericEntityEscaper} instance - */ - public static NumericEntityEscaper between(final int codepointLow, final int codepointHigh) { - return new NumericEntityEscaper(codepointLow, codepointHigh, true); - } - - /** - *

- * Constructs a NumericEntityEscaper outside of the specified - * values (exclusive). - *

- * - * @param codepointLow below which to escape - * @param codepointHigh above which to escape - * @return the newly created {@code NumericEntityEscaper} instance - */ - public static NumericEntityEscaper outsideOf(final int codepointLow, final int codepointHigh) { - return new NumericEntityEscaper(codepointLow, codepointHigh, false); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean translate(final int codepoint, final Writer out) throws IOException { - if (between) { - if (codepoint < below || codepoint > above) { - return false; - } - } else { - if (codepoint >= below && codepoint <= above) { - return false; - } - } - - out.write("&#"); - out.write(Integer.toString(codepoint, 10)); - out.write(';'); - return true; - } -} diff --git a/src/main/java/org/apache/commons/lang3/text/translate/NumericEntityUnescaper.java b/src/main/java/org/apache/commons/lang3/text/translate/NumericEntityUnescaper.java deleted file mode 100755 index 69223555..00000000 --- a/src/main/java/org/apache/commons/lang3/text/translate/NumericEntityUnescaper.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.lang3.text.translate; - -import java.io.IOException; -import java.io.Writer; -import java.util.Arrays; -import java.util.EnumSet; - -/** - * Translate XML numeric entities of the form &#[xX]?\d+;? to the specific - * codepoint. - * - * Note that the semi-colon is optional. - * - * @since 3.0 - * @version $Id: NumericEntityUnescaper.java 1583482 2014-03-31 22:54:57Z niallp - * $ - */ -public class NumericEntityUnescaper extends CharSequenceTranslator { - - public static enum OPTION { - semiColonRequired, semiColonOptional, errorIfNoSemiColon - } - - // TODO?: Create an OptionsSet class to hide some of the conditional logic below - private final EnumSet