diff --git a/CREDITS b/CREDITS index 5b07322..e72e0d4 100644 --- a/CREDITS +++ b/CREDITS @@ -217,6 +217,28 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + Project Name: High Performance Primitive Collections + Project Author: Carrot Search + Project URL: http://labs.carrotsearch.com/hppc.html + + Used For: Primitive collections library for the client + + * Copyright 2010-2013, Carrot Search s.c., Boznicza 11/56, Poznan, Poland + * + * Licensed 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 + * + * https://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. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + Project Name: Google Guava Project Author: Google Project URL: https://github.com/google/guava @@ -514,6 +536,45 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + Project Name: OpenJDK + Project Author: Oracle, IBM + Project URL: https://openjdk.org/projects/jdk/17/ + + Used For: Debloated version of IBM's ICU4J for reordering Arabic and Hebrew text + + * 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. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + Project Name: Java-WebSocket Project Author: Nathan Rajlich (TooTallNate) Project URL: http://tootallnate.github.io/Java-WebSocket diff --git a/buildtools/BuildTools.jar b/buildtools/BuildTools.jar index 6ead1ff..4fbb91d 100644 Binary files a/buildtools/BuildTools.jar and b/buildtools/BuildTools.jar differ diff --git a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/EaglerBuildTools.java b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/EaglerBuildTools.java index 82e051a..3a06843 100644 --- a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/EaglerBuildTools.java +++ b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/EaglerBuildTools.java @@ -32,7 +32,7 @@ public class EaglerBuildTools { public static void main(String[] args) { System.out.println("Eaglercraft 1.8 Build Tools"); - System.out.println("Copyright (c) 2022-2024 lax1dude"); + System.out.println("Copyright (c) 2022-2025 lax1dude"); System.out.println(); if(!System.getProperty("eaglercraft.isJava11", "false").equalsIgnoreCase("true")) { diff --git a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/CompileLatestClientFrame.java b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/CompileLatestClientFrame.java index cf52bd0..747ab74 100644 --- a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/CompileLatestClientFrame.java +++ b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/CompileLatestClientFrame.java @@ -429,7 +429,7 @@ public class CompileLatestClientFrame { lblNewLabel_1.setFont(new Font("Dialog", Font.BOLD, 14)); panel_2.add(lblNewLabel_1, BorderLayout.NORTH); - JLabel lblNewLabel_2 = new JLabel("Copyright (c) 2022-2024 lax1dude"); + JLabel lblNewLabel_2 = new JLabel("Copyright (c) 2022-2025 lax1dude"); lblNewLabel_2.setVerticalAlignment(SwingConstants.TOP); lblNewLabel_2.setPreferredSize(new Dimension(27, 24)); lblNewLabel_2.setFont(new Font("Dialog", Font.PLAIN, 14)); diff --git a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/CompileLatestClientGUI.java b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/CompileLatestClientGUI.java index 0701b2f..b569369 100644 --- a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/CompileLatestClientGUI.java +++ b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/CompileLatestClientGUI.java @@ -56,7 +56,7 @@ public class CompileLatestClientGUI { public static void main(String[] args) { System.out.println(); System.out.println("Launching client compiler wizard..."); - System.out.println("Copyright (c) 2022-2024 lax1dude"); + System.out.println("Copyright (c) 2022-2025 lax1dude"); System.out.println(); EventQueue.invokeLater(new Runnable() { public void run() { diff --git a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/TeaVMBinaries.java b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/TeaVMBinaries.java index 0c64c86..ca958ae 100644 --- a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/TeaVMBinaries.java +++ b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/TeaVMBinaries.java @@ -58,13 +58,6 @@ public class TeaVMBinaries { public static final MavenJAREntry teavmRelocatedLibsASMUtil = new MavenJAREntry("org/teavm/teavm-relocated-libs-asm-util/0.9.2/teavm-relocated-libs-asm-util-0.9.2.jar"); public static final MavenJAREntry teavmRelocatedLibsHPPC = new MavenJAREntry("org/teavm/teavm-relocated-libs-hppc/0.9.2/teavm-relocated-libs-hppc-0.9.2.jar"); public static final MavenJAREntry teavmRelocatedLibsRhino = new MavenJAREntry("org/teavm/teavm-relocated-libs-rhino/0.9.2/teavm-relocated-libs-rhino-0.9.2.jar"); - public static final MavenJAREntry asm = new MavenJAREntry("org/ow2/asm/asm/9.5/asm-9.5.jar"); - public static final MavenJAREntry asmAnalysis = new MavenJAREntry("org/ow2/asm/asm-analysis/9.5/asm-analysis-9.5.jar"); - public static final MavenJAREntry asmCommons = new MavenJAREntry("org/ow2/asm/asm-commons/9.5/asm-commons-9.5.jar"); - public static final MavenJAREntry asmTree = new MavenJAREntry("org/ow2/asm/asm-tree/9.5/asm-tree-9.5.jar"); - public static final MavenJAREntry asmUtil = new MavenJAREntry("org/ow2/asm/asm-util/9.5/asm-util-9.5.jar"); - public static final MavenJAREntry hppc = new MavenJAREntry("com/carrotsearch/hppc/0.9.1/hppc-0.9.1.jar"); - public static final MavenJAREntry rhino = new MavenJAREntry("org/mozilla/rhino/1.7.14/rhino-1.7.14.jar"); public static final MavenJAREntry teavmMetaprogrammingAPI = new MavenJAREntry("org/teavm/teavm-metaprogramming-api/0.9.2/teavm-metaprogramming-api-0.9.2.jar"); public static final MavenJAREntry teavmMetaprogrammingImpl = new MavenJAREntry("org/teavm/teavm-metaprogramming-impl/0.9.2/teavm-metaprogramming-impl-0.9.2.jar"); public static final MavenJAREntry teavmJodaTime = new MavenJAREntry("joda-time/joda-time/2.12.2/joda-time-2.12.2.jar"); @@ -73,10 +66,8 @@ public class TeaVMBinaries { private static final MavenJAREntry[] jarsList = new MavenJAREntry[] { teavmCore, teavmCli, teavmTooling, teavmPlatform, teavmClasslib, teavmInterop, teavmJSO, teavmJSOApis, teavmJSOImpl, teavmRelocatedLibsASM, teavmRelocatedLibsASMAnalysis, teavmRelocatedLibsASMCommons, teavmRelocatedLibsASMTree, - teavmRelocatedLibsASMUtil, teavmRelocatedLibsHPPC, teavmRelocatedLibsRhino, asm, asmAnalysis, asmCommons, - asmTree, asmUtil, hppc, rhino, teavmMetaprogrammingAPI, teavmMetaprogrammingImpl, teavmJodaTime, - teavmJZLIB - }; + teavmRelocatedLibsASMUtil, teavmRelocatedLibsHPPC, teavmRelocatedLibsRhino, teavmMetaprogrammingAPI, + teavmMetaprogrammingImpl, teavmJodaTime, teavmJZLIB }; public static File teavmBridge = null; @@ -205,8 +196,7 @@ public class TeaVMBinaries { return new File[] { teavmCore.file, teavmCli.file, teavmTooling.file, teavmInterop.file, teavmRelocatedLibsASM.file, teavmRelocatedLibsASMAnalysis.file, teavmRelocatedLibsASMCommons.file, teavmRelocatedLibsASMTree.file, teavmRelocatedLibsASMUtil.file, teavmRelocatedLibsHPPC.file, - teavmRelocatedLibsRhino.file, asm.file, asmAnalysis.file, asmCommons.file, asmTree.file, asmUtil.file, - hppc.file, rhino.file, teavmMetaprogrammingAPI.file, teavmBridge }; + teavmRelocatedLibsRhino.file, teavmMetaprogrammingAPI.file, teavmBridge }; } public static String[] getTeaVMRuntimeClasspath() { diff --git a/buildtools/src/main/resources/lang/LICENSE.txt b/buildtools/src/main/resources/lang/LICENSE.txt index c618c3b..395985c 100644 --- a/buildtools/src/main/resources/lang/LICENSE.txt +++ b/buildtools/src/main/resources/lang/LICENSE.txt @@ -1,3 +1,3 @@ -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 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. \ No newline at end of file diff --git a/buildtools/src/main/resources/lang/LICENSE_console_wrapped.txt b/buildtools/src/main/resources/lang/LICENSE_console_wrapped.txt index ecbb82d..f567215 100644 --- a/buildtools/src/main/resources/lang/LICENSE_console_wrapped.txt +++ b/buildtools/src/main/resources/lang/LICENSE_console_wrapped.txt @@ -1,4 +1,4 @@ -Copyright (c) 2022-2024 lax1dude, ayunami2000. +Copyright (c) 2022-2025 lax1dude, ayunami2000. All Rights Reserved. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND diff --git a/client_version b/client_version index 50356da..503c4b5 100644 --- a/client_version +++ b/client_version @@ -1 +1 @@ -u46 \ No newline at end of file +u47 \ No newline at end of file diff --git a/patches/minecraft/delete.txt b/patches/minecraft/delete.txt index fbf3884..2f01ea7 100644 --- a/patches/minecraft/delete.txt +++ b/patches/minecraft/delete.txt @@ -1,4 +1,4 @@ -# 146 files to delete: +# 148 files to delete: net/minecraft/client/renderer/VertexBufferUploader.java net/minecraft/realms/DisconnectedRealmsScreen.java net/minecraft/client/stream/Metadata.java @@ -95,6 +95,7 @@ net/minecraft/realms/Tezzelator.java net/minecraft/command/server/CommandSaveOff.java net/minecraft/realms/RealmsLevelSummary.java net/minecraft/realms/RealmsServerAddress.java +net/minecraft/util/LongHashMap.java net/minecraft/realms/RealmsAnvilLevelStorageSource.java net/minecraft/realms/RealmsSliderButton.java net/minecraft/world/storage/ThreadedFileIOBase.java @@ -140,6 +141,7 @@ net/minecraft/command/server/CommandStop.java net/minecraft/realms/RealmsSharedConstants.java net/minecraft/server/management/UserList.java net/minecraft/realms/RealmsSimpleScrolledSelectionList.java +net/minecraft/util/IntHashMap.java net/minecraft/client/resources/FolderResourcePack.java net/minecraft/client/stream/MetadataCombat.java net/minecraft/client/network/LanServerDetector.java diff --git a/patches/minecraft/net/minecraft/block/Block.edit.java b/patches/minecraft/net/minecraft/block/Block.edit.java index 4cd9ebb..4d942cd 100644 --- a/patches/minecraft/net/minecraft/block/Block.edit.java +++ b/patches/minecraft/net/minecraft/block/Block.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockAir.edit.java b/patches/minecraft/net/minecraft/block/BlockAir.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/block/BlockAir.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockAir.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockAnvil.edit.java b/patches/minecraft/net/minecraft/block/BlockAnvil.edit.java index 205a840..18ecd37 100644 --- a/patches/minecraft/net/minecraft/block/BlockAnvil.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockAnvil.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockBanner.edit.java b/patches/minecraft/net/minecraft/block/BlockBanner.edit.java index c941f8c..ae26bf7 100644 --- a/patches/minecraft/net/minecraft/block/BlockBanner.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockBanner.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockBarrier.edit.java b/patches/minecraft/net/minecraft/block/BlockBarrier.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/block/BlockBarrier.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockBarrier.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockBasePressurePlate.edit.java b/patches/minecraft/net/minecraft/block/BlockBasePressurePlate.edit.java index 2b08ce0..1dbd997 100644 --- a/patches/minecraft/net/minecraft/block/BlockBasePressurePlate.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockBasePressurePlate.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockBeacon.edit.java b/patches/minecraft/net/minecraft/block/BlockBeacon.edit.java index 9fbf5eb..a6e12ef 100644 --- a/patches/minecraft/net/minecraft/block/BlockBeacon.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockBeacon.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockBed.edit.java b/patches/minecraft/net/minecraft/block/BlockBed.edit.java index efdd843..e19020b 100644 --- a/patches/minecraft/net/minecraft/block/BlockBed.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockBed.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockBookshelf.edit.java b/patches/minecraft/net/minecraft/block/BlockBookshelf.edit.java index 3dc4f09..ee02fa5 100644 --- a/patches/minecraft/net/minecraft/block/BlockBookshelf.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockBookshelf.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockBreakable.edit.java b/patches/minecraft/net/minecraft/block/BlockBreakable.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/block/BlockBreakable.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockBreakable.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockBrewingStand.edit.java b/patches/minecraft/net/minecraft/block/BlockBrewingStand.edit.java index 8282ec1..3efa5be 100644 --- a/patches/minecraft/net/minecraft/block/BlockBrewingStand.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockBrewingStand.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockBush.edit.java b/patches/minecraft/net/minecraft/block/BlockBush.edit.java index 525bc4c..cb9821c 100644 --- a/patches/minecraft/net/minecraft/block/BlockBush.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockBush.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockButton.edit.java b/patches/minecraft/net/minecraft/block/BlockButton.edit.java index f3e2516..7b43b75 100644 --- a/patches/minecraft/net/minecraft/block/BlockButton.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockButton.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockButtonStone.edit.java b/patches/minecraft/net/minecraft/block/BlockButtonStone.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/block/BlockButtonStone.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockButtonStone.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockButtonWood.edit.java b/patches/minecraft/net/minecraft/block/BlockButtonWood.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/block/BlockButtonWood.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockButtonWood.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockCactus.edit.java b/patches/minecraft/net/minecraft/block/BlockCactus.edit.java index d4d351d..0193c02 100644 --- a/patches/minecraft/net/minecraft/block/BlockCactus.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockCactus.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockCake.edit.java b/patches/minecraft/net/minecraft/block/BlockCake.edit.java index 1e2e537..1525bd7 100644 --- a/patches/minecraft/net/minecraft/block/BlockCake.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockCake.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockCarpet.edit.java b/patches/minecraft/net/minecraft/block/BlockCarpet.edit.java index 13d1c06..17e66f0 100644 --- a/patches/minecraft/net/minecraft/block/BlockCarpet.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockCarpet.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockCarrot.edit.java b/patches/minecraft/net/minecraft/block/BlockCarrot.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/block/BlockCarrot.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockCarrot.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockCauldron.edit.java b/patches/minecraft/net/minecraft/block/BlockCauldron.edit.java index 650add7..ca12624 100644 --- a/patches/minecraft/net/minecraft/block/BlockCauldron.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockCauldron.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockChest.edit.java b/patches/minecraft/net/minecraft/block/BlockChest.edit.java index 8bc12b0..e451b6e 100644 --- a/patches/minecraft/net/minecraft/block/BlockChest.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockChest.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockClay.edit.java b/patches/minecraft/net/minecraft/block/BlockClay.edit.java index cfb9e06..f8caef1 100644 --- a/patches/minecraft/net/minecraft/block/BlockClay.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockClay.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockCocoa.edit.java b/patches/minecraft/net/minecraft/block/BlockCocoa.edit.java index 0f93a70..8bdfe42 100644 --- a/patches/minecraft/net/minecraft/block/BlockCocoa.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockCocoa.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockColored.edit.java b/patches/minecraft/net/minecraft/block/BlockColored.edit.java index 51a5901..b78c263 100644 --- a/patches/minecraft/net/minecraft/block/BlockColored.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockColored.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockCommandBlock.edit.java b/patches/minecraft/net/minecraft/block/BlockCommandBlock.edit.java index 9e65b1b..c8b4172 100644 --- a/patches/minecraft/net/minecraft/block/BlockCommandBlock.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockCommandBlock.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockCompressedPowered.edit.java b/patches/minecraft/net/minecraft/block/BlockCompressedPowered.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/block/BlockCompressedPowered.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockCompressedPowered.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockContainer.edit.java b/patches/minecraft/net/minecraft/block/BlockContainer.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/block/BlockContainer.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockContainer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockCrops.edit.java b/patches/minecraft/net/minecraft/block/BlockCrops.edit.java index 28e7197..88e55c6 100644 --- a/patches/minecraft/net/minecraft/block/BlockCrops.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockCrops.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockDaylightDetector.edit.java b/patches/minecraft/net/minecraft/block/BlockDaylightDetector.edit.java index 22b38ab..2731901 100644 --- a/patches/minecraft/net/minecraft/block/BlockDaylightDetector.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockDaylightDetector.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockDeadBush.edit.java b/patches/minecraft/net/minecraft/block/BlockDeadBush.edit.java index cb3af79..502158d 100644 --- a/patches/minecraft/net/minecraft/block/BlockDeadBush.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockDeadBush.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockDirectional.edit.java b/patches/minecraft/net/minecraft/block/BlockDirectional.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/block/BlockDirectional.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockDirectional.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockDirt.edit.java b/patches/minecraft/net/minecraft/block/BlockDirt.edit.java index be247c7..6b4b079 100644 --- a/patches/minecraft/net/minecraft/block/BlockDirt.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockDirt.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockDispenser.edit.java b/patches/minecraft/net/minecraft/block/BlockDispenser.edit.java index 5050903..f26a3ae 100644 --- a/patches/minecraft/net/minecraft/block/BlockDispenser.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockDispenser.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockDoor.edit.java b/patches/minecraft/net/minecraft/block/BlockDoor.edit.java index 2918a19..b9d4172 100644 --- a/patches/minecraft/net/minecraft/block/BlockDoor.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockDoor.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockDoublePlant.edit.java b/patches/minecraft/net/minecraft/block/BlockDoublePlant.edit.java index 5a497fa..1148b99 100644 --- a/patches/minecraft/net/minecraft/block/BlockDoublePlant.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockDoublePlant.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockDoubleStoneSlab.edit.java b/patches/minecraft/net/minecraft/block/BlockDoubleStoneSlab.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/block/BlockDoubleStoneSlab.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockDoubleStoneSlab.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockDoubleStoneSlabNew.edit.java b/patches/minecraft/net/minecraft/block/BlockDoubleStoneSlabNew.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/block/BlockDoubleStoneSlabNew.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockDoubleStoneSlabNew.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockDoubleWoodSlab.edit.java b/patches/minecraft/net/minecraft/block/BlockDoubleWoodSlab.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/block/BlockDoubleWoodSlab.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockDoubleWoodSlab.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockDragonEgg.edit.java b/patches/minecraft/net/minecraft/block/BlockDragonEgg.edit.java index 4fbd986..df9bd3e 100644 --- a/patches/minecraft/net/minecraft/block/BlockDragonEgg.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockDragonEgg.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockDropper.edit.java b/patches/minecraft/net/minecraft/block/BlockDropper.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/block/BlockDropper.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockDropper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockDynamicLiquid.edit.java b/patches/minecraft/net/minecraft/block/BlockDynamicLiquid.edit.java index 53fa48e..25f17ac 100644 --- a/patches/minecraft/net/minecraft/block/BlockDynamicLiquid.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockDynamicLiquid.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockEnchantmentTable.edit.java b/patches/minecraft/net/minecraft/block/BlockEnchantmentTable.edit.java index 286c939..24965d0 100644 --- a/patches/minecraft/net/minecraft/block/BlockEnchantmentTable.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockEnchantmentTable.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockEndPortal.edit.java b/patches/minecraft/net/minecraft/block/BlockEndPortal.edit.java index 0471034..c2c8499 100644 --- a/patches/minecraft/net/minecraft/block/BlockEndPortal.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockEndPortal.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockEndPortalFrame.edit.java b/patches/minecraft/net/minecraft/block/BlockEndPortalFrame.edit.java index 39007a9..ad2ede9 100644 --- a/patches/minecraft/net/minecraft/block/BlockEndPortalFrame.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockEndPortalFrame.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockEnderChest.edit.java b/patches/minecraft/net/minecraft/block/BlockEnderChest.edit.java index 4b754ba..bffaaf9 100644 --- a/patches/minecraft/net/minecraft/block/BlockEnderChest.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockEnderChest.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockEventData.edit.java b/patches/minecraft/net/minecraft/block/BlockEventData.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/block/BlockEventData.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockEventData.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockFalling.edit.java b/patches/minecraft/net/minecraft/block/BlockFalling.edit.java index a9ba20b..bb7c81c 100644 --- a/patches/minecraft/net/minecraft/block/BlockFalling.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockFalling.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockFarmland.edit.java b/patches/minecraft/net/minecraft/block/BlockFarmland.edit.java index 80c8da2..b59a6ac 100644 --- a/patches/minecraft/net/minecraft/block/BlockFarmland.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockFarmland.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockFence.edit.java b/patches/minecraft/net/minecraft/block/BlockFence.edit.java index 7ecd9a8..49457c1 100644 --- a/patches/minecraft/net/minecraft/block/BlockFence.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockFence.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockFenceGate.edit.java b/patches/minecraft/net/minecraft/block/BlockFenceGate.edit.java index 75c2154..384443a 100644 --- a/patches/minecraft/net/minecraft/block/BlockFenceGate.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockFenceGate.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockFire.edit.java b/patches/minecraft/net/minecraft/block/BlockFire.edit.java index 631f867..1d27f2d 100644 --- a/patches/minecraft/net/minecraft/block/BlockFire.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockFire.edit.java @@ -1,20 +1,29 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> DELETE 2 @ 2 : 3 - -> CHANGE 1 : 5 @ 1 : 4 +> CHANGE 2 : 7 @ 2 : 7 ~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; ~ -~ import com.google.common.collect.Maps; +~ import com.carrotsearch.hppc.ObjectIntIdentityHashMap; +~ import com.carrotsearch.hppc.ObjectIntMap; ~ -> CHANGE 118 : 119 @ 118 : 119 +> CHANGE 26 : 28 @ 26 : 28 + +~ private final ObjectIntMap encouragements = new ObjectIntIdentityHashMap<>(); +~ private final ObjectIntMap flammabilities = new ObjectIntIdentityHashMap<>(); + +> CHANGE 74 : 76 @ 74 : 76 + +~ this.encouragements.put(blockIn, encouragement); +~ this.flammabilities.put(blockIn, flammability); + +> CHANGE 14 : 15 @ 14 : 15 ~ public int quantityDropped(EaglercraftRandom var1) { @@ -22,21 +31,45 @@ ~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { -> CHANGE 106 : 107 @ 106 : 107 +> INSERT 59 : 61 @ 59 + ++ if (!world.isBlockLoaded(blockpos1)) ++ continue; + +> CHANGE 38 : 39 @ 38 : 40 + +~ return this.flammabilities.getOrDefault(blockIn, 0); + +> CHANGE 3 : 4 @ 3 : 5 + +~ return this.encouragements.getOrDefault(blockIn, 0); + +> CHANGE 2 : 8 @ 2 : 4 ~ private void catchOnFire(World worldIn, BlockPos pos, int chance, EaglercraftRandom random, int age) { +~ IBlockState iblockstate = worldIn.getBlockStateIfLoaded(pos); +~ if (iblockstate == null) { +~ return; +~ } +~ int i = this.getFlammability(iblockstate.getBlock()); -> CHANGE 23 : 26 @ 23 : 24 +> DELETE 1 @ 1 : 2 + +> CHANGE 20 : 23 @ 20 : 21 ~ EnumFacing[] facings = EnumFacing._VALUES; ~ for (int i = 0; i < facings.length; ++i) { ~ EnumFacing enumfacing = facings[i]; -> CHANGE 14 : 17 @ 14 : 16 +> CHANGE 14 : 21 @ 14 : 16 ~ EnumFacing[] facings = EnumFacing._VALUES; +~ BlockPos tmp = new BlockPos(0, 0, 0); ~ for (int j = 0; j < facings.length; ++j) { -~ i = Math.max(this.getEncouragement(worldIn.getBlockState(pos.offset(facings[j])).getBlock()), i); +~ IBlockState type = worldIn.getBlockStateIfLoaded(pos.offsetEvenFaster(facings[j], tmp)); +~ if (type != null) { +~ i = Math.max(this.getEncouragement(type.getBlock()), i); +~ } > CHANGE 37 : 38 @ 37 : 38 diff --git a/patches/minecraft/net/minecraft/block/BlockFlower.edit.java b/patches/minecraft/net/minecraft/block/BlockFlower.edit.java index 2e4ad4e..451ebd0 100644 --- a/patches/minecraft/net/minecraft/block/BlockFlower.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockFlower.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockFlowerPot.edit.java b/patches/minecraft/net/minecraft/block/BlockFlowerPot.edit.java index c09e195..0c61545 100644 --- a/patches/minecraft/net/minecraft/block/BlockFlowerPot.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockFlowerPot.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockFurnace.edit.java b/patches/minecraft/net/minecraft/block/BlockFurnace.edit.java index b636cba..8a40a96 100644 --- a/patches/minecraft/net/minecraft/block/BlockFurnace.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockFurnace.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockGlass.edit.java b/patches/minecraft/net/minecraft/block/BlockGlass.edit.java index 4d887ea..8f1d375 100644 --- a/patches/minecraft/net/minecraft/block/BlockGlass.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockGlass.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockGlowstone.edit.java b/patches/minecraft/net/minecraft/block/BlockGlowstone.edit.java index 85f0cff..cff7ae1 100644 --- a/patches/minecraft/net/minecraft/block/BlockGlowstone.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockGlowstone.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockGrass.edit.java b/patches/minecraft/net/minecraft/block/BlockGrass.edit.java index 9d9f4eb..490924d 100644 --- a/patches/minecraft/net/minecraft/block/BlockGrass.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockGrass.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockGravel.edit.java b/patches/minecraft/net/minecraft/block/BlockGravel.edit.java index b5e56a8..9d4f3b9 100644 --- a/patches/minecraft/net/minecraft/block/BlockGravel.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockGravel.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockHalfStoneSlab.edit.java b/patches/minecraft/net/minecraft/block/BlockHalfStoneSlab.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/block/BlockHalfStoneSlab.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockHalfStoneSlab.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockHalfStoneSlabNew.edit.java b/patches/minecraft/net/minecraft/block/BlockHalfStoneSlabNew.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/block/BlockHalfStoneSlabNew.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockHalfStoneSlabNew.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockHalfWoodSlab.edit.java b/patches/minecraft/net/minecraft/block/BlockHalfWoodSlab.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/block/BlockHalfWoodSlab.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockHalfWoodSlab.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockHardenedClay.edit.java b/patches/minecraft/net/minecraft/block/BlockHardenedClay.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/block/BlockHardenedClay.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockHardenedClay.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockHay.edit.java b/patches/minecraft/net/minecraft/block/BlockHay.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/block/BlockHay.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockHay.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockHopper.edit.java b/patches/minecraft/net/minecraft/block/BlockHopper.edit.java index 15f8ee7..809eeb4 100644 --- a/patches/minecraft/net/minecraft/block/BlockHopper.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockHopper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockHugeMushroom.edit.java b/patches/minecraft/net/minecraft/block/BlockHugeMushroom.edit.java index bff23d7..bfaa944 100644 --- a/patches/minecraft/net/minecraft/block/BlockHugeMushroom.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockHugeMushroom.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockIce.edit.java b/patches/minecraft/net/minecraft/block/BlockIce.edit.java index 1883e4b..84aa0da 100644 --- a/patches/minecraft/net/minecraft/block/BlockIce.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockIce.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockJukebox.edit.java b/patches/minecraft/net/minecraft/block/BlockJukebox.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/block/BlockJukebox.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockJukebox.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockLadder.edit.java b/patches/minecraft/net/minecraft/block/BlockLadder.edit.java index 6626424..d40c37a 100644 --- a/patches/minecraft/net/minecraft/block/BlockLadder.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockLadder.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockLeaves.edit.java b/patches/minecraft/net/minecraft/block/BlockLeaves.edit.java index 93793f8..1704374 100644 --- a/patches/minecraft/net/minecraft/block/BlockLeaves.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockLeaves.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockLeavesBase.edit.java b/patches/minecraft/net/minecraft/block/BlockLeavesBase.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/block/BlockLeavesBase.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockLeavesBase.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockLever.edit.java b/patches/minecraft/net/minecraft/block/BlockLever.edit.java index 5365ca5..245d45d 100644 --- a/patches/minecraft/net/minecraft/block/BlockLever.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockLever.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockLilyPad.edit.java b/patches/minecraft/net/minecraft/block/BlockLilyPad.edit.java index 0e799ee..fead192 100644 --- a/patches/minecraft/net/minecraft/block/BlockLilyPad.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockLilyPad.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockLiquid.edit.java b/patches/minecraft/net/minecraft/block/BlockLiquid.edit.java index 9f5af98..7427612 100644 --- a/patches/minecraft/net/minecraft/block/BlockLiquid.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockLiquid.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockLog.edit.java b/patches/minecraft/net/minecraft/block/BlockLog.edit.java index 23e2dab..46ee499 100644 --- a/patches/minecraft/net/minecraft/block/BlockLog.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockLog.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockMelon.edit.java b/patches/minecraft/net/minecraft/block/BlockMelon.edit.java index b817364..a407677 100644 --- a/patches/minecraft/net/minecraft/block/BlockMelon.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockMelon.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockMobSpawner.edit.java b/patches/minecraft/net/minecraft/block/BlockMobSpawner.edit.java index e4e8349..d4ed900 100644 --- a/patches/minecraft/net/minecraft/block/BlockMobSpawner.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockMobSpawner.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockMushroom.edit.java b/patches/minecraft/net/minecraft/block/BlockMushroom.edit.java index b2f97cb..d9e3743 100644 --- a/patches/minecraft/net/minecraft/block/BlockMushroom.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockMushroom.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockMycelium.edit.java b/patches/minecraft/net/minecraft/block/BlockMycelium.edit.java index d5830f9..fbfc3e5 100644 --- a/patches/minecraft/net/minecraft/block/BlockMycelium.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockMycelium.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockNetherBrick.edit.java b/patches/minecraft/net/minecraft/block/BlockNetherBrick.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/block/BlockNetherBrick.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockNetherBrick.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockNetherWart.edit.java b/patches/minecraft/net/minecraft/block/BlockNetherWart.edit.java index 7d8eaaf..8316a05 100644 --- a/patches/minecraft/net/minecraft/block/BlockNetherWart.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockNetherWart.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockNetherrack.edit.java b/patches/minecraft/net/minecraft/block/BlockNetherrack.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/block/BlockNetherrack.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockNetherrack.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockNewLeaf.edit.java b/patches/minecraft/net/minecraft/block/BlockNewLeaf.edit.java index ad556c5..728ea35 100644 --- a/patches/minecraft/net/minecraft/block/BlockNewLeaf.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockNewLeaf.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockNewLog.edit.java b/patches/minecraft/net/minecraft/block/BlockNewLog.edit.java index 13a8f45..0766fe4 100644 --- a/patches/minecraft/net/minecraft/block/BlockNewLog.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockNewLog.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockNote.edit.java b/patches/minecraft/net/minecraft/block/BlockNote.edit.java index 01a6d9d..eb8b548 100644 --- a/patches/minecraft/net/minecraft/block/BlockNote.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockNote.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockObsidian.edit.java b/patches/minecraft/net/minecraft/block/BlockObsidian.edit.java index 5ea1da3..a357b86 100644 --- a/patches/minecraft/net/minecraft/block/BlockObsidian.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockObsidian.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockOldLeaf.edit.java b/patches/minecraft/net/minecraft/block/BlockOldLeaf.edit.java index 9ff6516..987ed98 100644 --- a/patches/minecraft/net/minecraft/block/BlockOldLeaf.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockOldLeaf.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockOldLog.edit.java b/patches/minecraft/net/minecraft/block/BlockOldLog.edit.java index 8ccb229..fa3ba4d 100644 --- a/patches/minecraft/net/minecraft/block/BlockOldLog.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockOldLog.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockOre.edit.java b/patches/minecraft/net/minecraft/block/BlockOre.edit.java index a00a59b..8c0eb2c 100644 --- a/patches/minecraft/net/minecraft/block/BlockOre.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockOre.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockPackedIce.edit.java b/patches/minecraft/net/minecraft/block/BlockPackedIce.edit.java index 4a3f384..dac0fc1 100644 --- a/patches/minecraft/net/minecraft/block/BlockPackedIce.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockPackedIce.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockPane.edit.java b/patches/minecraft/net/minecraft/block/BlockPane.edit.java index b4e186f..d2636f0 100644 --- a/patches/minecraft/net/minecraft/block/BlockPane.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockPane.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockPistonBase.edit.java b/patches/minecraft/net/minecraft/block/BlockPistonBase.edit.java index 6030254..6c32818 100644 --- a/patches/minecraft/net/minecraft/block/BlockPistonBase.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockPistonBase.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockPistonExtension.edit.java b/patches/minecraft/net/minecraft/block/BlockPistonExtension.edit.java index d29ea37..ecc95c4 100644 --- a/patches/minecraft/net/minecraft/block/BlockPistonExtension.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockPistonExtension.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockPistonMoving.edit.java b/patches/minecraft/net/minecraft/block/BlockPistonMoving.edit.java index f28bb77..a57aaed 100644 --- a/patches/minecraft/net/minecraft/block/BlockPistonMoving.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockPistonMoving.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockPlanks.edit.java b/patches/minecraft/net/minecraft/block/BlockPlanks.edit.java index d88e7c2..ae90d93 100644 --- a/patches/minecraft/net/minecraft/block/BlockPlanks.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockPlanks.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockPortal.edit.java b/patches/minecraft/net/minecraft/block/BlockPortal.edit.java index bf73c06..0d624cb 100644 --- a/patches/minecraft/net/minecraft/block/BlockPortal.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockPortal.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockPotato.edit.java b/patches/minecraft/net/minecraft/block/BlockPotato.edit.java index 2294a1c..3b31c2d 100644 --- a/patches/minecraft/net/minecraft/block/BlockPotato.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockPotato.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockPressurePlate.edit.java b/patches/minecraft/net/minecraft/block/BlockPressurePlate.edit.java index 584a786..172357e 100644 --- a/patches/minecraft/net/minecraft/block/BlockPressurePlate.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockPressurePlate.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockPressurePlateWeighted.edit.java b/patches/minecraft/net/minecraft/block/BlockPressurePlateWeighted.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/block/BlockPressurePlateWeighted.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockPressurePlateWeighted.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockPrismarine.edit.java b/patches/minecraft/net/minecraft/block/BlockPrismarine.edit.java index 26609f7..dbee245 100644 --- a/patches/minecraft/net/minecraft/block/BlockPrismarine.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockPrismarine.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockPumpkin.edit.java b/patches/minecraft/net/minecraft/block/BlockPumpkin.edit.java index bfbb2b5..8262c3c 100644 --- a/patches/minecraft/net/minecraft/block/BlockPumpkin.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockPumpkin.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockQuartz.edit.java b/patches/minecraft/net/minecraft/block/BlockQuartz.edit.java index 3693676..e50762c 100644 --- a/patches/minecraft/net/minecraft/block/BlockQuartz.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockQuartz.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockRail.edit.java b/patches/minecraft/net/minecraft/block/BlockRail.edit.java index feb0f66..204b8bc 100644 --- a/patches/minecraft/net/minecraft/block/BlockRail.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockRail.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockRailBase.edit.java b/patches/minecraft/net/minecraft/block/BlockRailBase.edit.java index 7f1a219..3879bb5 100644 --- a/patches/minecraft/net/minecraft/block/BlockRailBase.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockRailBase.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockRailDetector.edit.java b/patches/minecraft/net/minecraft/block/BlockRailDetector.edit.java index ea7f17d..cd334d8 100644 --- a/patches/minecraft/net/minecraft/block/BlockRailDetector.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockRailDetector.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockRailPowered.edit.java b/patches/minecraft/net/minecraft/block/BlockRailPowered.edit.java index 19d3812..b200db5 100644 --- a/patches/minecraft/net/minecraft/block/BlockRailPowered.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockRailPowered.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockRedFlower.edit.java b/patches/minecraft/net/minecraft/block/BlockRedFlower.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/block/BlockRedFlower.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockRedFlower.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockRedSandstone.edit.java b/patches/minecraft/net/minecraft/block/BlockRedSandstone.edit.java index 008af82..a90bf9d 100644 --- a/patches/minecraft/net/minecraft/block/BlockRedSandstone.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockRedSandstone.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockRedstoneComparator.edit.java b/patches/minecraft/net/minecraft/block/BlockRedstoneComparator.edit.java index 5bb150c..3e401a2 100644 --- a/patches/minecraft/net/minecraft/block/BlockRedstoneComparator.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockRedstoneComparator.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockRedstoneDiode.edit.java b/patches/minecraft/net/minecraft/block/BlockRedstoneDiode.edit.java index 4248747..665713b 100644 --- a/patches/minecraft/net/minecraft/block/BlockRedstoneDiode.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockRedstoneDiode.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockRedstoneLight.edit.java b/patches/minecraft/net/minecraft/block/BlockRedstoneLight.edit.java index 6982aa7..6c36984 100644 --- a/patches/minecraft/net/minecraft/block/BlockRedstoneLight.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockRedstoneLight.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockRedstoneOre.edit.java b/patches/minecraft/net/minecraft/block/BlockRedstoneOre.edit.java index 2c0cbe6..f71ecd9 100644 --- a/patches/minecraft/net/minecraft/block/BlockRedstoneOre.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockRedstoneOre.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockRedstoneRepeater.edit.java b/patches/minecraft/net/minecraft/block/BlockRedstoneRepeater.edit.java index 8f21ba0..630bbca 100644 --- a/patches/minecraft/net/minecraft/block/BlockRedstoneRepeater.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockRedstoneRepeater.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockRedstoneTorch.edit.java b/patches/minecraft/net/minecraft/block/BlockRedstoneTorch.edit.java index 13b2d89..5179dc4 100644 --- a/patches/minecraft/net/minecraft/block/BlockRedstoneTorch.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockRedstoneTorch.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude @@ -37,7 +37,22 @@ ~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { -> CHANGE 48 : 49 @ 48 : 49 +> CHANGE 1 : 2 @ 1 : 2 + +~ List list = toggles.get(world); + +> CHANGE 1 : 9 @ 1 : 4 + +~ if (list != null) { +~ int index = 0; +~ while (index < list.size() && world.getTotalWorldTime() - list.get(index).time > 60L) { +~ index++; +~ } +~ if (index > 0) { +~ list.subList(0, index).clear(); +~ } + +> CHANGE 42 : 43 @ 42 : 43 ~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { diff --git a/patches/minecraft/net/minecraft/block/BlockRedstoneWire.edit.java b/patches/minecraft/net/minecraft/block/BlockRedstoneWire.edit.java index c0e3dec..174038f 100644 --- a/patches/minecraft/net/minecraft/block/BlockRedstoneWire.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockRedstoneWire.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockReed.edit.java b/patches/minecraft/net/minecraft/block/BlockReed.edit.java index e52db33..5ececff 100644 --- a/patches/minecraft/net/minecraft/block/BlockReed.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockReed.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockRotatedPillar.edit.java b/patches/minecraft/net/minecraft/block/BlockRotatedPillar.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/block/BlockRotatedPillar.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockRotatedPillar.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockSand.edit.java b/patches/minecraft/net/minecraft/block/BlockSand.edit.java index 683c02b..0a801af 100644 --- a/patches/minecraft/net/minecraft/block/BlockSand.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockSand.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockSandStone.edit.java b/patches/minecraft/net/minecraft/block/BlockSandStone.edit.java index 125ca6d..cd0d7f2 100644 --- a/patches/minecraft/net/minecraft/block/BlockSandStone.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockSandStone.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockSapling.edit.java b/patches/minecraft/net/minecraft/block/BlockSapling.edit.java index 552fb71..58715a4 100644 --- a/patches/minecraft/net/minecraft/block/BlockSapling.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockSapling.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockSeaLantern.edit.java b/patches/minecraft/net/minecraft/block/BlockSeaLantern.edit.java index fc6d7ac..eac0865 100644 --- a/patches/minecraft/net/minecraft/block/BlockSeaLantern.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockSeaLantern.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockSign.edit.java b/patches/minecraft/net/minecraft/block/BlockSign.edit.java index b453f80..e080f11 100644 --- a/patches/minecraft/net/minecraft/block/BlockSign.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockSign.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockSilverfish.edit.java b/patches/minecraft/net/minecraft/block/BlockSilverfish.edit.java index a52925c..409ce03 100644 --- a/patches/minecraft/net/minecraft/block/BlockSilverfish.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockSilverfish.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockSkull.edit.java b/patches/minecraft/net/minecraft/block/BlockSkull.edit.java index 11c3879..f462296 100644 --- a/patches/minecraft/net/minecraft/block/BlockSkull.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockSkull.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockSlab.edit.java b/patches/minecraft/net/minecraft/block/BlockSlab.edit.java index fab609f..a4fdd19 100644 --- a/patches/minecraft/net/minecraft/block/BlockSlab.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockSlab.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockSlime.edit.java b/patches/minecraft/net/minecraft/block/BlockSlime.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/block/BlockSlime.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockSlime.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockSnow.edit.java b/patches/minecraft/net/minecraft/block/BlockSnow.edit.java index 86db22f..f068fb2 100644 --- a/patches/minecraft/net/minecraft/block/BlockSnow.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockSnow.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockSnowBlock.edit.java b/patches/minecraft/net/minecraft/block/BlockSnowBlock.edit.java index c90abb3..3a0f4cb 100644 --- a/patches/minecraft/net/minecraft/block/BlockSnowBlock.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockSnowBlock.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockSoulSand.edit.java b/patches/minecraft/net/minecraft/block/BlockSoulSand.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/block/BlockSoulSand.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockSoulSand.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockSponge.edit.java b/patches/minecraft/net/minecraft/block/BlockSponge.edit.java index 49595da..0f9b3cf 100644 --- a/patches/minecraft/net/minecraft/block/BlockSponge.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockSponge.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockStainedGlass.edit.java b/patches/minecraft/net/minecraft/block/BlockStainedGlass.edit.java index 99d9a29..21914ce 100644 --- a/patches/minecraft/net/minecraft/block/BlockStainedGlass.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockStainedGlass.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockStainedGlassPane.edit.java b/patches/minecraft/net/minecraft/block/BlockStainedGlassPane.edit.java index 1560eb1..4cd239c 100644 --- a/patches/minecraft/net/minecraft/block/BlockStainedGlassPane.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockStainedGlassPane.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockStairs.edit.java b/patches/minecraft/net/minecraft/block/BlockStairs.edit.java index e14a8af..da3726b 100644 --- a/patches/minecraft/net/minecraft/block/BlockStairs.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockStairs.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockStandingSign.edit.java b/patches/minecraft/net/minecraft/block/BlockStandingSign.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/block/BlockStandingSign.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockStandingSign.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockStaticLiquid.edit.java b/patches/minecraft/net/minecraft/block/BlockStaticLiquid.edit.java index 5cd0ea8..74bfded 100644 --- a/patches/minecraft/net/minecraft/block/BlockStaticLiquid.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockStaticLiquid.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockStem.edit.java b/patches/minecraft/net/minecraft/block/BlockStem.edit.java index b8d3015..49bc577 100644 --- a/patches/minecraft/net/minecraft/block/BlockStem.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockStem.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockStone.edit.java b/patches/minecraft/net/minecraft/block/BlockStone.edit.java index f4a1838..b34d32c 100644 --- a/patches/minecraft/net/minecraft/block/BlockStone.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockStone.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockStoneBrick.edit.java b/patches/minecraft/net/minecraft/block/BlockStoneBrick.edit.java index 84e82cd..bbeb528 100644 --- a/patches/minecraft/net/minecraft/block/BlockStoneBrick.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockStoneBrick.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockStoneSlab.edit.java b/patches/minecraft/net/minecraft/block/BlockStoneSlab.edit.java index 41ad16e..5d356f3 100644 --- a/patches/minecraft/net/minecraft/block/BlockStoneSlab.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockStoneSlab.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockStoneSlabNew.edit.java b/patches/minecraft/net/minecraft/block/BlockStoneSlabNew.edit.java index 63bca25..3075474 100644 --- a/patches/minecraft/net/minecraft/block/BlockStoneSlabNew.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockStoneSlabNew.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockTNT.edit.java b/patches/minecraft/net/minecraft/block/BlockTNT.edit.java index 876bc6f..aeece2b 100644 --- a/patches/minecraft/net/minecraft/block/BlockTNT.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockTNT.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockTallGrass.edit.java b/patches/minecraft/net/minecraft/block/BlockTallGrass.edit.java index efed5f4..c5d1479 100644 --- a/patches/minecraft/net/minecraft/block/BlockTallGrass.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockTallGrass.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockTorch.edit.java b/patches/minecraft/net/minecraft/block/BlockTorch.edit.java index 21042f5..493cc59 100644 --- a/patches/minecraft/net/minecraft/block/BlockTorch.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockTorch.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockTrapDoor.edit.java b/patches/minecraft/net/minecraft/block/BlockTrapDoor.edit.java index 57a23f7..896e297 100644 --- a/patches/minecraft/net/minecraft/block/BlockTrapDoor.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockTrapDoor.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockTripWire.edit.java b/patches/minecraft/net/minecraft/block/BlockTripWire.edit.java index 39bf79e..190a8b2 100644 --- a/patches/minecraft/net/minecraft/block/BlockTripWire.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockTripWire.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockTripWireHook.edit.java b/patches/minecraft/net/minecraft/block/BlockTripWireHook.edit.java index db3af33..236e26f 100644 --- a/patches/minecraft/net/minecraft/block/BlockTripWireHook.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockTripWireHook.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockVine.edit.java b/patches/minecraft/net/minecraft/block/BlockVine.edit.java index 5259037..c2693e5 100644 --- a/patches/minecraft/net/minecraft/block/BlockVine.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockVine.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockWall.edit.java b/patches/minecraft/net/minecraft/block/BlockWall.edit.java index 242590e..3f53fab 100644 --- a/patches/minecraft/net/minecraft/block/BlockWall.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockWall.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockWallSign.edit.java b/patches/minecraft/net/minecraft/block/BlockWallSign.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/block/BlockWallSign.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockWallSign.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockWeb.edit.java b/patches/minecraft/net/minecraft/block/BlockWeb.edit.java index 856ba94..8f85e33 100644 --- a/patches/minecraft/net/minecraft/block/BlockWeb.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockWeb.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockWoodSlab.edit.java b/patches/minecraft/net/minecraft/block/BlockWoodSlab.edit.java index ba6306c..dbf6c3b 100644 --- a/patches/minecraft/net/minecraft/block/BlockWoodSlab.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockWoodSlab.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockWorkbench.edit.java b/patches/minecraft/net/minecraft/block/BlockWorkbench.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/block/BlockWorkbench.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockWorkbench.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/BlockYellowFlower.edit.java b/patches/minecraft/net/minecraft/block/BlockYellowFlower.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/block/BlockYellowFlower.edit.java +++ b/patches/minecraft/net/minecraft/block/BlockYellowFlower.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/IGrowable.edit.java b/patches/minecraft/net/minecraft/block/IGrowable.edit.java index 843bd17..c9a372e 100644 --- a/patches/minecraft/net/minecraft/block/IGrowable.edit.java +++ b/patches/minecraft/net/minecraft/block/IGrowable.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/material/Material.edit.java b/patches/minecraft/net/minecraft/block/material/Material.edit.java index 818a6dc..84b45d1 100644 --- a/patches/minecraft/net/minecraft/block/material/Material.edit.java +++ b/patches/minecraft/net/minecraft/block/material/Material.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/material/MaterialLiquid.edit.java b/patches/minecraft/net/minecraft/block/material/MaterialLiquid.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/block/material/MaterialLiquid.edit.java +++ b/patches/minecraft/net/minecraft/block/material/MaterialLiquid.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/material/MaterialLogic.edit.java b/patches/minecraft/net/minecraft/block/material/MaterialLogic.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/block/material/MaterialLogic.edit.java +++ b/patches/minecraft/net/minecraft/block/material/MaterialLogic.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/material/MaterialPortal.edit.java b/patches/minecraft/net/minecraft/block/material/MaterialPortal.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/block/material/MaterialPortal.edit.java +++ b/patches/minecraft/net/minecraft/block/material/MaterialPortal.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/material/MaterialTransparent.edit.java b/patches/minecraft/net/minecraft/block/material/MaterialTransparent.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/block/material/MaterialTransparent.edit.java +++ b/patches/minecraft/net/minecraft/block/material/MaterialTransparent.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/properties/IProperty.edit.java b/patches/minecraft/net/minecraft/block/properties/IProperty.edit.java index b0fab8b..e1bb18c 100644 --- a/patches/minecraft/net/minecraft/block/properties/IProperty.edit.java +++ b/patches/minecraft/net/minecraft/block/properties/IProperty.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/properties/PropertyBool.edit.java b/patches/minecraft/net/minecraft/block/properties/PropertyBool.edit.java index 0ff7e8c..aac8d85 100644 --- a/patches/minecraft/net/minecraft/block/properties/PropertyBool.edit.java +++ b/patches/minecraft/net/minecraft/block/properties/PropertyBool.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/properties/PropertyDirection.edit.java b/patches/minecraft/net/minecraft/block/properties/PropertyDirection.edit.java index 5b510d6..b43b4b4 100644 --- a/patches/minecraft/net/minecraft/block/properties/PropertyDirection.edit.java +++ b/patches/minecraft/net/minecraft/block/properties/PropertyDirection.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/properties/PropertyEnum.edit.java b/patches/minecraft/net/minecraft/block/properties/PropertyEnum.edit.java index c35b067..dccd00c 100644 --- a/patches/minecraft/net/minecraft/block/properties/PropertyEnum.edit.java +++ b/patches/minecraft/net/minecraft/block/properties/PropertyEnum.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/properties/PropertyHelper.edit.java b/patches/minecraft/net/minecraft/block/properties/PropertyHelper.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/block/properties/PropertyHelper.edit.java +++ b/patches/minecraft/net/minecraft/block/properties/PropertyHelper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/properties/PropertyInteger.edit.java b/patches/minecraft/net/minecraft/block/properties/PropertyInteger.edit.java index c3cd0fd..9243484 100644 --- a/patches/minecraft/net/minecraft/block/properties/PropertyInteger.edit.java +++ b/patches/minecraft/net/minecraft/block/properties/PropertyInteger.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/state/BlockPistonStructureHelper.edit.java b/patches/minecraft/net/minecraft/block/state/BlockPistonStructureHelper.edit.java index 156351f..cb67246 100644 --- a/patches/minecraft/net/minecraft/block/state/BlockPistonStructureHelper.edit.java +++ b/patches/minecraft/net/minecraft/block/state/BlockPistonStructureHelper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/state/BlockState.edit.java b/patches/minecraft/net/minecraft/block/state/BlockState.edit.java index cd6012b..9934cda 100644 --- a/patches/minecraft/net/minecraft/block/state/BlockState.edit.java +++ b/patches/minecraft/net/minecraft/block/state/BlockState.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/state/BlockStateBase.edit.java b/patches/minecraft/net/minecraft/block/state/BlockStateBase.edit.java index be450cd..18983b4 100644 --- a/patches/minecraft/net/minecraft/block/state/BlockStateBase.edit.java +++ b/patches/minecraft/net/minecraft/block/state/BlockStateBase.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/state/BlockWorldState.edit.java b/patches/minecraft/net/minecraft/block/state/BlockWorldState.edit.java index bfbb2b5..8262c3c 100644 --- a/patches/minecraft/net/minecraft/block/state/BlockWorldState.edit.java +++ b/patches/minecraft/net/minecraft/block/state/BlockWorldState.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/state/IBlockState.edit.java b/patches/minecraft/net/minecraft/block/state/IBlockState.edit.java index d14e30f..4fe17a3 100644 --- a/patches/minecraft/net/minecraft/block/state/IBlockState.edit.java +++ b/patches/minecraft/net/minecraft/block/state/IBlockState.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/state/pattern/BlockHelper.edit.java b/patches/minecraft/net/minecraft/block/state/pattern/BlockHelper.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/block/state/pattern/BlockHelper.edit.java +++ b/patches/minecraft/net/minecraft/block/state/pattern/BlockHelper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/state/pattern/BlockPattern.edit.java b/patches/minecraft/net/minecraft/block/state/pattern/BlockPattern.edit.java index a9ac68a..04f15a0 100644 --- a/patches/minecraft/net/minecraft/block/state/pattern/BlockPattern.edit.java +++ b/patches/minecraft/net/minecraft/block/state/pattern/BlockPattern.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/state/pattern/BlockStateHelper.edit.java b/patches/minecraft/net/minecraft/block/state/pattern/BlockStateHelper.edit.java index 548f07f..9dbb190 100644 --- a/patches/minecraft/net/minecraft/block/state/pattern/BlockStateHelper.edit.java +++ b/patches/minecraft/net/minecraft/block/state/pattern/BlockStateHelper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/block/state/pattern/FactoryBlockPattern.edit.java b/patches/minecraft/net/minecraft/block/state/pattern/FactoryBlockPattern.edit.java index cdfe605..9254dc9 100644 --- a/patches/minecraft/net/minecraft/block/state/pattern/FactoryBlockPattern.edit.java +++ b/patches/minecraft/net/minecraft/block/state/pattern/FactoryBlockPattern.edit.java @@ -1,27 +1,36 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> INSERT 2 : 9 @ 2 +> INSERT 2 : 10 @ 2 + import java.util.ArrayList; + import java.util.List; -+ import java.util.Map; -+ import java.util.Map.Entry; + + import org.apache.commons.lang3.StringUtils; + ++ import com.carrotsearch.hppc.CharObjectHashMap; ++ import com.carrotsearch.hppc.CharObjectMap; ++ import com.carrotsearch.hppc.cursors.CharObjectCursor; -> CHANGE 5 : 6 @ 5 : 10 +> CHANGE 4 : 5 @ 4 : 10 ~ > DELETE 1 @ 1 : 4 -> CHANGE 13 : 14 @ 13 : 14 +> CHANGE 4 : 5 @ 4 : 5 + +~ private final CharObjectMap> symbolMap = new CharObjectHashMap<>(); + +> CHANGE 4 : 5 @ 4 : 5 + +~ this.symbolMap.put(' ', Predicates.alwaysTrue()); + +> CHANGE 3 : 4 @ 3 : 4 ~ if (!(aisle == null || aisle.length <= 0) && !StringUtils.isEmpty(aisle[0])) { @@ -30,18 +39,30 @@ ~ for (int i = 0; i < aisle.length; ++i) { ~ String s = aisle[i]; -> CHANGE 6 : 9 @ 6 : 7 +> CHANGE 6 : 11 @ 6 : 9 ~ char[] achar = s.toCharArray(); ~ for (int j = 0; j < achar.length; ++j) { ~ char c0 = achar[j]; +~ if (!this.symbolMap.containsKey(c0)) { +~ this.symbolMap.put(c0, null); -> CHANGE 1 : 2 @ 1 : 2 +> CHANGE 17 : 18 @ 17 : 18 -~ this.symbolMap.put(Character.valueOf(c0), null); +~ this.symbolMap.put(symbol, blockMatcher); -> CHANGE 27 : 28 @ 27 : 29 +> CHANGE 9 : 10 @ 9 : 11 ~ Predicate[][][] apredicate = new Predicate[this.depth.size()][this.aisleHeight][this.rowWidth]; +> CHANGE 4 : 5 @ 4 : 6 + +~ apredicate[i][j][k] = this.symbolMap.get(((String[]) this.depth.get(i))[j].charAt(k)); + +> CHANGE 10 : 13 @ 10 : 13 + +~ for (CharObjectCursor> entry : this.symbolMap) { +~ if (entry.value == null) { +~ arraylist.add(entry.key); + > EOF diff --git a/patches/minecraft/net/minecraft/client/ClientBrandRetriever.edit.java b/patches/minecraft/net/minecraft/client/ClientBrandRetriever.edit.java index 31000e9..6f9b333 100644 --- a/patches/minecraft/net/minecraft/client/ClientBrandRetriever.edit.java +++ b/patches/minecraft/net/minecraft/client/ClientBrandRetriever.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/LoadingScreenRenderer.edit.java b/patches/minecraft/net/minecraft/client/LoadingScreenRenderer.edit.java index 09a0072..fd2411c 100644 --- a/patches/minecraft/net/minecraft/client/LoadingScreenRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/LoadingScreenRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/Minecraft.edit.java b/patches/minecraft/net/minecraft/client/Minecraft.edit.java index affa45c..434b437 100644 --- a/patches/minecraft/net/minecraft/client/Minecraft.edit.java +++ b/patches/minecraft/net/minecraft/client/Minecraft.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/audio/GuardianSound.edit.java b/patches/minecraft/net/minecraft/client/audio/GuardianSound.edit.java index 32d8833..00ee707 100644 --- a/patches/minecraft/net/minecraft/client/audio/GuardianSound.edit.java +++ b/patches/minecraft/net/minecraft/client/audio/GuardianSound.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/audio/ISound.edit.java b/patches/minecraft/net/minecraft/client/audio/ISound.edit.java index 37072b7..f7fbcd0 100644 --- a/patches/minecraft/net/minecraft/client/audio/ISound.edit.java +++ b/patches/minecraft/net/minecraft/client/audio/ISound.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/audio/ITickableSound.edit.java b/patches/minecraft/net/minecraft/client/audio/ITickableSound.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/client/audio/ITickableSound.edit.java +++ b/patches/minecraft/net/minecraft/client/audio/ITickableSound.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/audio/MovingSound.edit.java b/patches/minecraft/net/minecraft/client/audio/MovingSound.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/audio/MovingSound.edit.java +++ b/patches/minecraft/net/minecraft/client/audio/MovingSound.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/audio/MovingSoundMinecart.edit.java b/patches/minecraft/net/minecraft/client/audio/MovingSoundMinecart.edit.java index 6b39a63..6308adb 100644 --- a/patches/minecraft/net/minecraft/client/audio/MovingSoundMinecart.edit.java +++ b/patches/minecraft/net/minecraft/client/audio/MovingSoundMinecart.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/audio/MovingSoundMinecartRiding.edit.java b/patches/minecraft/net/minecraft/client/audio/MovingSoundMinecartRiding.edit.java index 323c5a8..a2c4c57 100644 --- a/patches/minecraft/net/minecraft/client/audio/MovingSoundMinecartRiding.edit.java +++ b/patches/minecraft/net/minecraft/client/audio/MovingSoundMinecartRiding.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/audio/MusicTicker.edit.java b/patches/minecraft/net/minecraft/client/audio/MusicTicker.edit.java index 41d71f2..4ec190f 100644 --- a/patches/minecraft/net/minecraft/client/audio/MusicTicker.edit.java +++ b/patches/minecraft/net/minecraft/client/audio/MusicTicker.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/audio/PositionedSound.edit.java b/patches/minecraft/net/minecraft/client/audio/PositionedSound.edit.java index f486f12..1050601 100644 --- a/patches/minecraft/net/minecraft/client/audio/PositionedSound.edit.java +++ b/patches/minecraft/net/minecraft/client/audio/PositionedSound.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/audio/PositionedSoundRecord.edit.java b/patches/minecraft/net/minecraft/client/audio/PositionedSoundRecord.edit.java index a00ab40..9ef6517 100644 --- a/patches/minecraft/net/minecraft/client/audio/PositionedSoundRecord.edit.java +++ b/patches/minecraft/net/minecraft/client/audio/PositionedSoundRecord.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/audio/SoundCategory.edit.java b/patches/minecraft/net/minecraft/client/audio/SoundCategory.edit.java index 241cae8..d7d112b 100644 --- a/patches/minecraft/net/minecraft/client/audio/SoundCategory.edit.java +++ b/patches/minecraft/net/minecraft/client/audio/SoundCategory.edit.java @@ -1,14 +1,16 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 3 -> INSERT 2 : 4 @ 2 +> INSERT 2 : 6 @ 2 ++ import com.carrotsearch.hppc.IntObjectHashMap; ++ import com.carrotsearch.hppc.IntObjectMap; + import com.google.common.collect.Maps; + @@ -17,10 +19,22 @@ + public static final SoundCategory[] _VALUES = values(); + -> CHANGE 23 : 26 @ 23 : 24 +> CHANGE 1 : 2 @ 1 : 2 + +~ private static final IntObjectMap ID_CATEGORY_MAP = new IntObjectHashMap<>(); + +> CHANGE 21 : 24 @ 21 : 22 ~ SoundCategory[] categories = _VALUES; ~ for (int i = 0; i < categories.length; ++i) { ~ SoundCategory soundcategory = categories[i]; +> CHANGE 1 : 2 @ 1 : 2 + +~ || ID_CATEGORY_MAP.containsKey(soundcategory.getCategoryId())) { + +> CHANGE 4 : 5 @ 4 : 5 + +~ ID_CATEGORY_MAP.put(soundcategory.getCategoryId(), soundcategory); + > EOF diff --git a/patches/minecraft/net/minecraft/client/audio/SoundEventAccessor.edit.java b/patches/minecraft/net/minecraft/client/audio/SoundEventAccessor.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/client/audio/SoundEventAccessor.edit.java +++ b/patches/minecraft/net/minecraft/client/audio/SoundEventAccessor.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/audio/SoundEventAccessorComposite.edit.java b/patches/minecraft/net/minecraft/client/audio/SoundEventAccessorComposite.edit.java index 01bc933..f4e9818 100644 --- a/patches/minecraft/net/minecraft/client/audio/SoundEventAccessorComposite.edit.java +++ b/patches/minecraft/net/minecraft/client/audio/SoundEventAccessorComposite.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/audio/SoundHandler.edit.java b/patches/minecraft/net/minecraft/client/audio/SoundHandler.edit.java index c09c014..d73a2f3 100644 --- a/patches/minecraft/net/minecraft/client/audio/SoundHandler.edit.java +++ b/patches/minecraft/net/minecraft/client/audio/SoundHandler.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/audio/SoundList.edit.java b/patches/minecraft/net/minecraft/client/audio/SoundList.edit.java index f62bf1a..e590b1d 100644 --- a/patches/minecraft/net/minecraft/client/audio/SoundList.edit.java +++ b/patches/minecraft/net/minecraft/client/audio/SoundList.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/audio/SoundListSerializer.edit.java b/patches/minecraft/net/minecraft/client/audio/SoundListSerializer.edit.java index 19c4c3f..f750757 100644 --- a/patches/minecraft/net/minecraft/client/audio/SoundListSerializer.edit.java +++ b/patches/minecraft/net/minecraft/client/audio/SoundListSerializer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/audio/SoundRegistry.edit.java b/patches/minecraft/net/minecraft/client/audio/SoundRegistry.edit.java index edaa3fa..a5b8f74 100644 --- a/patches/minecraft/net/minecraft/client/audio/SoundRegistry.edit.java +++ b/patches/minecraft/net/minecraft/client/audio/SoundRegistry.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/entity/AbstractClientPlayer.edit.java b/patches/minecraft/net/minecraft/client/entity/AbstractClientPlayer.edit.java index 15fc7f5..b4406ab 100644 --- a/patches/minecraft/net/minecraft/client/entity/AbstractClientPlayer.edit.java +++ b/patches/minecraft/net/minecraft/client/entity/AbstractClientPlayer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/entity/EntityOtherPlayerMP.edit.java b/patches/minecraft/net/minecraft/client/entity/EntityOtherPlayerMP.edit.java index 73441d7..4120a98 100644 --- a/patches/minecraft/net/minecraft/client/entity/EntityOtherPlayerMP.edit.java +++ b/patches/minecraft/net/minecraft/client/entity/EntityOtherPlayerMP.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/entity/EntityPlayerSP.edit.java b/patches/minecraft/net/minecraft/client/entity/EntityPlayerSP.edit.java index b7cf567..fe69948 100644 --- a/patches/minecraft/net/minecraft/client/entity/EntityPlayerSP.edit.java +++ b/patches/minecraft/net/minecraft/client/entity/EntityPlayerSP.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/ChatLine.edit.java b/patches/minecraft/net/minecraft/client/gui/ChatLine.edit.java index 2a9236b..9406e36 100644 --- a/patches/minecraft/net/minecraft/client/gui/ChatLine.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/ChatLine.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/FontRenderer.edit.java b/patches/minecraft/net/minecraft/client/gui/FontRenderer.edit.java index 5569b47..3140f79 100644 --- a/patches/minecraft/net/minecraft/client/gui/FontRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/FontRenderer.edit.java @@ -1,14 +1,16 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 6 -> CHANGE 4 : 12 @ 4 : 6 +> CHANGE 4 : 14 @ 4 : 6 +~ +~ import net.lax1dude.eaglercraft.v1_8.EaglerBidiReorder; ~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; ~ ~ import net.lax1dude.eaglercraft.v1_8.HString; @@ -129,18 +131,7 @@ ~ tessellator.draw(); ~ -> CHANGE 27 : 35 @ 27 : 34 - -~ // try { -~ // Bidi bidi = new Bidi((new ArabicShaping(8)).shape(parString1), 127); -~ // bidi.setReorderingMode(0); -~ // return bidi.writeReordered(2); -~ // } catch (ArabicShapingException var3) { -~ // return parString1; -~ // } -~ return parString1; - -> CHANGE 2 : 3 @ 2 : 3 +> CHANGE 26 : 27 @ 26 : 37 ~ protected void resetStyles() { @@ -169,8 +160,9 @@ ~ private int renderStringAligned(String text, int x, int y, int wrapWidth, int color, boolean parFlag) { -> CHANGE 2 : 3 @ 2 : 3 +> CHANGE 1 : 3 @ 1 : 3 +~ int i = this.getStringWidth(EaglerBidiReorder.bidiReorder(text)); ~ x = x + wrapWidth - i; > CHANGE 2 : 3 @ 2 : 3 @@ -182,7 +174,11 @@ ~ this.posX = x; ~ this.posY = y; -> DELETE 21 @ 21 : 22 +> CHANGE 2 : 3 @ 2 : 3 + +~ text = EaglerBidiReorder.bidiReorder(text); + +> DELETE 18 @ 18 : 19 > INSERT 1 : 2 @ 1 diff --git a/patches/minecraft/net/minecraft/client/gui/Gui.edit.java b/patches/minecraft/net/minecraft/client/gui/Gui.edit.java index bcb9787..72c246e 100644 --- a/patches/minecraft/net/minecraft/client/gui/Gui.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/Gui.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiButton.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiButton.edit.java index 36d87be..f0bf092 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiButton.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiButton.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiButtonLanguage.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiButtonLanguage.edit.java index bc783e8..0976722 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiButtonLanguage.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiButtonLanguage.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiChat.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiChat.edit.java index d76204f..3680278 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiChat.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiChat.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiCommandBlock.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiCommandBlock.edit.java index 98576b0..13b38ab 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiCommandBlock.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiCommandBlock.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiConfirmOpenLink.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiConfirmOpenLink.edit.java index c8cad4b..3c90d9f 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiConfirmOpenLink.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiConfirmOpenLink.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiControls.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiControls.edit.java index 50dea76..47992b0 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiControls.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiControls.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiCreateFlatWorld.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiCreateFlatWorld.edit.java index 5cca10b..656361e 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiCreateFlatWorld.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiCreateFlatWorld.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiCreateWorld.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiCreateWorld.edit.java index a343ba9..883b75f 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiCreateWorld.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiCreateWorld.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiCustomizeSkin.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiCustomizeSkin.edit.java index 66f4b38..d04cbb4 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiCustomizeSkin.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiCustomizeSkin.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiCustomizeWorldScreen.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiCustomizeWorldScreen.edit.java index f49b59c..0b7006a 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiCustomizeWorldScreen.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiCustomizeWorldScreen.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiDisconnected.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiDisconnected.edit.java index 26c14cd..77d42fa 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiDisconnected.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiDisconnected.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiDownloadTerrain.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiDownloadTerrain.edit.java index 8ae7b8d..cbd05fc 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiDownloadTerrain.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiDownloadTerrain.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiEnchantment.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiEnchantment.edit.java index 600bbc6..5fa3a5d 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiEnchantment.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiEnchantment.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiErrorScreen.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiErrorScreen.edit.java index 7a8de1e..5717f21 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiErrorScreen.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiErrorScreen.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiFlatPresets.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiFlatPresets.edit.java index c0dfa35..f1da7be 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiFlatPresets.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiFlatPresets.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiGameOver.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiGameOver.edit.java index b489499..7a856db 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiGameOver.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiGameOver.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiHopper.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiHopper.edit.java index 8f2ebab..d1ae411 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiHopper.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiHopper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiIngame.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiIngame.edit.java index 3b33933..8fc5758 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiIngame.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiIngame.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiIngameMenu.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiIngameMenu.edit.java index 1ff8d28..d82ac0d 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiIngameMenu.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiIngameMenu.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiKeyBindingList.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiKeyBindingList.edit.java index 3ed0d13..a0774a6 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiKeyBindingList.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiKeyBindingList.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiLabel.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiLabel.edit.java index 8f997f1..4c86eb3 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiLabel.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiLabel.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiLanguage.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiLanguage.edit.java index 3ed88ce..ec0795d 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiLanguage.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiLanguage.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiListButton.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiListButton.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiListButton.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiListButton.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiListExtended.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiListExtended.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiListExtended.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiListExtended.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiLockIconButton.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiLockIconButton.edit.java index 20d8a9c..474ead8 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiLockIconButton.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiLockIconButton.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiMainMenu.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiMainMenu.edit.java index 713bced..b51c906 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiMainMenu.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiMainMenu.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiMemoryErrorScreen.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiMemoryErrorScreen.edit.java index 7605fe3..5faf451 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiMemoryErrorScreen.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiMemoryErrorScreen.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiMerchant.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiMerchant.edit.java index 2305f67..1b6ed4e 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiMerchant.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiMerchant.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiMultiplayer.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiMultiplayer.edit.java index f713d4b..bf3047a 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiMultiplayer.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiMultiplayer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiNewChat.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiNewChat.edit.java index f0c9e36..55aa572 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiNewChat.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiNewChat.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiOptionButton.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiOptionButton.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiOptionButton.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiOptionButton.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiOptionSlider.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiOptionSlider.edit.java index 7e726ab..fb7b6ce 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiOptionSlider.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiOptionSlider.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiOptions.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiOptions.edit.java index d81dd46..a0e8ea2 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiOptions.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiOptions.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiOptionsRowList.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiOptionsRowList.edit.java index a5f4796..64832aa 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiOptionsRowList.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiOptionsRowList.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiOverlayDebug.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiOverlayDebug.edit.java index 5888e29..435d1b3 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiOverlayDebug.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiOverlayDebug.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude @@ -9,22 +9,22 @@ ~ import java.text.SimpleDateFormat; -> INSERT 1 : 3 @ 1 +> INSERT 1 : 2 @ 1 + import java.util.Calendar; -+ import java.util.Iterator; > INSERT 1 : 2 @ 1 + import java.util.Locale; -> INSERT 1 : 17 @ 1 +> INSERT 1 : 18 @ 1 + + import org.apache.commons.lang3.StringUtils; + + import java.util.TimeZone; + ++ import com.carrotsearch.hppc.cursors.ObjectCursor; + import com.google.common.base.Strings; + import com.google.common.collect.Lists; + @@ -108,7 +108,7 @@ ~ } ~ } -> INSERT 2 : 140 @ 2 +> INSERT 2 : 137 @ 2 + private void drawFPS(int x, int y) { + this.fontRenderer.drawStringWithShadow(this.mc.renderGlobal.getDebugInfoShort(), x, y, 0xFFFFFF); @@ -153,28 +153,25 @@ + this.fontRenderer.drawStringWithShadow(line, x - lw, y - i, 0xFFFFFF); + i += 11; + -+ Iterator potions = mc.thePlayer.getActivePotionEffects().iterator(); -+ if (potions.hasNext()) { -+ while (potions.hasNext()) { -+ i += 11; -+ PotionEffect e = potions.next(); -+ int t = e.getDuration() / 20; -+ int m = t / 60; -+ int s = t % 60; -+ int j = e.getAmplifier(); -+ if (j > 0) { -+ line = I18n.format(e.getEffectName()) -+ + (j > 0 ? (" " + EnumChatFormatting.YELLOW + EnumChatFormatting.BOLD -+ + I18n.format("potion.potency." + j) + EnumChatFormatting.RESET) : "") -+ + " [" + EnumChatFormatting.YELLOW + HString.format("%02d:%02d", m, s) -+ + EnumChatFormatting.RESET + "]"; -+ } else { -+ line = I18n.format(e.getEffectName()) + " [" + EnumChatFormatting.YELLOW -+ + HString.format("%02d:%02d", m, s) + EnumChatFormatting.RESET + "]"; -+ } -+ lw = fontRenderer.getStringWidth(line); -+ this.fontRenderer.drawStringWithShadow(line, x - lw, y - i, 0xFFFFFF); ++ for (ObjectCursor ee : mc.thePlayer.getActivePotionEffects()) { ++ i += 11; ++ PotionEffect e = ee.value; ++ int t = e.getDuration() / 20; ++ int m = t / 60; ++ int s = t % 60; ++ int j = e.getAmplifier(); ++ if (j > 0) { ++ line = I18n.format(e.getEffectName()) ++ + (j > 0 ? (" " + EnumChatFormatting.YELLOW + EnumChatFormatting.BOLD ++ + I18n.format("potion.potency." + j) + EnumChatFormatting.RESET) : "") ++ + " [" + EnumChatFormatting.YELLOW + HString.format("%02d:%02d", m, s) ++ + EnumChatFormatting.RESET + "]"; ++ } else { ++ line = I18n.format(e.getEffectName()) + " [" + EnumChatFormatting.YELLOW ++ + HString.format("%02d:%02d", m, s) + EnumChatFormatting.RESET + "]"; + } ++ lw = fontRenderer.getStringWidth(line); ++ this.fontRenderer.drawStringWithShadow(line, x - lw, y - i, 0xFFFFFF); + } + + } @@ -350,7 +347,7 @@ ~ long j = EagRuntime.totalMemory(); ~ long k = EagRuntime.freeMemory(); ~ long l = j - k; -~ arraylist = Lists.newArrayList(new String[] { +~ arraylist = Lists.newArrayList(new String[] { "Platform: Desktop", ~ HString.format("Java: %s %dbit", ~ new Object[] { System.getProperty("java.version"), ~ Integer.valueOf(this.mc.isJava64bit() ? 64 : 32) }), @@ -365,12 +362,12 @@ ~ EaglercraftGPU.glGetString(7936) }), ~ EaglercraftGPU.glGetString(7937), EaglercraftGPU.glGetString(7938) }); ~ } else { -~ arraylist = Lists.newArrayList( -~ new String[] { "Java: TeaVM", "", HString.format("CPU: %s", new Object[] { "eaglercraft" }), "", -~ HString.format("Display: %dx%d (%s)", -~ new Object[] { Integer.valueOf(Display.getWidth()), -~ Integer.valueOf(Display.getHeight()), EaglercraftGPU.glGetString(7936) }), -~ EaglercraftGPU.glGetString(7937), EaglercraftGPU.glGetString(7938) }); +~ arraylist = Lists.newArrayList(new String[] { "Platform: " + EagRuntime.getPlatformType().getName(), +~ "Java: TeaVM", "", HString.format("CPU: %s", new Object[] { "eaglercraft" }), "", +~ HString.format("Display: %dx%d (%s)", +~ new Object[] { Integer.valueOf(Display.getWidth()), Integer.valueOf(Display.getHeight()), +~ EaglercraftGPU.glGetString(7936) }), +~ EaglercraftGPU.glGetString(7937), EaglercraftGPU.glGetString(7938) }); ~ } > DELETE 8 @ 8 : 12 diff --git a/patches/minecraft/net/minecraft/client/gui/GuiPageButtonList.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiPageButtonList.edit.java index d5c1e1c..fbab6ab 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiPageButtonList.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiPageButtonList.edit.java @@ -1,14 +1,16 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> INSERT 2 : 4 @ 2 +> INSERT 2 : 6 @ 2 + import java.util.List; + ++ import com.carrotsearch.hppc.IntObjectHashMap; ++ import com.carrotsearch.hppc.IntObjectMap; > CHANGE 4 : 7 @ 4 : 5 @@ -16,27 +18,51 @@ ~ import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; ~ import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; -> DELETE 1 @ 1 : 9 +> DELETE 1 @ 1 : 10 -> CHANGE 22 : 24 @ 22 : 23 +> CHANGE 3 : 4 @ 3 : 4 + +~ private final IntObjectMap field_178073_v = new IntObjectHashMap<>(); + +> CHANGE 17 : 19 @ 17 : 18 ~ for (int n = 0; n < this.field_178078_x.length; ++n) { ~ GuiPageButtonList.GuiListEntry[] aguipagebuttonlist$guilistentry = this.field_178078_x[n]; -> CHANGE 86 : 91 @ 86 : 90 +> CHANGE 11 : 12 @ 11 : 12 + +~ this.field_178073_v.put(guipagebuttonlist$guilistentry.func_178935_b(), gui); + +> CHANGE 6 : 7 @ 6 : 7 + +~ this.field_178073_v.put(guipagebuttonlist$guilistentry1.func_178935_b(), gui1); + +> CHANGE 16 : 17 @ 16 : 17 + +~ Gui gui = this.field_178073_v.get(guipagebuttonlist$guilistentry.func_178935_b()); + +> CHANGE 1 : 2 @ 1 : 2 + +~ ? this.field_178073_v.get(guipagebuttonlist$guilistentry1.func_178935_b()) + +> CHANGE 44 : 45 @ 44 : 45 + +~ return this.field_178073_v.get(parInt1); + +> CHANGE 3 : 8 @ 3 : 7 ~ ~ GuiListEntry[] etr = this.field_178078_x[parInt1]; ~ for (int i = 0; i < etr.length; ++i) { ~ if (etr[i] != null) { -~ this.func_178066_a((Gui) this.field_178073_v.lookup(etr[i].func_178935_b()), false); +~ this.func_178066_a(this.field_178073_v.get(etr[i].func_178935_b()), false); > CHANGE 3 : 7 @ 3 : 7 ~ etr = this.field_178078_x[parInt2]; ~ for (int i = 0; i < etr.length; ++i) { ~ if (etr[i] != null) { -~ this.func_178066_a((Gui) this.field_178073_v.lookup(etr[i].func_178935_b()), true); +~ this.func_178066_a(this.field_178073_v.get(etr[i].func_178935_b()), true); > CHANGE 33 : 35 @ 33 : 34 diff --git a/patches/minecraft/net/minecraft/client/gui/GuiPlayerTabOverlay.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiPlayerTabOverlay.edit.java index 6b048d5..1059fba 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiPlayerTabOverlay.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiPlayerTabOverlay.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiRenameWorld.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiRenameWorld.edit.java index 3c333a7..8af69b0 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiRenameWorld.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiRenameWorld.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiRepair.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiRepair.edit.java index 6550f08..be998ae 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiRepair.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiRepair.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiResourcePackAvailable.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiResourcePackAvailable.edit.java index 4d0a732..8c9d894 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiResourcePackAvailable.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiResourcePackAvailable.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiResourcePackList.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiResourcePackList.edit.java index 4d0a732..8c9d894 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiResourcePackList.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiResourcePackList.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiResourcePackSelected.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiResourcePackSelected.edit.java index 4d0a732..8c9d894 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiResourcePackSelected.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiResourcePackSelected.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiScreen.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiScreen.edit.java index df86be6..969fead 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiScreen.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiScreen.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude @@ -9,20 +9,14 @@ > DELETE 1 @ 1 : 3 -> INSERT 2 : 3 @ 2 - -+ import java.util.HashMap; - -> INSERT 1 : 2 @ 1 - -+ import java.util.Map; - -> INSERT 1 : 24 @ 1 +> INSERT 4 : 29 @ 4 + + import net.lax1dude.eaglercraft.v1_8.internal.EnumTouchEvent; + import org.apache.commons.lang3.StringUtils; + ++ import com.carrotsearch.hppc.IntObjectHashMap; ++ import com.carrotsearch.hppc.IntObjectMap; + import com.google.common.base.Splitter; + import com.google.common.collect.Lists; + import com.google.common.collect.Sets; @@ -305,7 +299,7 @@ > INSERT 11 : 96 @ 11 -+ public final Map touchStarts = new HashMap<>(); ++ public final IntObjectMap touchStarts = new IntObjectHashMap<>(); + + /** + * Handles touch input. diff --git a/patches/minecraft/net/minecraft/client/gui/GuiScreenAddServer.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiScreenAddServer.edit.java index f0c9b4b..d8caeea 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiScreenAddServer.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiScreenAddServer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiScreenBook.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiScreenBook.edit.java index 1c1b9de..bd015f4 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiScreenBook.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiScreenBook.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiScreenCustomizePresets.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiScreenCustomizePresets.edit.java index 74c12e4..e0aca3b 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiScreenCustomizePresets.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiScreenCustomizePresets.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiScreenOptionsSounds.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiScreenOptionsSounds.edit.java index 5f6f77c..369cc40 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiScreenOptionsSounds.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiScreenOptionsSounds.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiScreenResourcePacks.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiScreenResourcePacks.edit.java index 2c63271..a3ce87d 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiScreenResourcePacks.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiScreenResourcePacks.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiScreenServerList.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiScreenServerList.edit.java index 824578d..87127ee 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiScreenServerList.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiScreenServerList.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiScreenWorking.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiScreenWorking.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiScreenWorking.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiScreenWorking.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiSelectWorld.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiSelectWorld.edit.java index 4969828..8846bae 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiSelectWorld.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiSelectWorld.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiSleepMP.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiSleepMP.edit.java index 3ba9656..2c084eb 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiSleepMP.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiSleepMP.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiSlider.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiSlider.edit.java index 10d2bba..fa85ba0 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiSlider.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiSlider.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiSlot.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiSlot.edit.java index d689e82..99f393e 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiSlot.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiSlot.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiSpectator.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiSpectator.edit.java index f64cbb9..82f1af8 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiSpectator.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiSpectator.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiTextField.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiTextField.edit.java index 8074042..f63476a 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiTextField.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiTextField.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiUtilRenderComponents.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiUtilRenderComponents.edit.java index 88eb062..8a8466d 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiUtilRenderComponents.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiUtilRenderComponents.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiVideoSettings.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiVideoSettings.edit.java index be42ea7..4c30fae 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiVideoSettings.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiVideoSettings.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiWinGame.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiWinGame.edit.java index 92da334..f2d36fd 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiWinGame.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiWinGame.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/GuiYesNo.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiYesNo.edit.java index f2978a5..cebc8f4 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiYesNo.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiYesNo.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/MapItemRenderer.edit.java b/patches/minecraft/net/minecraft/client/gui/MapItemRenderer.edit.java index 2a04379..992d2d2 100644 --- a/patches/minecraft/net/minecraft/client/gui/MapItemRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/MapItemRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/ScaledResolution.edit.java b/patches/minecraft/net/minecraft/client/gui/ScaledResolution.edit.java index 2d8714a..13e536d 100644 --- a/patches/minecraft/net/minecraft/client/gui/ScaledResolution.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/ScaledResolution.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/ScreenChatOptions.edit.java b/patches/minecraft/net/minecraft/client/gui/ScreenChatOptions.edit.java index 892d37c..63db1bb 100644 --- a/patches/minecraft/net/minecraft/client/gui/ScreenChatOptions.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/ScreenChatOptions.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/ServerListEntryNormal.edit.java b/patches/minecraft/net/minecraft/client/gui/ServerListEntryNormal.edit.java index 6094581..4332482 100644 --- a/patches/minecraft/net/minecraft/client/gui/ServerListEntryNormal.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/ServerListEntryNormal.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/ServerSelectionList.edit.java b/patches/minecraft/net/minecraft/client/gui/ServerSelectionList.edit.java index 3eba377..3d12b9f 100644 --- a/patches/minecraft/net/minecraft/client/gui/ServerSelectionList.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/ServerSelectionList.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/achievement/GuiAchievement.edit.java b/patches/minecraft/net/minecraft/client/gui/achievement/GuiAchievement.edit.java index 3c0d6ef..aad40f0 100644 --- a/patches/minecraft/net/minecraft/client/gui/achievement/GuiAchievement.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/achievement/GuiAchievement.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/achievement/GuiAchievements.edit.java b/patches/minecraft/net/minecraft/client/gui/achievement/GuiAchievements.edit.java index 11f866c..9033054 100644 --- a/patches/minecraft/net/minecraft/client/gui/achievement/GuiAchievements.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/achievement/GuiAchievements.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/achievement/GuiStats.edit.java b/patches/minecraft/net/minecraft/client/gui/achievement/GuiStats.edit.java index 35e7d51..c800064 100644 --- a/patches/minecraft/net/minecraft/client/gui/achievement/GuiStats.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/achievement/GuiStats.edit.java @@ -1,15 +1,16 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 3 -> INSERT 4 : 10 @ 4 +> INSERT 4 : 11 @ 4 + ++ import com.carrotsearch.hppc.cursors.ObjectCursor; + import com.google.common.collect.Lists; + + import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; @@ -50,4 +51,9 @@ ~ for (int m = 0, l = StatList.itemStats.size(); m < l; ++m) { ~ StatCrafting statcrafting = StatList.itemStats.get(m); +> CHANGE 100 : 102 @ 100 : 101 + +~ for (ObjectCursor entitylist$entityegginfo_ : EntityList.entityEggs.values()) { +~ EntityList.EntityEggInfo entitylist$entityegginfo = entitylist$entityegginfo_.value; + > EOF diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/CreativeCrafting.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/CreativeCrafting.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/client/gui/inventory/CreativeCrafting.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/inventory/CreativeCrafting.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiBeacon.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiBeacon.edit.java index 4d470aa..9df8c1b 100644 --- a/patches/minecraft/net/minecraft/client/gui/inventory/GuiBeacon.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiBeacon.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiBrewingStand.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiBrewingStand.edit.java index beb06bb..51b9cd3 100644 --- a/patches/minecraft/net/minecraft/client/gui/inventory/GuiBrewingStand.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiBrewingStand.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiChest.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiChest.edit.java index 20d8a9c..474ead8 100644 --- a/patches/minecraft/net/minecraft/client/gui/inventory/GuiChest.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiChest.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiContainer.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiContainer.edit.java index c7fdbd3..30a5770 100644 --- a/patches/minecraft/net/minecraft/client/gui/inventory/GuiContainer.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiContainer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiContainerCreative.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiContainerCreative.edit.java index 0739bb5..2cea69a 100644 --- a/patches/minecraft/net/minecraft/client/gui/inventory/GuiContainerCreative.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiContainerCreative.edit.java @@ -1,23 +1,24 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 3 -> INSERT 4 : 13 @ 4 +> CHANGE 3 : 13 @ 3 : 4 -+ -+ import com.google.common.collect.Lists; -+ -+ import net.lax1dude.eaglercraft.v1_8.Keyboard; -+ import net.lax1dude.eaglercraft.v1_8.Mouse; -+ import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; -+ import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType; -+ import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; -+ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ +~ import com.carrotsearch.hppc.IntIntMap; +~ import com.google.common.collect.Lists; +~ +~ import net.lax1dude.eaglercraft.v1_8.Keyboard; +~ import net.lax1dude.eaglercraft.v1_8.Mouse; +~ import net.lax1dude.eaglercraft.v1_8.PointerInputAbstraction; +~ import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType; +~ import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; > DELETE 4 @ 4 : 7 @@ -100,7 +101,15 @@ ~ List list = itemstack.getTooltipProfanityFilter(this.mc.thePlayer, ~ this.mc.gameSettings.advancedItemTooltips); -> CHANGE 7 : 9 @ 7 : 8 +> CHANGE 2 : 3 @ 2 : 3 + +~ IntIntMap map = EnchantmentHelper.getEnchantments(itemstack); + +> CHANGE 1 : 2 @ 1 : 3 + +~ Enchantment enchantment = Enchantment.getEnchantmentById(map.keys().iterator().next().value); + +> CHANGE 1 : 3 @ 1 : 2 ~ for (int m = 0; m < CreativeTabs.creativeTabArray.length; ++m) { ~ CreativeTabs creativetabs1 = CreativeTabs.creativeTabArray[m]; diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiCrafting.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiCrafting.edit.java index beb06bb..51b9cd3 100644 --- a/patches/minecraft/net/minecraft/client/gui/inventory/GuiCrafting.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiCrafting.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiDispenser.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiDispenser.edit.java index beb06bb..51b9cd3 100644 --- a/patches/minecraft/net/minecraft/client/gui/inventory/GuiDispenser.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiDispenser.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiEditSign.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiEditSign.edit.java index dda3843..4a8e0bb 100644 --- a/patches/minecraft/net/minecraft/client/gui/inventory/GuiEditSign.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiEditSign.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiFurnace.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiFurnace.edit.java index beb06bb..51b9cd3 100644 --- a/patches/minecraft/net/minecraft/client/gui/inventory/GuiFurnace.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiFurnace.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiInventory.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiInventory.edit.java index 3a416d0..d83c9f4 100644 --- a/patches/minecraft/net/minecraft/client/gui/inventory/GuiInventory.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiInventory.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiScreenHorseInventory.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiScreenHorseInventory.edit.java index b28e0d5..ae76ce9 100644 --- a/patches/minecraft/net/minecraft/client/gui/inventory/GuiScreenHorseInventory.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiScreenHorseInventory.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/spectator/BaseSpectatorGroup.edit.java b/patches/minecraft/net/minecraft/client/gui/spectator/BaseSpectatorGroup.edit.java index 7c29447..f2407aa 100644 --- a/patches/minecraft/net/minecraft/client/gui/spectator/BaseSpectatorGroup.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/spectator/BaseSpectatorGroup.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuObject.edit.java b/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuObject.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuObject.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuObject.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuRecipient.edit.java b/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuRecipient.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuRecipient.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuRecipient.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuView.edit.java b/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuView.edit.java index bfbb2b5..8262c3c 100644 --- a/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuView.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuView.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/spectator/PlayerMenuObject.edit.java b/patches/minecraft/net/minecraft/client/gui/spectator/PlayerMenuObject.edit.java index e19ff03..2ec2c23 100644 --- a/patches/minecraft/net/minecraft/client/gui/spectator/PlayerMenuObject.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/spectator/PlayerMenuObject.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/spectator/SpectatorMenu.edit.java b/patches/minecraft/net/minecraft/client/gui/spectator/SpectatorMenu.edit.java index f6e3dfb..9067ca7 100644 --- a/patches/minecraft/net/minecraft/client/gui/spectator/SpectatorMenu.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/spectator/SpectatorMenu.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/spectator/categories/SpectatorDetails.edit.java b/patches/minecraft/net/minecraft/client/gui/spectator/categories/SpectatorDetails.edit.java index b5e2972..408f83b 100644 --- a/patches/minecraft/net/minecraft/client/gui/spectator/categories/SpectatorDetails.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/spectator/categories/SpectatorDetails.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/spectator/categories/TeleportToPlayer.edit.java b/patches/minecraft/net/minecraft/client/gui/spectator/categories/TeleportToPlayer.edit.java index 93db571..0e15a37 100644 --- a/patches/minecraft/net/minecraft/client/gui/spectator/categories/TeleportToPlayer.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/spectator/categories/TeleportToPlayer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/gui/spectator/categories/TeleportToTeam.edit.java b/patches/minecraft/net/minecraft/client/gui/spectator/categories/TeleportToTeam.edit.java index 353826b..fb497c4 100644 --- a/patches/minecraft/net/minecraft/client/gui/spectator/categories/TeleportToTeam.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/spectator/categories/TeleportToTeam.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/main/GameConfiguration.edit.java b/patches/minecraft/net/minecraft/client/main/GameConfiguration.edit.java index 1d902f9..4de416f 100644 --- a/patches/minecraft/net/minecraft/client/main/GameConfiguration.edit.java +++ b/patches/minecraft/net/minecraft/client/main/GameConfiguration.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/main/Main.edit.java b/patches/minecraft/net/minecraft/client/main/Main.edit.java index dc89d14..bcf81f2 100644 --- a/patches/minecraft/net/minecraft/client/main/Main.edit.java +++ b/patches/minecraft/net/minecraft/client/main/Main.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelArmorStand.edit.java b/patches/minecraft/net/minecraft/client/model/ModelArmorStand.edit.java index de98a05..3f67f13 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelArmorStand.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelArmorStand.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelArmorStandArmor.edit.java b/patches/minecraft/net/minecraft/client/model/ModelArmorStandArmor.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelArmorStandArmor.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelArmorStandArmor.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelBanner.edit.java b/patches/minecraft/net/minecraft/client/model/ModelBanner.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelBanner.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelBanner.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelBase.edit.java b/patches/minecraft/net/minecraft/client/model/ModelBase.edit.java index 16e8fc2..c9eeb66 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelBase.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelBase.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelBat.edit.java b/patches/minecraft/net/minecraft/client/model/ModelBat.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelBat.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelBat.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelBiped.edit.java b/patches/minecraft/net/minecraft/client/model/ModelBiped.edit.java index dfdd3fa..7126380 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelBiped.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelBiped.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelBlaze.edit.java b/patches/minecraft/net/minecraft/client/model/ModelBlaze.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelBlaze.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelBlaze.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelBoat.edit.java b/patches/minecraft/net/minecraft/client/model/ModelBoat.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelBoat.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelBoat.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelBook.edit.java b/patches/minecraft/net/minecraft/client/model/ModelBook.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelBook.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelBook.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelBox.edit.java b/patches/minecraft/net/minecraft/client/model/ModelBox.edit.java index ddc2df7..bbc09ed 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelBox.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelBox.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelChest.edit.java b/patches/minecraft/net/minecraft/client/model/ModelChest.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelChest.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelChest.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelChicken.edit.java b/patches/minecraft/net/minecraft/client/model/ModelChicken.edit.java index de98a05..3f67f13 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelChicken.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelChicken.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelCow.edit.java b/patches/minecraft/net/minecraft/client/model/ModelCow.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelCow.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelCow.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelCreeper.edit.java b/patches/minecraft/net/minecraft/client/model/ModelCreeper.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelCreeper.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelCreeper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelDragon.edit.java b/patches/minecraft/net/minecraft/client/model/ModelDragon.edit.java index cd768fc..3f106f9 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelDragon.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelDragon.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelEnderCrystal.edit.java b/patches/minecraft/net/minecraft/client/model/ModelEnderCrystal.edit.java index de98a05..3f67f13 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelEnderCrystal.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelEnderCrystal.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelEnderMite.edit.java b/patches/minecraft/net/minecraft/client/model/ModelEnderMite.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelEnderMite.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelEnderMite.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelEnderman.edit.java b/patches/minecraft/net/minecraft/client/model/ModelEnderman.edit.java index 5c9c914..2d73d95 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelEnderman.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelEnderman.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelGhast.edit.java b/patches/minecraft/net/minecraft/client/model/ModelGhast.edit.java index 5588df9..54f0272 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelGhast.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelGhast.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelGuardian.edit.java b/patches/minecraft/net/minecraft/client/model/ModelGuardian.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelGuardian.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelGuardian.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelHorse.edit.java b/patches/minecraft/net/minecraft/client/model/ModelHorse.edit.java index de98a05..3f67f13 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelHorse.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelHorse.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelHumanoidHead.edit.java b/patches/minecraft/net/minecraft/client/model/ModelHumanoidHead.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelHumanoidHead.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelHumanoidHead.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelIronGolem.edit.java b/patches/minecraft/net/minecraft/client/model/ModelIronGolem.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelIronGolem.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelIronGolem.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelLargeChest.edit.java b/patches/minecraft/net/minecraft/client/model/ModelLargeChest.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelLargeChest.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelLargeChest.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelLeashKnot.edit.java b/patches/minecraft/net/minecraft/client/model/ModelLeashKnot.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelLeashKnot.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelLeashKnot.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelMagmaCube.edit.java b/patches/minecraft/net/minecraft/client/model/ModelMagmaCube.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelMagmaCube.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelMagmaCube.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelMinecart.edit.java b/patches/minecraft/net/minecraft/client/model/ModelMinecart.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelMinecart.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelMinecart.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelOcelot.edit.java b/patches/minecraft/net/minecraft/client/model/ModelOcelot.edit.java index de98a05..3f67f13 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelOcelot.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelOcelot.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelPig.edit.java b/patches/minecraft/net/minecraft/client/model/ModelPig.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelPig.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelPig.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelPlayer.edit.java b/patches/minecraft/net/minecraft/client/model/ModelPlayer.edit.java index 6fef1af..f862b97 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelPlayer.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelPlayer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelQuadruped.edit.java b/patches/minecraft/net/minecraft/client/model/ModelQuadruped.edit.java index de98a05..3f67f13 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelQuadruped.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelQuadruped.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelRabbit.edit.java b/patches/minecraft/net/minecraft/client/model/ModelRabbit.edit.java index de98a05..3f67f13 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelRabbit.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelRabbit.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelRenderer.edit.java b/patches/minecraft/net/minecraft/client/model/ModelRenderer.edit.java index 97c32f9..b53dbfd 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelSheep1.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSheep1.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelSheep1.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelSheep1.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelSheep2.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSheep2.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelSheep2.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelSheep2.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelSign.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSign.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelSign.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelSign.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelSilverfish.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSilverfish.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelSilverfish.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelSilverfish.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelSkeleton.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSkeleton.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelSkeleton.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelSkeleton.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelSkeletonHead.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSkeletonHead.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelSkeletonHead.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelSkeletonHead.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelSlime.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSlime.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelSlime.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelSlime.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelSnowMan.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSnowMan.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelSnowMan.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelSnowMan.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelSpider.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSpider.edit.java index 0225cba..63c8ece 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelSpider.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelSpider.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelSquid.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSquid.edit.java index 31ce0cb..e83b424 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelSquid.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelSquid.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelVillager.edit.java b/patches/minecraft/net/minecraft/client/model/ModelVillager.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelVillager.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelVillager.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelWitch.edit.java b/patches/minecraft/net/minecraft/client/model/ModelWitch.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelWitch.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelWitch.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelWither.edit.java b/patches/minecraft/net/minecraft/client/model/ModelWither.edit.java index 9d5baca..5cb9b0e 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelWither.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelWither.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelWolf.edit.java b/patches/minecraft/net/minecraft/client/model/ModelWolf.edit.java index de98a05..3f67f13 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelWolf.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelWolf.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelZombie.edit.java b/patches/minecraft/net/minecraft/client/model/ModelZombie.edit.java index 6f9ed03..7acfe96 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelZombie.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelZombie.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/ModelZombieVillager.edit.java b/patches/minecraft/net/minecraft/client/model/ModelZombieVillager.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/model/ModelZombieVillager.edit.java +++ b/patches/minecraft/net/minecraft/client/model/ModelZombieVillager.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/model/TexturedQuad.edit.java b/patches/minecraft/net/minecraft/client/model/TexturedQuad.edit.java index 59c388b..a9d9eaa 100644 --- a/patches/minecraft/net/minecraft/client/model/TexturedQuad.edit.java +++ b/patches/minecraft/net/minecraft/client/model/TexturedQuad.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/multiplayer/ChunkProviderClient.edit.java b/patches/minecraft/net/minecraft/client/multiplayer/ChunkProviderClient.edit.java index 37b4080..ac50601 100644 --- a/patches/minecraft/net/minecraft/client/multiplayer/ChunkProviderClient.edit.java +++ b/patches/minecraft/net/minecraft/client/multiplayer/ChunkProviderClient.edit.java @@ -1,24 +1,40 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 3 -> INSERT 1 : 7 @ 1 +> INSERT 1 : 9 @ 1 + ++ import com.carrotsearch.hppc.LongObjectHashMap; ++ import com.carrotsearch.hppc.LongObjectMap; + import com.google.common.collect.Lists; + + import net.lax1dude.eaglercraft.v1_8.EagRuntime; + import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; + import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -> DELETE 10 @ 10 : 12 +> DELETE 3 @ 3 : 4 -> CHANGE 48 : 49 @ 48 : 49 +> DELETE 6 @ 6 : 8 + +> CHANGE 4 : 5 @ 4 : 5 + +~ private LongObjectMap chunkMapping = new LongObjectHashMap<>(); + +> CHANGE 24 : 25 @ 24 : 25 + +~ this.chunkMapping.put(ChunkCoordIntPair.chunkXZ2Int(parInt1, parInt2), chunk); + +> CHANGE 6 : 7 @ 6 : 7 + +~ Chunk chunk = this.chunkMapping.get(ChunkCoordIntPair.chunkXZ2Int(i, j)); + +> CHANGE 11 : 12 @ 11 : 12 ~ long i = EagRuntime.steadyTimeMillis(); @@ -35,4 +51,8 @@ ~ new Object[] { Long.valueOf(EagRuntime.steadyTimeMillis() - i) }); +> CHANGE 17 : 18 @ 17 : 18 + +~ return "MultiplayerChunkCache: " + this.chunkMapping.size() + ", " + this.chunkListing.size(); + > EOF diff --git a/patches/minecraft/net/minecraft/client/multiplayer/GuiConnecting.edit.java b/patches/minecraft/net/minecraft/client/multiplayer/GuiConnecting.edit.java index 430109d..de58dc1 100644 --- a/patches/minecraft/net/minecraft/client/multiplayer/GuiConnecting.edit.java +++ b/patches/minecraft/net/minecraft/client/multiplayer/GuiConnecting.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/multiplayer/PlayerControllerMP.edit.java b/patches/minecraft/net/minecraft/client/multiplayer/PlayerControllerMP.edit.java index 057f10a..aaf4c2d 100644 --- a/patches/minecraft/net/minecraft/client/multiplayer/PlayerControllerMP.edit.java +++ b/patches/minecraft/net/minecraft/client/multiplayer/PlayerControllerMP.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/multiplayer/ServerAddress.edit.java b/patches/minecraft/net/minecraft/client/multiplayer/ServerAddress.edit.java index ddc4bd6..895b91f 100644 --- a/patches/minecraft/net/minecraft/client/multiplayer/ServerAddress.edit.java +++ b/patches/minecraft/net/minecraft/client/multiplayer/ServerAddress.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/multiplayer/ServerData.edit.java b/patches/minecraft/net/minecraft/client/multiplayer/ServerData.edit.java index e4f32ad..fd6bf7d 100644 --- a/patches/minecraft/net/minecraft/client/multiplayer/ServerData.edit.java +++ b/patches/minecraft/net/minecraft/client/multiplayer/ServerData.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/multiplayer/ServerList.edit.java b/patches/minecraft/net/minecraft/client/multiplayer/ServerList.edit.java index 5e33c58..7dc4e83 100644 --- a/patches/minecraft/net/minecraft/client/multiplayer/ServerList.edit.java +++ b/patches/minecraft/net/minecraft/client/multiplayer/ServerList.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/multiplayer/WorldClient.edit.java b/patches/minecraft/net/minecraft/client/multiplayer/WorldClient.edit.java index a74df85..9574680 100644 --- a/patches/minecraft/net/minecraft/client/multiplayer/WorldClient.edit.java +++ b/patches/minecraft/net/minecraft/client/multiplayer/WorldClient.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude @@ -9,9 +9,12 @@ ~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; -> INSERT 2 : 5 @ 2 +> INSERT 2 : 8 @ 2 + ++ import com.carrotsearch.hppc.LongHashSet; ++ import com.carrotsearch.hppc.LongSet; ++ import com.carrotsearch.hppc.cursors.LongCursor; + import com.google.common.collect.Sets; + @@ -19,7 +22,13 @@ > DELETE 9 @ 9 : 10 -> CHANGE 25 : 26 @ 25 : 26 +> DELETE 5 @ 5 : 6 + +> CHANGE 16 : 17 @ 16 : 17 + +~ private final LongSet previousActiveChunkSet = new LongHashSet(); + +> CHANGE 2 : 3 @ 2 : 3 ~ EnumDifficulty parEnumDifficulty) { @@ -35,11 +44,38 @@ > DELETE 1 @ 1 : 2 -> DELETE 24 @ 24 : 25 +> CHANGE 13 : 14 @ 13 : 14 -> DELETE 2 @ 2 : 3 +~ this.previousActiveChunkSet.retainAll(this.activeChunkSet::contains); -> CHANGE 113 : 114 @ 113 : 114 +> CHANGE 6 : 14 @ 6 : 12 + +~ for (LongCursor chunkcoordintpair : this.activeChunkSet) { +~ long l = chunkcoordintpair.value; +~ if (!this.previousActiveChunkSet.contains(l)) { +~ int chunkXPos = (int) (l & 4294967295L); +~ int chunkZPos = (int) (l >>> 32); +~ int j = chunkXPos * 16; +~ int k = chunkZPos * 16; +~ Chunk chunk = this.getChunkFromChunkCoords(chunkXPos, chunkZPos); + +> CHANGE 1 : 2 @ 1 : 3 + +~ this.previousActiveChunkSet.add(l); + +> CHANGE 74 : 75 @ 74 : 75 + +~ this.entitiesById.put(parInt1, parEntity); + +> CHANGE 3 : 4 @ 3 : 4 + +~ return i == this.mc.thePlayer.getEntityId() ? this.mc.thePlayer : super.getEntityByID(i); + +> CHANGE 3 : 4 @ 3 : 4 + +~ Entity entity = this.entitiesById.remove(parInt1); + +> CHANGE 29 : 30 @ 29 : 30 ~ EaglercraftRandom random = new EaglercraftRandom(); diff --git a/patches/minecraft/net/minecraft/client/network/NetHandlerPlayClient.edit.java b/patches/minecraft/net/minecraft/client/network/NetHandlerPlayClient.edit.java index 1c04a98..3ca2966 100644 --- a/patches/minecraft/net/minecraft/client/network/NetHandlerPlayClient.edit.java +++ b/patches/minecraft/net/minecraft/client/network/NetHandlerPlayClient.edit.java @@ -1,39 +1,38 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 8 -> DELETE 4 @ 4 : 6 +> CHANGE 4 : 28 @ 4 : 7 -> INSERT 1 : 24 @ 1 - -+ -+ import net.lax1dude.eaglercraft.v1_8.ClientUUIDLoadingCache; -+ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; -+ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -+ -+ 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.socket.EaglercraftNetworkManager; -+ 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.GameMessagePacket; -+ import net.lax1dude.eaglercraft.v1_8.sp.lan.LANClientNetworkManager; -+ import net.lax1dude.eaglercraft.v1_8.sp.socket.ClientIntegratedServerNetworkManager; -+ import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; -+ import net.lax1dude.eaglercraft.v1_8.webview.WebViewOverlayController; -+ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -+ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -+ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack; -+ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; +~ +~ import net.lax1dude.eaglercraft.v1_8.ClientUUIDLoadingCache; +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ +~ import com.carrotsearch.hppc.cursors.ObjectIntCursor; +~ 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.socket.EaglercraftNetworkManager; +~ 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.GameMessagePacket; +~ import net.lax1dude.eaglercraft.v1_8.sp.lan.LANClientNetworkManager; +~ import net.lax1dude.eaglercraft.v1_8.sp.socket.ClientIntegratedServerNetworkManager; +~ import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; +~ import net.lax1dude.eaglercraft.v1_8.webview.WebViewOverlayController; +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +~ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack; +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; > DELETE 14 @ 14 : 16 @@ -443,7 +442,13 @@ > DELETE 11 @ 11 : 12 -> DELETE 9 @ 9 : 10 +> CHANGE 2 : 5 @ 2 : 5 + +~ for (ObjectIntCursor entry : packetIn.func_148974_c()) { +~ StatBase statbase = entry.key; +~ int i = entry.value; + +> DELETE 4 @ 4 : 5 > DELETE 25 @ 25 : 26 diff --git a/patches/minecraft/net/minecraft/client/network/NetworkPlayerInfo.edit.java b/patches/minecraft/net/minecraft/client/network/NetworkPlayerInfo.edit.java index 0d3b961..c78d1aa 100644 --- a/patches/minecraft/net/minecraft/client/network/NetworkPlayerInfo.edit.java +++ b/patches/minecraft/net/minecraft/client/network/NetworkPlayerInfo.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/Barrier.edit.java b/patches/minecraft/net/minecraft/client/particle/Barrier.edit.java index 11c2cce..822b912 100644 --- a/patches/minecraft/net/minecraft/client/particle/Barrier.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/Barrier.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EffectRenderer.edit.java b/patches/minecraft/net/minecraft/client/particle/EffectRenderer.edit.java index 606886f..4475532 100644 --- a/patches/minecraft/net/minecraft/client/particle/EffectRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EffectRenderer.edit.java @@ -1,13 +1,13 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 4 -> CHANGE 3 : 7 @ 3 : 4 +> CHANGE 2 : 6 @ 2 : 4 ~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; ~ import net.lax1dude.eaglercraft.v1_8.minecraft.AcceleratedEffectRenderer; @@ -17,9 +17,9 @@ > INSERT 1 : 10 @ 1 + ++ import com.carrotsearch.hppc.IntObjectHashMap; ++ import com.carrotsearch.hppc.IntObjectMap; + import com.google.common.collect.Lists; -+ import com.google.common.collect.Maps; -+ + import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; + import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; @@ -37,11 +37,12 @@ + private static final ResourceLocation particleMaterialsTextures = new ResourceLocation( + "eagler:glsl/deferred/particles_s.png"); -> CHANGE 4 : 5 @ 4 : 5 +> CHANGE 4 : 6 @ 4 : 6 ~ private EaglercraftRandom rand = new EaglercraftRandom(); +~ private IntObjectMap particleTypes = new IntObjectHashMap<>(); -> INSERT 2 : 5 @ 2 +> INSERT 1 : 4 @ 1 + public static final AcceleratedEffectRenderer vanillaAcceleratedParticleRenderer = new AcceleratedEffectRenderer(); + public IAcceleratedParticleEngine acceleratedParticleRenderer = null; @@ -52,7 +53,15 @@ + this.acceleratedParticleRenderer = EaglercraftGPU.checkInstancingCapable() ? vanillaAcceleratedParticleRenderer + : null; -> CHANGE 91 : 93 @ 91 : 92 +> CHANGE 52 : 53 @ 52 : 53 + +~ this.particleTypes.put(id, particleFactory); + +> CHANGE 8 : 9 @ 8 : 9 + +~ IParticleFactory iparticlefactory = this.particleTypes.get(particleId); + +> CHANGE 29 : 31 @ 29 : 30 ~ for (int i = 0, l = this.particleEmitters.size(); i < l; ++i) { ~ EntityParticleEmitter entityparticleemitter = this.particleEmitters.get(i); diff --git a/patches/minecraft/net/minecraft/client/particle/EntityAuraFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityAuraFX.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityAuraFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityAuraFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityBlockDustFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityBlockDustFX.edit.java index 0401ba9..8fdc3b6 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityBlockDustFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityBlockDustFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityBreakingFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityBreakingFX.edit.java index c92cf0c..2fe476f 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityBreakingFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityBreakingFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityBubbleFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityBubbleFX.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityBubbleFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityBubbleFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityCloudFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityCloudFX.edit.java index 6640654..4d94916 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityCloudFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityCloudFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityCrit2FX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityCrit2FX.edit.java index 6640654..4d94916 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityCrit2FX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityCrit2FX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityCritFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityCritFX.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityCritFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityCritFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityDiggingFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityDiggingFX.edit.java index f039080..004c72f 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityDiggingFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityDiggingFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityDropParticleFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityDropParticleFX.edit.java index 41da09f..86bf348 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityDropParticleFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityDropParticleFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityEnchantmentTableParticleFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityEnchantmentTableParticleFX.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityEnchantmentTableParticleFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityEnchantmentTableParticleFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityExplodeFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityExplodeFX.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityExplodeFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityExplodeFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityFX.edit.java index dfc213e..eb19986 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityFirework.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityFirework.edit.java index ac702a5..91a0c44 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityFirework.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityFirework.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityFishWakeFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityFishWakeFX.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityFishWakeFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityFishWakeFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityFlameFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityFlameFX.edit.java index 6640654..4d94916 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityFlameFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityFlameFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityFootStepFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityFootStepFX.edit.java index c01065f..903cbb6 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityFootStepFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityFootStepFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityHeartFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityHeartFX.edit.java index 6640654..4d94916 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityHeartFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityHeartFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityHugeExplodeFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityHugeExplodeFX.edit.java index 6640654..4d94916 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityHugeExplodeFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityHugeExplodeFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityLargeExplodeFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityLargeExplodeFX.edit.java index 7f522b0..cf7a6cb 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityLargeExplodeFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityLargeExplodeFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityLavaFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityLavaFX.edit.java index 6640654..4d94916 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityLavaFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityLavaFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityNoteFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityNoteFX.edit.java index 6640654..4d94916 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityNoteFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityNoteFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityParticleEmitter.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityParticleEmitter.edit.java index fe1dfe8..ed7ae13 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityParticleEmitter.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityParticleEmitter.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityPickupFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityPickupFX.edit.java index 3cd06e1..98ab972 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityPickupFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityPickupFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityPortalFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityPortalFX.edit.java index 6640654..4d94916 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityPortalFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityPortalFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityRainFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityRainFX.edit.java index 6422a38..77b3843 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityRainFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityRainFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntityReddustFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityReddustFX.edit.java index 6640654..4d94916 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntityReddustFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntityReddustFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntitySmokeFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntitySmokeFX.edit.java index 6640654..4d94916 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntitySmokeFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntitySmokeFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntitySnowShovelFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntitySnowShovelFX.edit.java index 6640654..4d94916 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntitySnowShovelFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntitySnowShovelFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntitySpellParticleFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntitySpellParticleFX.edit.java index 7182b55..8e363bb 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntitySpellParticleFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntitySpellParticleFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntitySplashFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntitySplashFX.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntitySplashFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntitySplashFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/EntitySuspendFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntitySuspendFX.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/client/particle/EntitySuspendFX.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/EntitySuspendFX.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/IParticleFactory.edit.java b/patches/minecraft/net/minecraft/client/particle/IParticleFactory.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/client/particle/IParticleFactory.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/IParticleFactory.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/particle/MobAppearance.edit.java b/patches/minecraft/net/minecraft/client/particle/MobAppearance.edit.java index e707e03..7f14bb1 100644 --- a/patches/minecraft/net/minecraft/client/particle/MobAppearance.edit.java +++ b/patches/minecraft/net/minecraft/client/particle/MobAppearance.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/player/inventory/ContainerLocalMenu.edit.java b/patches/minecraft/net/minecraft/client/player/inventory/ContainerLocalMenu.edit.java index 2493e99..535bbae 100644 --- a/patches/minecraft/net/minecraft/client/player/inventory/ContainerLocalMenu.edit.java +++ b/patches/minecraft/net/minecraft/client/player/inventory/ContainerLocalMenu.edit.java @@ -1,16 +1,25 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> DELETE 2 @ 2 : 3 +> CHANGE 2 : 4 @ 2 : 4 -> INSERT 1 : 4 @ 1 +~ import com.carrotsearch.hppc.IntIntHashMap; +~ import com.carrotsearch.hppc.IntIntMap; -+ -+ import com.google.common.collect.Maps; -+ +> CHANGE 10 : 11 @ 10 : 11 + +~ private IntIntMap field_174895_b = new IntIntHashMap(); + +> CHANGE 7 : 8 @ 7 : 10 + +~ return this.field_174895_b.getOrDefault(i, 0); + +> CHANGE 3 : 4 @ 3 : 4 + +~ this.field_174895_b.put(i, j); > EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/ActiveRenderInfo.edit.java b/patches/minecraft/net/minecraft/client/renderer/ActiveRenderInfo.edit.java index 2143df2..4ab9c69 100644 --- a/patches/minecraft/net/minecraft/client/renderer/ActiveRenderInfo.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/ActiveRenderInfo.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/BlockFluidRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/BlockFluidRenderer.edit.java index 2d31d35..13c8d47 100644 --- a/patches/minecraft/net/minecraft/client/renderer/BlockFluidRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/BlockFluidRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/BlockModelRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/BlockModelRenderer.edit.java index 21af64a..bee8911 100644 --- a/patches/minecraft/net/minecraft/client/renderer/BlockModelRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/BlockModelRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/BlockModelShapes.edit.java b/patches/minecraft/net/minecraft/client/renderer/BlockModelShapes.edit.java index 4ae20c7..8417dbb 100644 --- a/patches/minecraft/net/minecraft/client/renderer/BlockModelShapes.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/BlockModelShapes.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.edit.java b/patches/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.edit.java index 40307c1..adf316f 100644 --- a/patches/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/ChestRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/ChestRenderer.edit.java index ad6bc7d..9beb2fd 100644 --- a/patches/minecraft/net/minecraft/client/renderer/ChestRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/ChestRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/ChunkRenderContainer.edit.java b/patches/minecraft/net/minecraft/client/renderer/ChunkRenderContainer.edit.java index f4b0620..3fe072f 100644 --- a/patches/minecraft/net/minecraft/client/renderer/ChunkRenderContainer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/ChunkRenderContainer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/EntityRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/EntityRenderer.edit.java index 1698a10..869a2cf 100644 --- a/patches/minecraft/net/minecraft/client/renderer/EntityRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/EntityRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude @@ -692,7 +692,7 @@ > DELETE 9 @ 9 : 10 -> INSERT 12 : 952 @ 12 +> INSERT 12 : 953 @ 12 + + private static final Vector4f tmpVec4f_1 = new Vector4f(); @@ -738,7 +738,7 @@ + // System.out.println(builder.toString()); + + float waveTimer = (float) ((EagRuntime.steadyTimeMillis() % 600000l) * 0.001); -+ DeferredStateManager.setWaterWindOffset(0.0f, 0.0f, waveTimer, waveTimer); ++ DeferredStateManager.setWaterWindOffset(waveTimer * 0.25f, waveTimer * 0.13f, waveTimer * 1.45f, waveTimer); + + float blockWaveDistX = (float) (d0 - blockWaveOffsetX); + float blockWaveDistY = (float) (d1 - blockWaveOffsetY); @@ -1373,7 +1373,7 @@ + + mc.theWorld.getThunderStrength(partialTicks) * 5.0f; + ds *= MathHelper.clamp_float(6.0f - DeferredStateManager.getSunHeight() * 17.0f, 1.0f, 3.0f); + if (conf.is_rendering_lightShafts) { -+ ds *= Math.max(2.0f - Math.abs(DeferredStateManager.getSunHeight()) * 5.0f, 1.0f); ++ ds *= Math.max(2.5f - Math.abs(DeferredStateManager.getSunHeight()) * 5.0f, 2.5f); + } + DeferredStateManager.enableFogExp(ds, true, 1.0f, 1.0f, 1.0f, 1.0f, ff, ff, ff, 1.0f); + } @@ -1471,6 +1471,7 @@ + for (int i = 0, l = lst.size(); i < l; ++i) { + lst.get(i).draw(ShadersRenderPassFuture.PassType.MAIN); + } ++ DeferredStateManager.forwardCallbackGBuffer.reset(); + GlStateManager.matrixMode(5889); + GlStateManager.popMatrix(); + GlStateManager.matrixMode(5888); diff --git a/patches/minecraft/net/minecraft/client/renderer/GLAllocation.edit.java b/patches/minecraft/net/minecraft/client/renderer/GLAllocation.edit.java index e0720a7..f05f374 100644 --- a/patches/minecraft/net/minecraft/client/renderer/GLAllocation.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/GLAllocation.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/IImageBuffer.edit.java b/patches/minecraft/net/minecraft/client/renderer/IImageBuffer.edit.java index 8321b8b..1ae9845 100644 --- a/patches/minecraft/net/minecraft/client/renderer/IImageBuffer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/IImageBuffer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/ImageBufferDownload.edit.java b/patches/minecraft/net/minecraft/client/renderer/ImageBufferDownload.edit.java index e8912f3..733f0b1 100644 --- a/patches/minecraft/net/minecraft/client/renderer/ImageBufferDownload.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/ImageBufferDownload.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/InventoryEffectRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/InventoryEffectRenderer.edit.java index cfc7fe8..044f6fa 100644 --- a/patches/minecraft/net/minecraft/client/renderer/InventoryEffectRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/InventoryEffectRenderer.edit.java @@ -1,19 +1,30 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> INSERT 3 : 5 @ 3 +> CHANGE 2 : 6 @ 2 : 3 -+ -+ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import com.carrotsearch.hppc.ObjectContainer; +~ import com.carrotsearch.hppc.cursors.ObjectCursor; +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; > DELETE 1 @ 1 : 2 -> INSERT 44 : 45 @ 44 +> CHANGE 40 : 41 @ 40 : 41 + +~ ObjectContainer collection = this.mc.thePlayer.getActivePotionEffects(); + +> INSERT 3 : 4 @ 3 + GlStateManager.enableAlpha(); +> CHANGE 5 : 7 @ 5 : 6 + +~ for (ObjectCursor potioneffect_ : collection) { +~ PotionEffect potioneffect = potioneffect_.value; + > EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/ItemModelMesher.edit.java b/patches/minecraft/net/minecraft/client/renderer/ItemModelMesher.edit.java index 032b250..e7704fe 100644 --- a/patches/minecraft/net/minecraft/client/renderer/ItemModelMesher.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/ItemModelMesher.edit.java @@ -1,20 +1,27 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 3 -> CHANGE 2 : 6 @ 2 : 4 +> CHANGE 1 : 7 @ 1 : 4 -~ +~ import com.carrotsearch.hppc.IntObjectHashMap; +~ import com.carrotsearch.hppc.IntObjectMap; +~ import com.carrotsearch.hppc.cursors.IntObjectCursor; ~ import com.google.common.collect.Maps; ~ ~ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; -> CHANGE 16 : 17 @ 16 : 17 +> CHANGE 7 : 9 @ 7 : 9 + +~ private final IntObjectMap simpleShapes = new IntObjectHashMap<>(); +~ private final IntObjectMap simpleShapesCache = new IntObjectHashMap<>(); + +> CHANGE 7 : 8 @ 7 : 8 ~ public EaglerTextureAtlasSprite getParticleIcon(Item item) { @@ -22,8 +29,24 @@ ~ public EaglerTextureAtlasSprite getParticleIcon(Item item, int meta) { -> CHANGE 49 : 50 @ 49 : 50 +> CHANGE 7 : 8 @ 7 : 8 -~ this.simpleShapesCache.put((Integer) entry.getKey(), +~ ItemMeshDefinition itemmeshdefinition = this.shapers.get(item); + +> CHANGE 17 : 18 @ 17 : 18 + +~ return this.simpleShapesCache.get(this.getIndex(item, meta)); + +> CHANGE 7 : 9 @ 7 : 9 + +~ this.simpleShapes.put(this.getIndex(item, meta), location); +~ this.simpleShapesCache.put(this.getIndex(item, meta), this.modelManager.getModel(location)); + +> CHANGE 13 : 15 @ 13 : 16 + +~ for (IntObjectCursor entry : this.simpleShapes) { +~ this.simpleShapesCache.put(entry.key, this.modelManager.getModel(entry.value)); + +> DELETE 1 @ 1 : 2 > EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/ItemRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/ItemRenderer.edit.java index 0f33a51..163a3e4 100644 --- a/patches/minecraft/net/minecraft/client/renderer/ItemRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/ItemRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude @@ -33,7 +33,7 @@ > CHANGE 101 : 102 @ 101 : 102 -~ EaglercraftGPU.glNormal3f(0.0F, 0.0F, -1.0F); +~ EaglercraftGPU.glNormal3f(0.0F, 0.0F, 1.0F); > CHANGE 194 : 195 @ 194 : 195 diff --git a/patches/minecraft/net/minecraft/client/renderer/RegionRenderCache.edit.java b/patches/minecraft/net/minecraft/client/renderer/RegionRenderCache.edit.java index 0e43732..d08bcdf 100644 --- a/patches/minecraft/net/minecraft/client/renderer/RegionRenderCache.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/RegionRenderCache.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude @@ -9,28 +9,77 @@ + -> CHANGE 10 : 11 @ 10 : 11 +> INSERT 7 : 8 @ 7 + ++ import net.minecraft.world.biome.BiomeGenBase; + +> CHANGE 3 : 4 @ 3 : 4 ~ private final IBlockState DEFAULT_STATE = Blocks.air.getDefaultState(); -> INSERT 41 : 55 @ 41 +> CHANGE 1 : 6 @ 1 : 3 + +~ private final BlockPos tmpStupid = new BlockPos(); +~ private static final int[] combinedLights = new int[8000]; +~ private static final IBlockState[] blockStates = new IBlockState[8000]; +~ private static final int[] biomeColors = new int[1200]; +~ private static final int[] biomeColorsBlended = new int[768]; + +> CHANGE 4 : 8 @ 4 : 8 + +~ Arrays.fill(combinedLights, -1); +~ Arrays.fill(blockStates, null); +~ Arrays.fill(biomeColors, 0); +~ Arrays.fill(biomeColorsBlended, 0); + +> CHANGE 10 : 11 @ 10 : 11 + +~ int k = combinedLights[j]; + +> CHANGE 2 : 3 @ 2 : 3 + +~ combinedLights[j] = k; + +> CHANGE 7 : 8 @ 7 : 8 + +~ IBlockState iblockstate = blockStates[i]; + +> CHANGE 2 : 3 @ 2 : 3 + +~ blockStates[i] = iblockstate; + +> INSERT 5 : 33 @ 5 + /** + * only use with a regular "net.minecraft.util.BlockPos"! + */ + public IBlockState getBlockStateFaster(BlockPos blockpos) { + int i = this.getPositionIndexFaster(blockpos); -+ IBlockState iblockstate = this.blockStates[i]; ++ IBlockState iblockstate = blockStates[i]; + if (iblockstate == null) { + iblockstate = this.getBlockStateRawFaster(blockpos); -+ this.blockStates[i] = iblockstate; ++ blockStates[i] = iblockstate; + } + + return iblockstate; + } + ++ /** ++ * only use with a regular "net.minecraft.util.BlockPos"! ++ */ ++ public int getBiomeColorForCoords(BlockPos blockpos, int colorIndex) { ++ int i = this.getPositionIndex16Faster(blockpos); ++ i += colorIndex * 256; ++ int j = biomeColorsBlended[i]; ++ if (j == 0) { ++ j = getBiomeColorBlended(blockpos, colorIndex); ++ biomeColorsBlended[i] = j; ++ } ++ return j; ++ } ++ -> INSERT 10 : 23 @ 10 +> INSERT 10 : 76 @ 10 + /** + * only use with a regular "net.minecraft.util.BlockPos"! @@ -45,8 +94,61 @@ + } + } + ++ private int getBiomeColorBlended(BlockPos blockpos, int colorIndex) { ++ BlockPos blockpos2 = tmpStupid; ++ blockpos2.y = blockpos.y; ++ int rad = 1; ++ int xmin = blockpos.x - rad; ++ int zmin = blockpos.z - rad; ++ int xmax = blockpos.x + rad; ++ int zmax = blockpos.z + rad; ++ int r = 0; ++ int g = 0; ++ int b = 0; ++ int rgb; ++ for (int x = xmin; x <= xmax; ++x) { ++ for (int z = zmin; z <= zmax; ++z) { ++ blockpos2.x = x; ++ blockpos2.z = z; ++ rgb = getBiomeColorRaw(blockpos2, colorIndex); ++ r += (rgb >> 16) & 0xFF; ++ g += (rgb >> 8) & 0xFF; ++ b += rgb & 0xFF; ++ } ++ } ++ rad = 1 + rad * 2; ++ rad *= rad; ++ return 0xFF000000 | ((r / rad) << 16) | ((g / rad) << 8) | (b / rad); ++ } ++ ++ private int getBiomeColorRaw(BlockPos blockpos, int colorIndex) { ++ int i = getPositionIndex20Faster(blockpos); ++ i += colorIndex * 400; ++ int j = biomeColors[i]; ++ BiomeGenBase biome; ++ int ii, jj; ++ if (j == 0) { ++ if (blockpos.y >= 0 && blockpos.y < 256) { ++ ii = (blockpos.x >> 4) - this.chunkX; ++ jj = (blockpos.z >> 4) - this.chunkZ; ++ biome = this.chunkArray[ii][jj].getBiome(blockpos, worldObj.getWorldChunkManager()); ++ if (colorIndex == 0) { ++ j = biome.getGrassColorAtPos(blockpos); ++ } else if (colorIndex == 1) { ++ j = biome.getFoliageColorAtPos(blockpos); ++ } else { ++ j = biome.waterColorMultiplier; ++ } ++ } else { ++ j = 0xFFFFFFFF; ++ } ++ biomeColors[i] = j; ++ } ++ return j; ++ } ++ -> INSERT 6 : 16 @ 6 +> INSERT 6 : 31 @ 6 + + /** @@ -58,5 +160,20 @@ + int k = parBlockPos.z - this.position.z; + return i * 400 + k * 20 + j; + } ++ ++ /** ++ * only use with a regular "net.minecraft.util.BlockPos"! ++ */ ++ private int getPositionIndex16Faster(BlockPos parBlockPos) { ++ int i = parBlockPos.x - this.position.x - 2; ++ int k = parBlockPos.z - this.position.z - 2; ++ return i * 16 + k; ++ } ++ ++ private int getPositionIndex20Faster(BlockPos parBlockPos) { ++ int i = parBlockPos.x - this.position.x; ++ int k = parBlockPos.z - this.position.z; ++ return i * 20 + k; ++ } > EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/RegionRenderCacheBuilder.edit.java b/patches/minecraft/net/minecraft/client/renderer/RegionRenderCacheBuilder.edit.java index 9ce878c..70de38d 100644 --- a/patches/minecraft/net/minecraft/client/renderer/RegionRenderCacheBuilder.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/RegionRenderCacheBuilder.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/RenderGlobal.edit.java b/patches/minecraft/net/minecraft/client/renderer/RenderGlobal.edit.java index d6d243d..4b05bc0 100644 --- a/patches/minecraft/net/minecraft/client/renderer/RenderGlobal.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/RenderGlobal.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/RenderHelper.edit.java b/patches/minecraft/net/minecraft/client/renderer/RenderHelper.edit.java index 2c2747c..3b47087 100644 --- a/patches/minecraft/net/minecraft/client/renderer/RenderHelper.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/RenderHelper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/RenderList.edit.java b/patches/minecraft/net/minecraft/client/renderer/RenderList.edit.java index d562edd..059f399 100644 --- a/patches/minecraft/net/minecraft/client/renderer/RenderList.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/RenderList.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/Tessellator.edit.java b/patches/minecraft/net/minecraft/client/renderer/Tessellator.edit.java index 62bbaad..3557e70 100644 --- a/patches/minecraft/net/minecraft/client/renderer/Tessellator.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/Tessellator.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/ViewFrustum.edit.java b/patches/minecraft/net/minecraft/client/renderer/ViewFrustum.edit.java index 4cf8796..af6786b 100644 --- a/patches/minecraft/net/minecraft/client/renderer/ViewFrustum.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/ViewFrustum.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/BakedQuad.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/BakedQuad.edit.java index 2f98276..f586a49 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/model/BakedQuad.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/BakedQuad.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/BlockFaceUV.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/BlockFaceUV.edit.java index 1eb24b7..b0a8d42 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/model/BlockFaceUV.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/BlockFaceUV.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPart.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPart.edit.java index 6bb7314..7c9eb70 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPart.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPart.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPartFace.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPartFace.edit.java index dec2d15..69007ef 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPartFace.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPartFace.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPartRotation.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPartRotation.edit.java index 939f4e5..b4dbdbe 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPartRotation.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPartRotation.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/BreakingFour.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/BreakingFour.edit.java index 4e45d24..661afea 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/model/BreakingFour.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/BreakingFour.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/FaceBakery.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/FaceBakery.edit.java index 0f4527a..026f08d 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/model/FaceBakery.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/FaceBakery.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/ItemCameraTransforms.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/ItemCameraTransforms.edit.java index 6b342cf..2c32ff7 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/model/ItemCameraTransforms.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/ItemCameraTransforms.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/ItemModelGenerator.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/ItemModelGenerator.edit.java index 76879ce..11655df 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/model/ItemModelGenerator.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/ItemModelGenerator.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/ItemTransformVec3f.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/ItemTransformVec3f.edit.java index 768ca24..d055962 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/model/ItemTransformVec3f.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/ItemTransformVec3f.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/ModelBlock.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/ModelBlock.edit.java index aef6252..ba4e61a 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/model/ModelBlock.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/ModelBlock.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/ModelBlockDefinition.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/ModelBlockDefinition.edit.java index 26e1d52..5f69cea 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/model/ModelBlockDefinition.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/ModelBlockDefinition.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/block/statemap/BlockStateMapper.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/statemap/BlockStateMapper.edit.java index f9b0c27..f68c81c 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/statemap/BlockStateMapper.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/block/statemap/BlockStateMapper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/block/statemap/DefaultStateMapper.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/statemap/DefaultStateMapper.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/statemap/DefaultStateMapper.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/block/statemap/DefaultStateMapper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/block/statemap/IStateMapper.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/statemap/IStateMapper.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/statemap/IStateMapper.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/block/statemap/IStateMapper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMap.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMap.edit.java index 5c2315b..a7247f3 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMap.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMap.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMapperBase.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMapperBase.edit.java index f5658e5..82d5311 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMapperBase.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMapperBase.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/ChunkCompileTaskGenerator.edit.java b/patches/minecraft/net/minecraft/client/renderer/chunk/ChunkCompileTaskGenerator.edit.java index e967705..7eb53bd 100644 --- a/patches/minecraft/net/minecraft/client/renderer/chunk/ChunkCompileTaskGenerator.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/ChunkCompileTaskGenerator.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/ChunkRenderWorker.edit.java b/patches/minecraft/net/minecraft/client/renderer/chunk/ChunkRenderWorker.edit.java index a4e467c..ddb72cd 100644 --- a/patches/minecraft/net/minecraft/client/renderer/chunk/ChunkRenderWorker.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/ChunkRenderWorker.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/CompiledChunk.edit.java b/patches/minecraft/net/minecraft/client/renderer/chunk/CompiledChunk.edit.java index f22aa94..76dd53f 100644 --- a/patches/minecraft/net/minecraft/client/renderer/chunk/CompiledChunk.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/CompiledChunk.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/IRenderChunkFactory.edit.java b/patches/minecraft/net/minecraft/client/renderer/chunk/IRenderChunkFactory.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/client/renderer/chunk/IRenderChunkFactory.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/IRenderChunkFactory.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/ListChunkFactory.edit.java b/patches/minecraft/net/minecraft/client/renderer/chunk/ListChunkFactory.edit.java index 9c2c149..a9d03a6 100644 --- a/patches/minecraft/net/minecraft/client/renderer/chunk/ListChunkFactory.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/ListChunkFactory.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/ListedRenderChunk.edit.java b/patches/minecraft/net/minecraft/client/renderer/chunk/ListedRenderChunk.edit.java index 284570f..3f314b1 100644 --- a/patches/minecraft/net/minecraft/client/renderer/chunk/ListedRenderChunk.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/ListedRenderChunk.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/RenderChunk.edit.java b/patches/minecraft/net/minecraft/client/renderer/chunk/RenderChunk.edit.java index 680967e..0d6c66b 100644 --- a/patches/minecraft/net/minecraft/client/renderer/chunk/RenderChunk.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/RenderChunk.edit.java @@ -1,19 +1,18 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 5 -> CHANGE 3 : 12 @ 3 : 4 +> CHANGE 3 : 11 @ 3 : 4 ~ ~ import com.google.common.collect.Maps; ~ import com.google.common.collect.Sets; ~ -~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ~ 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; @@ -34,9 +33,7 @@ > DELETE 5 @ 5 : 7 -> CHANGE 3 : 4 @ 3 : 5 - -~ private final float[] modelviewMatrix = new float[16]; +> DELETE 3 @ 3 : 5 > INSERT 3 : 9 @ 3 @@ -57,7 +54,9 @@ ~ for (int i = 0; i < facings.length; ++i) { ~ this.field_181702_p.put(facings[i], pos.offset(facings[i], 16)); -> INSERT 17 : 26 @ 17 +> DELETE 1 @ 1 : 3 + +> INSERT 14 : 23 @ 14 + if (DeferredStateManager.isRenderingRealisticWater() && compiledchunk.getStateRealisticWater() != null + && !compiledchunk.isLayerEmpty(EnumWorldBlockLayer.REALISTIC_WATER)) { @@ -204,7 +203,9 @@ ~ compiledChunkIn.setState(worldRendererIn.func_181672_a()); ~ } -> DELETE 24 @ 24 : 35 +> DELETE 5 @ 5 : 20 + +> DELETE 4 @ 4 : 15 > CHANGE 2 : 7 @ 2 : 3 diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/SetVisibility.edit.java b/patches/minecraft/net/minecraft/client/renderer/chunk/SetVisibility.edit.java index 278b1cb..6129ea6 100644 --- a/patches/minecraft/net/minecraft/client/renderer/chunk/SetVisibility.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/SetVisibility.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/VisGraph.edit.java b/patches/minecraft/net/minecraft/client/renderer/chunk/VisGraph.edit.java index 1fd523b..eab07d2 100644 --- a/patches/minecraft/net/minecraft/client/renderer/chunk/VisGraph.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/VisGraph.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/culling/ClippingHelperImpl.edit.java b/patches/minecraft/net/minecraft/client/renderer/culling/ClippingHelperImpl.edit.java index 302c7d4..8700572 100644 --- a/patches/minecraft/net/minecraft/client/renderer/culling/ClippingHelperImpl.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/culling/ClippingHelperImpl.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/culling/Frustum.edit.java b/patches/minecraft/net/minecraft/client/renderer/culling/Frustum.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/client/renderer/culling/Frustum.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/culling/Frustum.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/ArmorStandRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/ArmorStandRenderer.edit.java index 5846c9b..9511baa 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/ArmorStandRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/ArmorStandRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/Render.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/Render.edit.java index 6223d48..ee51cbb 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/Render.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/Render.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderArrow.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderArrow.edit.java index 20e26a4..13ed588 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderArrow.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderArrow.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderBat.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderBat.edit.java index b28e0d5..ae76ce9 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderBat.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderBat.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderBiped.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderBiped.edit.java index b28e0d5..ae76ce9 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderBiped.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderBiped.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderBlaze.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderBlaze.edit.java index 31bf739..43baa4c 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderBlaze.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderBlaze.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderBoat.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderBoat.edit.java index 5846c9b..9511baa 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderBoat.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderBoat.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderCaveSpider.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderCaveSpider.edit.java index de98a05..3f67f13 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderCaveSpider.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderCaveSpider.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderChicken.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderChicken.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderChicken.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderChicken.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderCow.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderCow.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderCow.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderCow.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderCreeper.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderCreeper.edit.java index a2070f3..d458ec6 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderCreeper.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderCreeper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderDragon.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderDragon.edit.java index c084aeb..db148d0 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderDragon.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderDragon.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderEnderman.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderEnderman.edit.java index 7b0fd27..37ac49b 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderEnderman.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderEnderman.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderEndermite.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderEndermite.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderEndermite.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderEndermite.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderEntity.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderEntity.edit.java index de98a05..3f67f13 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderEntity.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderEntity.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderEntityItem.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderEntityItem.edit.java index daffe3b..f2eb4b9 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderEntityItem.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderEntityItem.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderFallingBlock.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderFallingBlock.edit.java index 4ed39be..9695650 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderFallingBlock.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderFallingBlock.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderFireball.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderFireball.edit.java index b31864f..75efef3 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderFireball.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderFireball.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderFish.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderFish.edit.java index e705d2e..4e8ee28 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderFish.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderFish.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderGhast.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderGhast.edit.java index b28e0d5..ae76ce9 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderGhast.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderGhast.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderGiantZombie.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderGiantZombie.edit.java index 70c40ed..7261ef3 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderGiantZombie.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderGiantZombie.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderGuardian.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderGuardian.edit.java index 881d51d..9451733 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderGuardian.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderGuardian.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderHorse.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderHorse.edit.java index 2d31612..f186f59 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderHorse.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderHorse.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderIronGolem.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderIronGolem.edit.java index b28e0d5..ae76ce9 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderIronGolem.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderIronGolem.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderItem.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderItem.edit.java index fa0e250..88a65bc 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderItem.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderItem.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderLeashKnot.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderLeashKnot.edit.java index b28e0d5..ae76ce9 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderLeashKnot.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderLeashKnot.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderLightningBolt.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderLightningBolt.edit.java index 0476812..8ed5f57 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderLightningBolt.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderLightningBolt.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderLiving.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderLiving.edit.java index e7fa55d..c217ae6 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderLiving.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderLiving.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderMagmaCube.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderMagmaCube.edit.java index b28e0d5..ae76ce9 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderMagmaCube.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderMagmaCube.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderManager.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderManager.edit.java index 1542328..4feb177 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderManager.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderManager.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderMinecart.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderMinecart.edit.java index b6db9ee..0e4b780 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderMinecart.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderMinecart.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderMinecartMobSpawner.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderMinecartMobSpawner.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderMinecartMobSpawner.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderMinecartMobSpawner.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderMooshroom.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderMooshroom.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderMooshroom.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderMooshroom.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderOcelot.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderOcelot.edit.java index b28e0d5..ae76ce9 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderOcelot.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderOcelot.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderPainting.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPainting.edit.java index f3a3c2b..64bc09e 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderPainting.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPainting.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderPig.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPig.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderPig.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPig.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderPigZombie.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPigZombie.edit.java index 7914e03..0b1b6db 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderPigZombie.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPigZombie.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderPlayer.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPlayer.edit.java index 21709fb..465a70b 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderPlayer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPlayer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderPotion.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPotion.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderPotion.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPotion.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderRabbit.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderRabbit.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderRabbit.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderRabbit.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSheep.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSheep.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSheep.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSheep.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSilverfish.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSilverfish.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSilverfish.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSilverfish.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSkeleton.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSkeleton.edit.java index b28e0d5..ae76ce9 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSkeleton.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSkeleton.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSlime.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSlime.edit.java index 7608441..17d17ea 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSlime.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSlime.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSnowMan.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSnowMan.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSnowMan.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSnowMan.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSnowball.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSnowball.edit.java index 712d857..26bff48 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSnowball.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSnowball.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSpider.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSpider.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSpider.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSpider.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSquid.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSquid.edit.java index b28e0d5..ae76ce9 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSquid.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSquid.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderTNTPrimed.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderTNTPrimed.edit.java index 2fe69be..a94adf0 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderTNTPrimed.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderTNTPrimed.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderTntMinecart.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderTntMinecart.edit.java index e371422..8e6c384 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderTntMinecart.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderTntMinecart.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderVillager.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderVillager.edit.java index b28e0d5..ae76ce9 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderVillager.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderVillager.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderWitch.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderWitch.edit.java index b28e0d5..ae76ce9 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderWitch.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderWitch.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderWither.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderWither.edit.java index b28e0d5..ae76ce9 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderWither.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderWither.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderWolf.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderWolf.edit.java index b28e0d5..ae76ce9 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderWolf.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderWolf.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderXPOrb.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderXPOrb.edit.java index f3a3c2b..64bc09e 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderXPOrb.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderXPOrb.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderZombie.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderZombie.edit.java index 7f74530..792932c 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderZombie.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderZombie.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RendererLivingEntity.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RendererLivingEntity.edit.java index 455565d..b190134 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RendererLivingEntity.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RendererLivingEntity.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerArmorBase.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerArmorBase.edit.java index adca35f..9c39ad0 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerArmorBase.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerArmorBase.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerArrow.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerArrow.edit.java index 93f3957..41e9a5a 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerArrow.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerArrow.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerBipedArmor.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerBipedArmor.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerBipedArmor.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerBipedArmor.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCape.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCape.edit.java index 9d4ae77..92b6440 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCape.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCape.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCreeperCharge.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCreeperCharge.edit.java index 3e6bea4..d6a1078 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCreeperCharge.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCreeperCharge.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCustomHead.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCustomHead.edit.java index 5991968..b65fa77 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCustomHead.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCustomHead.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerDeadmau5Head.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerDeadmau5Head.edit.java index e59a8fd..cc73a91 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerDeadmau5Head.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerDeadmau5Head.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEnderDragonDeath.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEnderDragonDeath.edit.java index 9c50c61..1801b13 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEnderDragonDeath.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEnderDragonDeath.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEnderDragonEyes.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEnderDragonEyes.edit.java index ecbe4c5..62c4dc7 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEnderDragonEyes.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEnderDragonEyes.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEndermanEyes.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEndermanEyes.edit.java index 992dbe3..cead693 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEndermanEyes.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEndermanEyes.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldBlock.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldBlock.edit.java index d3c10c4..ca2f51d 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldBlock.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldBlock.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldItem.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldItem.edit.java index f34182a..eb55e20 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldItem.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldItem.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldItemWitch.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldItemWitch.edit.java index f34182a..eb55e20 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldItemWitch.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldItemWitch.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerIronGolemFlower.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerIronGolemFlower.edit.java index 7937e6e..7ced632 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerIronGolemFlower.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerIronGolemFlower.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerMooshroomMushroom.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerMooshroomMushroom.edit.java index 281fc45..6571527 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerMooshroomMushroom.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerMooshroomMushroom.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSaddle.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSaddle.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSaddle.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSaddle.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSheepWool.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSheepWool.edit.java index 6547a5e..c3d71b1 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSheepWool.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSheepWool.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSlimeGel.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSlimeGel.edit.java index 2014bb5..1371a5c 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSlimeGel.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSlimeGel.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSnowmanHead.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSnowmanHead.edit.java index fb7a8e8..3b1087c 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSnowmanHead.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSnowmanHead.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSpiderEyes.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSpiderEyes.edit.java index 0abe69e..b71315b 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSpiderEyes.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSpiderEyes.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerVillagerArmor.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerVillagerArmor.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerVillagerArmor.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerVillagerArmor.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerWitherAura.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerWitherAura.edit.java index fb6a0f9..ac7e254 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerWitherAura.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerWitherAura.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerWolfCollar.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerWolfCollar.edit.java index ca3d82d..832b236 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerWolfCollar.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerWolfCollar.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/AbstractTexture.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/AbstractTexture.edit.java index 81fc204..01c990f 100644 --- a/patches/minecraft/net/minecraft/client/renderer/texture/AbstractTexture.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/texture/AbstractTexture.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/DynamicTexture.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/DynamicTexture.edit.java index a51d347..e03d83d 100644 --- a/patches/minecraft/net/minecraft/client/renderer/texture/DynamicTexture.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/texture/DynamicTexture.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/IIconCreator.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/IIconCreator.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/renderer/texture/IIconCreator.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/texture/IIconCreator.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/ITextureObject.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/ITextureObject.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/client/renderer/texture/ITextureObject.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/texture/ITextureObject.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/ITickableTextureObject.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/ITickableTextureObject.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/client/renderer/texture/ITickableTextureObject.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/texture/ITickableTextureObject.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/LayeredColorMaskTexture.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/LayeredColorMaskTexture.edit.java index 68efd3f..bbe23bc 100644 --- a/patches/minecraft/net/minecraft/client/renderer/texture/LayeredColorMaskTexture.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/texture/LayeredColorMaskTexture.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/LayeredTexture.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/LayeredTexture.edit.java index 099c835..db230ab 100644 --- a/patches/minecraft/net/minecraft/client/renderer/texture/LayeredTexture.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/texture/LayeredTexture.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/SimpleTexture.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/SimpleTexture.edit.java index 8088c68..4b5a1f0 100644 --- a/patches/minecraft/net/minecraft/client/renderer/texture/SimpleTexture.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/texture/SimpleTexture.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/Stitcher.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/Stitcher.edit.java index 6ccfdfe..3da9a8e 100644 --- a/patches/minecraft/net/minecraft/client/renderer/texture/Stitcher.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/texture/Stitcher.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/TextureClock.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/TextureClock.edit.java index f281bc2..fabebcd 100644 --- a/patches/minecraft/net/minecraft/client/renderer/texture/TextureClock.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/texture/TextureClock.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/TextureCompass.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/TextureCompass.edit.java index 35cc270..0b44a73 100644 --- a/patches/minecraft/net/minecraft/client/renderer/texture/TextureCompass.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/texture/TextureCompass.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/TextureManager.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/TextureManager.edit.java index 1a5a73d..760e33b 100644 --- a/patches/minecraft/net/minecraft/client/renderer/texture/TextureManager.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/texture/TextureManager.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude @@ -10,9 +10,11 @@ ~ import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; ~ -> CHANGE 5 : 15 @ 5 : 11 +> CHANGE 5 : 17 @ 5 : 11 ~ +~ import com.carrotsearch.hppc.ObjectIntHashMap; +~ import com.carrotsearch.hppc.ObjectIntMap; ~ import com.google.common.collect.Lists; ~ import com.google.common.collect.Maps; ~ @@ -25,7 +27,11 @@ > DELETE 6 @ 6 : 8 -> CHANGE 13 : 22 @ 13 : 18 +> CHANGE 5 : 6 @ 5 : 6 + +~ private final ObjectIntMap mapTextureCounters = new ObjectIntHashMap<>(); + +> CHANGE 7 : 16 @ 7 : 12 ~ int glTex; ~ if (resource.cachedPointerType == ResourceLocation.CACHED_POINTER_TEXTURE) { @@ -72,16 +78,26 @@ + textureLocation.cachedPointerType = ResourceLocation.CACHED_POINTER_TEXTURE; + textureLocation.cachedPointer = textureObj; -> CHANGE 5 : 11 @ 5 : 6 +> CHANGE 5 : 7 @ 5 : 12 ~ if (textureLocation.cachedPointerType == ResourceLocation.CACHED_POINTER_TEXTURE) { ~ return (ITextureObject) textureLocation.cachedPointer; -~ } else { + +> CHANGE 1 : 3 @ 1 : 2 + ~ textureLocation.cachedPointerType = ResourceLocation.CACHED_POINTER_TEXTURE; ~ return (ITextureObject) (textureLocation.cachedPointer = this.mapTextureObjects.get(textureLocation)); -~ } -> CHANGE 12 : 13 @ 12 : 13 +> INSERT 1 : 2 @ 1 + ++ } + +> INSERT 1 : 3 @ 1 + ++ public ResourceLocation getDynamicTextureLocation(String name, DynamicTexture texture) { ++ int integer = this.mapTextureCounters.getOrDefault(name, 0) + 1; + +> CHANGE 2 : 3 @ 2 : 3 ~ HString.format("dynamic/%s_%d", new Object[] { name, integer })); diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/TextureMap.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/TextureMap.edit.java index 6c0afd2..11758fc 100644 --- a/patches/minecraft/net/minecraft/client/renderer/texture/TextureMap.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/texture/TextureMap.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude @@ -11,9 +11,13 @@ + import java.util.Collection; -> INSERT 6 : 20 @ 6 +> DELETE 1 @ 1 : 2 + +> INSERT 4 : 20 @ 4 + ++ import com.carrotsearch.hppc.IntIndexedContainer; ++ import com.carrotsearch.hppc.cursors.IntCursor; + import com.google.common.collect.Lists; + import com.google.common.collect.Maps; + @@ -163,7 +167,7 @@ ~ EaglerTextureAtlasSprite textureatlassprite = (EaglerTextureAtlasSprite) entry.getValue(); -> INSERT 3 : 108 @ 3 +> INSERT 3 : 105 @ 3 + if (isEaglerPBRMode) { + try { @@ -192,7 +196,7 @@ + TextureMetadataSection texturemetadatasection = (TextureMetadataSection) iresource + .getMetadata("texture"); + if (texturemetadatasection != null) { -+ List list = texturemetadatasection.getListMipmaps(); ++ IntIndexedContainer list = texturemetadatasection.getListMipmaps(); + if (!list.isEmpty()) { + int l = abufferedimageColor[0].width; + int i1 = abufferedimageColor[0].height; @@ -201,11 +205,8 @@ + "Unable to load extra miplevels, source-texture is not power of two"); + } + } -+ -+ Iterator iterator = list.iterator(); -+ -+ while (iterator.hasNext()) { -+ int i2 = ((Integer) iterator.next()).intValue(); ++ for (IntCursor cur : list) { ++ int i2 = cur.value; + if (i2 > 0 && i2 < abufferedimageColor.length - 1 && abufferedimageColor[i2] == null) { + ResourceLocation resourcelocation2 = this.completeResourceLocation(resourcelocation, + i2); @@ -275,12 +276,21 @@ ~ ImageData[] abufferedimage = new ImageData[1 + this.mipmapLevels]; -> CHANGE 6 : 8 @ 6 : 8 +> CHANGE 4 : 5 @ 4 : 5 + +~ IntIndexedContainer list = texturemetadatasection.getListMipmaps(); + +> CHANGE 1 : 3 @ 1 : 3 ~ int l = abufferedimage[0].width; ~ int i1 = abufferedimage[0].height; -> CHANGE 18 : 20 @ 18 : 19 +> CHANGE 6 : 8 @ 6 : 10 + +~ for (IntCursor cur : list) { +~ int i2 = cur.value; + +> CHANGE 8 : 10 @ 8 : 9 ~ new Object[] { Integer.valueOf(i2), resourcelocation2 }); ~ logger.error(ioexception); diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/TextureUtil.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/TextureUtil.edit.java index f17a744..21ed4aa 100644 --- a/patches/minecraft/net/minecraft/client/renderer/texture/TextureUtil.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/texture/TextureUtil.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderEnderCrystal.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderEnderCrystal.edit.java index 8f2ebab..d1ae411 100644 --- a/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderEnderCrystal.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderEnderCrystal.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderItemFrame.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderItemFrame.edit.java index 72a7ff3..7240381 100644 --- a/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderItemFrame.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderItemFrame.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderWitherSkull.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderWitherSkull.edit.java index ad6bc7d..9beb2fd 100644 --- a/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderWitherSkull.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderWitherSkull.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityBannerRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityBannerRenderer.edit.java index 116da42..72e93a7 100644 --- a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityBannerRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityBannerRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityBeaconRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityBeaconRenderer.edit.java index 5257449..b0febad 100644 --- a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityBeaconRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityBeaconRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityChestRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityChestRenderer.edit.java index dea0afd..fce840a 100644 --- a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityChestRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityChestRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEnchantmentTableRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEnchantmentTableRenderer.edit.java index 20d8a9c..474ead8 100644 --- a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEnchantmentTableRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEnchantmentTableRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEndPortalRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEndPortalRenderer.edit.java index 7239b9e..21457d3 100644 --- a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEndPortalRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEndPortalRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEnderChestRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEnderChestRenderer.edit.java index 20d8a9c..474ead8 100644 --- a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEnderChestRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEnderChestRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityItemStackRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityItemStackRenderer.edit.java index cc0342f..4fb0f2e 100644 --- a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityItemStackRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityItemStackRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityMobSpawnerRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityMobSpawnerRenderer.edit.java index 20d8a9c..474ead8 100644 --- a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityMobSpawnerRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityMobSpawnerRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityPistonRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityPistonRenderer.edit.java index 7ae5597..18a29a7 100644 --- a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityPistonRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityPistonRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityRendererDispatcher.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityRendererDispatcher.edit.java index 1fc12ec..0d48775 100644 --- a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityRendererDispatcher.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityRendererDispatcher.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySignRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySignRenderer.edit.java index f29ef56..bd7ef18 100644 --- a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySignRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySignRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySkullRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySkullRenderer.edit.java index 0be21fd..c64db9c 100644 --- a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySkullRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySkullRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySpecialRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySpecialRenderer.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySpecialRenderer.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySpecialRenderer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/renderer/vertex/DefaultVertexFormats.edit.java b/patches/minecraft/net/minecraft/client/renderer/vertex/DefaultVertexFormats.edit.java index a25edbc..b9ddb4c 100644 --- a/patches/minecraft/net/minecraft/client/renderer/vertex/DefaultVertexFormats.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/vertex/DefaultVertexFormats.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/AbstractResourcePack.edit.java b/patches/minecraft/net/minecraft/client/resources/AbstractResourcePack.edit.java index 6eafe1d..c4aad38 100644 --- a/patches/minecraft/net/minecraft/client/resources/AbstractResourcePack.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/AbstractResourcePack.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/DefaultPlayerSkin.edit.java b/patches/minecraft/net/minecraft/client/resources/DefaultPlayerSkin.edit.java index 4a7d385..24be3ca 100644 --- a/patches/minecraft/net/minecraft/client/resources/DefaultPlayerSkin.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/DefaultPlayerSkin.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/DefaultResourcePack.edit.java b/patches/minecraft/net/minecraft/client/resources/DefaultResourcePack.edit.java index 7b3cd4c..2c1f9b9 100644 --- a/patches/minecraft/net/minecraft/client/resources/DefaultResourcePack.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/DefaultResourcePack.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/FallbackResourceManager.edit.java b/patches/minecraft/net/minecraft/client/resources/FallbackResourceManager.edit.java index b2e41e7..2fdd4c9 100644 --- a/patches/minecraft/net/minecraft/client/resources/FallbackResourceManager.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/FallbackResourceManager.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/FoliageColorReloadListener.edit.java b/patches/minecraft/net/minecraft/client/resources/FoliageColorReloadListener.edit.java index f4bb81f..5e4d673 100644 --- a/patches/minecraft/net/minecraft/client/resources/FoliageColorReloadListener.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/FoliageColorReloadListener.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/GrassColorReloadListener.edit.java b/patches/minecraft/net/minecraft/client/resources/GrassColorReloadListener.edit.java index 7f24113..edfb994 100644 --- a/patches/minecraft/net/minecraft/client/resources/GrassColorReloadListener.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/GrassColorReloadListener.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/I18n.edit.java b/patches/minecraft/net/minecraft/client/resources/I18n.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/resources/I18n.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/I18n.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/IReloadableResourceManager.edit.java b/patches/minecraft/net/minecraft/client/resources/IReloadableResourceManager.edit.java index 9c2c149..a9d03a6 100644 --- a/patches/minecraft/net/minecraft/client/resources/IReloadableResourceManager.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/IReloadableResourceManager.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/IResource.edit.java b/patches/minecraft/net/minecraft/client/resources/IResource.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/client/resources/IResource.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/IResource.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/IResourceManager.edit.java b/patches/minecraft/net/minecraft/client/resources/IResourceManager.edit.java index ea150e7..1b57a3e 100644 --- a/patches/minecraft/net/minecraft/client/resources/IResourceManager.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/IResourceManager.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/IResourceManagerReloadListener.edit.java b/patches/minecraft/net/minecraft/client/resources/IResourceManagerReloadListener.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/resources/IResourceManagerReloadListener.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/IResourceManagerReloadListener.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/IResourcePack.edit.java b/patches/minecraft/net/minecraft/client/resources/IResourcePack.edit.java index 22417eb..03f76d5 100644 --- a/patches/minecraft/net/minecraft/client/resources/IResourcePack.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/IResourcePack.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/Language.edit.java b/patches/minecraft/net/minecraft/client/resources/Language.edit.java index f58df6b..1a15466 100644 --- a/patches/minecraft/net/minecraft/client/resources/Language.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/Language.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/LanguageManager.edit.java b/patches/minecraft/net/minecraft/client/resources/LanguageManager.edit.java index 2565a58..591ed89 100644 --- a/patches/minecraft/net/minecraft/client/resources/LanguageManager.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/LanguageManager.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/Locale.edit.java b/patches/minecraft/net/minecraft/client/resources/Locale.edit.java index 7930551..37a770e 100644 --- a/patches/minecraft/net/minecraft/client/resources/Locale.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/Locale.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/ResourcePackFileNotFoundException.edit.java b/patches/minecraft/net/minecraft/client/resources/ResourcePackFileNotFoundException.edit.java index 5a50d38..35fe1a4 100644 --- a/patches/minecraft/net/minecraft/client/resources/ResourcePackFileNotFoundException.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/ResourcePackFileNotFoundException.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntry.edit.java b/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntry.edit.java index a16f17e..8780a11 100644 --- a/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntry.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntry.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntryDefault.edit.java b/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntryDefault.edit.java index 03be68d..de66207 100644 --- a/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntryDefault.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntryDefault.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntryFound.edit.java b/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntryFound.edit.java index fda6d7f..898b611 100644 --- a/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntryFound.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntryFound.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/ResourcePackRepository.edit.java b/patches/minecraft/net/minecraft/client/resources/ResourcePackRepository.edit.java index 6f2ef20..d18a789 100644 --- a/patches/minecraft/net/minecraft/client/resources/ResourcePackRepository.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/ResourcePackRepository.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/SimpleReloadableResourceManager.edit.java b/patches/minecraft/net/minecraft/client/resources/SimpleReloadableResourceManager.edit.java index 50d84fc..3d58856 100644 --- a/patches/minecraft/net/minecraft/client/resources/SimpleReloadableResourceManager.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/SimpleReloadableResourceManager.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/SimpleResource.edit.java b/patches/minecraft/net/minecraft/client/resources/SimpleResource.edit.java index 9d982f9..283b9fa 100644 --- a/patches/minecraft/net/minecraft/client/resources/SimpleResource.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/SimpleResource.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/data/AnimationMetadataSection.edit.java b/patches/minecraft/net/minecraft/client/resources/data/AnimationMetadataSection.edit.java index 426d385..20390d3 100644 --- a/patches/minecraft/net/minecraft/client/resources/data/AnimationMetadataSection.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/data/AnimationMetadataSection.edit.java @@ -1,22 +1,25 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> DELETE 2 @ 2 : 3 +> DELETE 2 @ 2 : 4 -> DELETE 3 @ 3 : 5 +> CHANGE 1 : 3 @ 1 : 4 -> INSERT 1 : 3 @ 1 +~ import com.carrotsearch.hppc.IntHashSet; +~ import com.carrotsearch.hppc.IntSet; -+ import com.google.common.collect.Sets; -+ +> CHANGE 54 : 56 @ 54 : 56 -> CHANGE 56 : 58 @ 56 : 58 +~ public IntSet getFrameIndexSet() { +~ IntHashSet hashset = new IntHashSet(); + +> CHANGE 1 : 3 @ 1 : 3 ~ for (int i = 0, l = this.animationFrames.size(); i < l; ++i) { -~ hashset.add(Integer.valueOf(this.animationFrames.get(i).getFrameIndex())); +~ hashset.add(this.animationFrames.get(i).getFrameIndex()); > EOF diff --git a/patches/minecraft/net/minecraft/client/resources/data/AnimationMetadataSectionSerializer.edit.java b/patches/minecraft/net/minecraft/client/resources/data/AnimationMetadataSectionSerializer.edit.java index 1475fe3..0e9c3ee 100644 --- a/patches/minecraft/net/minecraft/client/resources/data/AnimationMetadataSectionSerializer.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/data/AnimationMetadataSectionSerializer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/data/BaseMetadataSectionSerializer.edit.java b/patches/minecraft/net/minecraft/client/resources/data/BaseMetadataSectionSerializer.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/client/resources/data/BaseMetadataSectionSerializer.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/data/BaseMetadataSectionSerializer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/data/FontMetadataSection.edit.java b/patches/minecraft/net/minecraft/client/resources/data/FontMetadataSection.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/client/resources/data/FontMetadataSection.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/data/FontMetadataSection.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/data/FontMetadataSectionSerializer.edit.java b/patches/minecraft/net/minecraft/client/resources/data/FontMetadataSectionSerializer.edit.java index f65f813..02abcc3 100644 --- a/patches/minecraft/net/minecraft/client/resources/data/FontMetadataSectionSerializer.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/data/FontMetadataSectionSerializer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/data/IMetadataSectionSerializer.edit.java b/patches/minecraft/net/minecraft/client/resources/data/IMetadataSectionSerializer.edit.java index d5e3ce8..fa62694 100644 --- a/patches/minecraft/net/minecraft/client/resources/data/IMetadataSectionSerializer.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/data/IMetadataSectionSerializer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/data/IMetadataSerializer.edit.java b/patches/minecraft/net/minecraft/client/resources/data/IMetadataSerializer.edit.java index 078988b..9b03e00 100644 --- a/patches/minecraft/net/minecraft/client/resources/data/IMetadataSerializer.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/data/IMetadataSerializer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/data/LanguageMetadataSection.edit.java b/patches/minecraft/net/minecraft/client/resources/data/LanguageMetadataSection.edit.java index 4d0a732..8c9d894 100644 --- a/patches/minecraft/net/minecraft/client/resources/data/LanguageMetadataSection.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/data/LanguageMetadataSection.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/data/LanguageMetadataSectionSerializer.edit.java b/patches/minecraft/net/minecraft/client/resources/data/LanguageMetadataSectionSerializer.edit.java index 5c2fe5c..47e5fb4 100644 --- a/patches/minecraft/net/minecraft/client/resources/data/LanguageMetadataSectionSerializer.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/data/LanguageMetadataSectionSerializer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/data/PackMetadataSection.edit.java b/patches/minecraft/net/minecraft/client/resources/data/PackMetadataSection.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/client/resources/data/PackMetadataSection.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/data/PackMetadataSection.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/data/PackMetadataSectionSerializer.edit.java b/patches/minecraft/net/minecraft/client/resources/data/PackMetadataSectionSerializer.edit.java index 5d80dad..1115d30 100644 --- a/patches/minecraft/net/minecraft/client/resources/data/PackMetadataSectionSerializer.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/data/PackMetadataSectionSerializer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/data/TextureMetadataSection.edit.java b/patches/minecraft/net/minecraft/client/resources/data/TextureMetadataSection.edit.java index 0d7c65a..1c82fa2 100644 --- a/patches/minecraft/net/minecraft/client/resources/data/TextureMetadataSection.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/data/TextureMetadataSection.edit.java @@ -1,10 +1,25 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> DELETE 4 @ 4 : 5 +> CHANGE 2 : 3 @ 2 : 5 + +~ import com.carrotsearch.hppc.IntIndexedContainer; + +> CHANGE 4 : 5 @ 4 : 5 + +~ private final IntIndexedContainer listMipmaps; + +> CHANGE 1 : 2 @ 1 : 2 + +~ public TextureMetadataSection(boolean parFlag, boolean parFlag2, IntIndexedContainer parList) { + +> CHANGE 13 : 15 @ 13 : 15 + +~ public IntIndexedContainer getListMipmaps() { +~ return listMipmaps; > EOF diff --git a/patches/minecraft/net/minecraft/client/resources/data/TextureMetadataSectionSerializer.edit.java b/patches/minecraft/net/minecraft/client/resources/data/TextureMetadataSectionSerializer.edit.java index 9068067..e3a3429 100644 --- a/patches/minecraft/net/minecraft/client/resources/data/TextureMetadataSectionSerializer.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/data/TextureMetadataSectionSerializer.edit.java @@ -1,30 +1,29 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> DELETE 2 @ 2 : 9 +> CHANGE 2 : 5 @ 2 : 13 -> DELETE 1 @ 1 : 4 +~ import org.json.JSONArray; +~ import org.json.JSONException; +~ import org.json.JSONObject; -> INSERT 1 : 7 @ 1 +> INSERT 1 : 3 @ 1 -+ import org.json.JSONArray; -+ import org.json.JSONException; -+ import org.json.JSONObject; -+ -+ import com.google.common.collect.Lists; ++ import com.carrotsearch.hppc.IntArrayList; + -> CHANGE 1 : 4 @ 1 : 6 +> CHANGE 1 : 5 @ 1 : 7 ~ public TextureMetadataSection deserialize(JSONObject jsonobject) throws JSONException { ~ boolean flag = jsonobject.optBoolean("blur", false); ~ boolean flag1 = jsonobject.optBoolean("clamp", false); +~ IntArrayList arraylist = new IntArrayList(); -> CHANGE 3 : 4 @ 3 : 4 +> CHANGE 2 : 3 @ 2 : 3 ~ JSONArray jsonarray = jsonobject.getJSONArray("mipmaps"); diff --git a/patches/minecraft/net/minecraft/client/resources/model/BuiltInModel.edit.java b/patches/minecraft/net/minecraft/client/resources/model/BuiltInModel.edit.java index 88136bb..aaf38eb 100644 --- a/patches/minecraft/net/minecraft/client/resources/model/BuiltInModel.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/model/BuiltInModel.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/model/IBakedModel.edit.java b/patches/minecraft/net/minecraft/client/resources/model/IBakedModel.edit.java index 32f694f..ce5ea69 100644 --- a/patches/minecraft/net/minecraft/client/resources/model/IBakedModel.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/model/IBakedModel.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/model/ModelBakery.edit.java b/patches/minecraft/net/minecraft/client/resources/model/ModelBakery.edit.java index 154ff68..c2523cc 100644 --- a/patches/minecraft/net/minecraft/client/resources/model/ModelBakery.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/model/ModelBakery.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude @@ -99,17 +99,17 @@ + if (deferred) { + ModelBlock currentBlockModel = modelblock; + ResourceLocation currentResourceLocation = modelblockdefinition$variant.getModelLocation(); -+ Integer blockId = null; ++ int blockId = -1; + do { -+ blockId = BlockVertexIDs.modelToID.get(currentResourceLocation.toString()); -+ if (blockId != null) { ++ blockId = BlockVertexIDs.modelToID.getOrDefault(currentResourceLocation.toString(), -1); ++ if (blockId != -1) { + break; + } + currentResourceLocation = currentBlockModel.getParentLocation(); + currentBlockModel = models.get(currentResourceLocation); + } while (currentBlockModel != null); -+ if (blockId != null) { -+ VertexMarkerState.markId = blockId.intValue(); ++ if (blockId != -1) { ++ VertexMarkerState.markId = blockId; + try { + weightedbakedmodel$builder.add( + this.bakeModel(modelblock, modelblockdefinition$variant.getRotation(), diff --git a/patches/minecraft/net/minecraft/client/resources/model/ModelManager.edit.java b/patches/minecraft/net/minecraft/client/resources/model/ModelManager.edit.java index 7507481..3df906f 100644 --- a/patches/minecraft/net/minecraft/client/resources/model/ModelManager.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/model/ModelManager.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/model/ModelResourceLocation.edit.java b/patches/minecraft/net/minecraft/client/resources/model/ModelResourceLocation.edit.java index c2c9449..66fe45a 100644 --- a/patches/minecraft/net/minecraft/client/resources/model/ModelResourceLocation.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/model/ModelResourceLocation.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/model/ModelRotation.edit.java b/patches/minecraft/net/minecraft/client/resources/model/ModelRotation.edit.java index d0aab90..581d6f0 100644 --- a/patches/minecraft/net/minecraft/client/resources/model/ModelRotation.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/model/ModelRotation.edit.java @@ -1,26 +1,33 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> DELETE 2 @ 2 : 3 +> CHANGE 2 : 7 @ 2 : 4 -> INSERT 1 : 6 @ 1 - -+ -+ import com.google.common.collect.Maps; -+ -+ import net.lax1dude.eaglercraft.v1_8.vector.Matrix4f; -+ import net.lax1dude.eaglercraft.v1_8.vector.Vector3f; +~ import com.carrotsearch.hppc.IntObjectHashMap; +~ import com.carrotsearch.hppc.IntObjectMap; +~ +~ import net.lax1dude.eaglercraft.v1_8.vector.Matrix4f; +~ import net.lax1dude.eaglercraft.v1_8.vector.Vector3f; > DELETE 2 @ 2 : 4 -> CHANGE 75 : 78 @ 75 : 77 +> CHANGE 6 : 7 @ 6 : 7 + +~ private static final IntObjectMap mapRotations = new IntObjectHashMap<>(); + +> CHANGE 63 : 65 @ 63 : 65 + +~ return mapRotations +~ .get(combineXY(MathHelper.normalizeAngle(parInt1, 360), MathHelper.normalizeAngle(parInt2, 360))); + +> CHANGE 3 : 6 @ 3 : 5 ~ ModelRotation[] lst = values(); ~ for (int i = 0; i < lst.length; ++i) { -~ mapRotations.put(Integer.valueOf(lst[i].combinedXY), lst[i]); +~ mapRotations.put(lst[i].combinedXY, lst[i]); > EOF diff --git a/patches/minecraft/net/minecraft/client/resources/model/SimpleBakedModel.edit.java b/patches/minecraft/net/minecraft/client/resources/model/SimpleBakedModel.edit.java index 758586e..63bcdd5 100644 --- a/patches/minecraft/net/minecraft/client/resources/model/SimpleBakedModel.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/model/SimpleBakedModel.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/resources/model/WeightedBakedModel.edit.java b/patches/minecraft/net/minecraft/client/resources/model/WeightedBakedModel.edit.java index 515dbca..d2ff7c2 100644 --- a/patches/minecraft/net/minecraft/client/resources/model/WeightedBakedModel.edit.java +++ b/patches/minecraft/net/minecraft/client/resources/model/WeightedBakedModel.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/settings/GameSettings.edit.java b/patches/minecraft/net/minecraft/client/settings/GameSettings.edit.java index 4f16f3e..e520bb4 100644 --- a/patches/minecraft/net/minecraft/client/settings/GameSettings.edit.java +++ b/patches/minecraft/net/minecraft/client/settings/GameSettings.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude @@ -48,7 +48,11 @@ > DELETE 5 @ 5 : 7 -> DELETE 5 @ 5 : 11 +> INSERT 3 : 4 @ 3 + ++ import net.minecraft.util.EnumChatFormatting; + +> DELETE 2 @ 2 : 8 > DELETE 3 @ 3 : 8 @@ -337,7 +341,7 @@ ~ .calculateChatboxHeight( -> CHANGE 2 : 25 @ 2 : 36 +> CHANGE 2 : 30 @ 2 : 36 ~ : (parOptions == GameSettings.Options.CHAT_WIDTH ~ ? s + GuiNewChat @@ -362,6 +366,11 @@ ~ * 100.0F) ~ + "%") ~ : "yee"))))))))))))); +~ } else if (EagRuntime.getPlatformType() != EnumPlatformType.DESKTOP +~ && parOptions == GameSettings.Options.EAGLER_VSYNC) { +~ boolean flag = this.getOptionOrdinalValue(parOptions); +~ return flag ? s + I18n.format("options.on", new Object[0]) +~ : EnumChatFormatting.RED + s + I18n.format("options.off", new Object[0]); > DELETE 11 @ 11 : 19 diff --git a/patches/minecraft/net/minecraft/client/settings/KeyBinding.edit.java b/patches/minecraft/net/minecraft/client/settings/KeyBinding.edit.java index 5bf2077..27f7b8a 100644 --- a/patches/minecraft/net/minecraft/client/settings/KeyBinding.edit.java +++ b/patches/minecraft/net/minecraft/client/settings/KeyBinding.edit.java @@ -1,27 +1,52 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 4 -> INSERT 2 : 6 @ 2 +> INSERT 2 : 8 @ 2 + ++ import com.carrotsearch.hppc.IntObjectHashMap; ++ import com.carrotsearch.hppc.IntObjectMap; + import com.google.common.collect.Lists; + import com.google.common.collect.Sets; + -> CHANGE 35 : 37 @ 35 : 37 +> DELETE 1 @ 1 : 2 + +> CHANGE 3 : 4 @ 3 : 4 + +~ private static final IntObjectMap hash = new IntObjectHashMap<>(); + +> CHANGE 10 : 11 @ 10 : 11 + +~ KeyBinding keybinding = hash.get(keyCode); + +> CHANGE 9 : 10 @ 9 : 10 + +~ KeyBinding keybinding = hash.get(keyCode); + +> CHANGE 8 : 10 @ 8 : 10 ~ for (int i = 0, l = keybindArray.size(); i < l; ++i) { ~ keybindArray.get(i).unpressKey(); -> CHANGE 7 : 9 @ 7 : 8 +> CHANGE 5 : 6 @ 5 : 6 + +~ hash.clear(); + +> CHANGE 1 : 4 @ 1 : 3 ~ for (int i = 0, l = keybindArray.size(); i < l; ++i) { ~ KeyBinding keybinding = keybindArray.get(i); +~ hash.put(keybinding.keyCode, keybinding); + +> CHANGE 14 : 15 @ 14 : 15 + +~ hash.put(keyCode, this); > EOF diff --git a/patches/minecraft/net/minecraft/client/stream/IStream.edit.java b/patches/minecraft/net/minecraft/client/stream/IStream.edit.java index acf6819..6b3c6f1 100644 --- a/patches/minecraft/net/minecraft/client/stream/IStream.edit.java +++ b/patches/minecraft/net/minecraft/client/stream/IStream.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/util/JsonBlendingMode.edit.java b/patches/minecraft/net/minecraft/client/util/JsonBlendingMode.edit.java index ee89662..083f92b 100644 --- a/patches/minecraft/net/minecraft/client/util/JsonBlendingMode.edit.java +++ b/patches/minecraft/net/minecraft/client/util/JsonBlendingMode.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/client/util/JsonException.edit.java b/patches/minecraft/net/minecraft/client/util/JsonException.edit.java index f0108c9..3cdba24 100644 --- a/patches/minecraft/net/minecraft/client/util/JsonException.edit.java +++ b/patches/minecraft/net/minecraft/client/util/JsonException.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandBase.edit.java b/patches/minecraft/net/minecraft/command/CommandBase.edit.java index 6bce1e9..ac09048 100644 --- a/patches/minecraft/net/minecraft/command/CommandBase.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandBase.edit.java @@ -1,11 +1,16 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> INSERT 6 : 7 @ 6 +> INSERT 2 : 4 @ 2 + ++ import com.carrotsearch.hppc.ObjectContainer; ++ import com.carrotsearch.hppc.cursors.ObjectCursor; + +> INSERT 4 : 5 @ 4 + @@ -27,4 +32,23 @@ ~ EaglercraftUUID uuid = EaglercraftUUID.fromString(parString1); +> INSERT 317 : 333 @ 317 + ++ public static List getListOfStringsMatchingLastWord(String[] parArrayOfString, ++ ObjectContainer parCollection) { ++ String s = parArrayOfString[parArrayOfString.length - 1]; ++ ArrayList arraylist = Lists.newArrayList(); ++ if (!parCollection.isEmpty()) { ++ for (ObjectCursor s1_ : parCollection) { ++ String s1 = s1_.value; ++ if (doesStringStartWith(s, s1)) { ++ arraylist.add(s1); ++ } ++ } ++ } ++ ++ return arraylist; ++ } ++ + > EOF diff --git a/patches/minecraft/net/minecraft/command/CommandBlockData.edit.java b/patches/minecraft/net/minecraft/command/CommandBlockData.edit.java index 4900322..af5bff3 100644 --- a/patches/minecraft/net/minecraft/command/CommandBlockData.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandBlockData.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandClearInventory.edit.java b/patches/minecraft/net/minecraft/command/CommandClearInventory.edit.java index fd97d31..2b7371b 100644 --- a/patches/minecraft/net/minecraft/command/CommandClearInventory.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandClearInventory.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandClone.edit.java b/patches/minecraft/net/minecraft/command/CommandClone.edit.java index fd286ed..e0de342 100644 --- a/patches/minecraft/net/minecraft/command/CommandClone.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandClone.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandCompare.edit.java b/patches/minecraft/net/minecraft/command/CommandCompare.edit.java index 04a848a..7691ff9 100644 --- a/patches/minecraft/net/minecraft/command/CommandCompare.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandCompare.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandDefaultGameMode.edit.java b/patches/minecraft/net/minecraft/command/CommandDefaultGameMode.edit.java index e04e6b8..e8ec50a 100644 --- a/patches/minecraft/net/minecraft/command/CommandDefaultGameMode.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandDefaultGameMode.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandDifficulty.edit.java b/patches/minecraft/net/minecraft/command/CommandDifficulty.edit.java index 940400a..276cc0e 100644 --- a/patches/minecraft/net/minecraft/command/CommandDifficulty.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandDifficulty.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandEffect.edit.java b/patches/minecraft/net/minecraft/command/CommandEffect.edit.java index 4900322..af5bff3 100644 --- a/patches/minecraft/net/minecraft/command/CommandEffect.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandEffect.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandEnchant.edit.java b/patches/minecraft/net/minecraft/command/CommandEnchant.edit.java index 317b685..85c621e 100644 --- a/patches/minecraft/net/minecraft/command/CommandEnchant.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandEnchant.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandEntityData.edit.java b/patches/minecraft/net/minecraft/command/CommandEntityData.edit.java index 93eb289..45c7a4e 100644 --- a/patches/minecraft/net/minecraft/command/CommandEntityData.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandEntityData.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandExecuteAt.edit.java b/patches/minecraft/net/minecraft/command/CommandExecuteAt.edit.java index 45ee67e..b357136 100644 --- a/patches/minecraft/net/minecraft/command/CommandExecuteAt.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandExecuteAt.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandFill.edit.java b/patches/minecraft/net/minecraft/command/CommandFill.edit.java index 73b1ce5..2049972 100644 --- a/patches/minecraft/net/minecraft/command/CommandFill.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandFill.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandGameMode.edit.java b/patches/minecraft/net/minecraft/command/CommandGameMode.edit.java index 815cb1e..b328e3f 100644 --- a/patches/minecraft/net/minecraft/command/CommandGameMode.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandGameMode.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandGameRule.edit.java b/patches/minecraft/net/minecraft/command/CommandGameRule.edit.java index b27cf88..6c4a581 100644 --- a/patches/minecraft/net/minecraft/command/CommandGameRule.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandGameRule.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandGive.edit.java b/patches/minecraft/net/minecraft/command/CommandGive.edit.java index ca4e730..754edbe 100644 --- a/patches/minecraft/net/minecraft/command/CommandGive.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandGive.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandHandler.edit.java b/patches/minecraft/net/minecraft/command/CommandHandler.edit.java index 487da41..1d56667 100644 --- a/patches/minecraft/net/minecraft/command/CommandHandler.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandHandler.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandHelp.edit.java b/patches/minecraft/net/minecraft/command/CommandHelp.edit.java index a6010ec..de40193 100644 --- a/patches/minecraft/net/minecraft/command/CommandHelp.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandHelp.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandKill.edit.java b/patches/minecraft/net/minecraft/command/CommandKill.edit.java index 0c4dac1..24a6356 100644 --- a/patches/minecraft/net/minecraft/command/CommandKill.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandKill.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandNotFoundException.edit.java b/patches/minecraft/net/minecraft/command/CommandNotFoundException.edit.java index 5d95064..73587b3 100644 --- a/patches/minecraft/net/minecraft/command/CommandNotFoundException.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandNotFoundException.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandParticle.edit.java b/patches/minecraft/net/minecraft/command/CommandParticle.edit.java index 1b9af6f..47aa3b4 100644 --- a/patches/minecraft/net/minecraft/command/CommandParticle.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandParticle.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandPlaySound.edit.java b/patches/minecraft/net/minecraft/command/CommandPlaySound.edit.java index a47ee1d..2752643 100644 --- a/patches/minecraft/net/minecraft/command/CommandPlaySound.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandPlaySound.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandReplaceItem.edit.java b/patches/minecraft/net/minecraft/command/CommandReplaceItem.edit.java index ce71b4c..53c436a 100644 --- a/patches/minecraft/net/minecraft/command/CommandReplaceItem.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandReplaceItem.edit.java @@ -1,14 +1,69 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> DELETE 6 @ 6 : 12 +> DELETE 2 @ 2 : 3 -> INSERT 14 : 15 @ 14 +> CHANGE 1 : 5 @ 1 : 2 +~ +~ import com.carrotsearch.hppc.ObjectIntHashMap; +~ import com.carrotsearch.hppc.ObjectIntMap; +~ + +> DELETE 1 @ 1 : 7 + +> DELETE 14 @ 14 : 15 + +> INSERT 1 : 3 @ 1 + ++ private static final ObjectIntMap SHORTCUTS = new ObjectIntHashMap<>(); + +> CHANGE 116 : 117 @ 116 : 117 + +~ return SHORTCUTS.get(shortcut); + +> CHANGE 16 : 17 @ 16 : 17 + +~ : getListOfStringsMatchingLastWord(astring, SHORTCUTS.keys())))); + +> CHANGE 12 : 13 @ 12 : 13 + +~ SHORTCUTS.put("slot.container." + i, i); + +> CHANGE 3 : 4 @ 3 : 4 + +~ SHORTCUTS.put("slot.hotbar." + j, j); + +> CHANGE 3 : 4 @ 3 : 4 + +~ SHORTCUTS.put("slot.inventory." + k, 9 + k); + +> CHANGE 3 : 4 @ 3 : 4 + +~ SHORTCUTS.put("slot.enderchest." + l, 200 + l); + +> CHANGE 3 : 4 @ 3 : 4 + +~ SHORTCUTS.put("slot.villager." + i1, 300 + i1); + +> CHANGE 3 : 4 @ 3 : 4 + +~ SHORTCUTS.put("slot.horse." + j1, 500 + j1); + +> CHANGE 2 : 10 @ 2 : 10 + +~ SHORTCUTS.put("slot.weapon", 99); +~ SHORTCUTS.put("slot.armor.head", 103); +~ SHORTCUTS.put("slot.armor.chest", 102); +~ SHORTCUTS.put("slot.armor.legs", 101); +~ SHORTCUTS.put("slot.armor.feet", 100); +~ SHORTCUTS.put("slot.horse.saddle", 400); +~ SHORTCUTS.put("slot.horse.armor", 401); +~ SHORTCUTS.put("slot.horse.chest", 499); + > EOF diff --git a/patches/minecraft/net/minecraft/command/CommandResultStats.edit.java b/patches/minecraft/net/minecraft/command/CommandResultStats.edit.java index 707dfb1..6c754c4 100644 --- a/patches/minecraft/net/minecraft/command/CommandResultStats.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandResultStats.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandServerKick.edit.java b/patches/minecraft/net/minecraft/command/CommandServerKick.edit.java index f1bcc50..125e344 100644 --- a/patches/minecraft/net/minecraft/command/CommandServerKick.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandServerKick.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandSetPlayerTimeout.edit.java b/patches/minecraft/net/minecraft/command/CommandSetPlayerTimeout.edit.java index a4d21d0..d69d8db 100644 --- a/patches/minecraft/net/minecraft/command/CommandSetPlayerTimeout.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandSetPlayerTimeout.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandSetSpawnpoint.edit.java b/patches/minecraft/net/minecraft/command/CommandSetSpawnpoint.edit.java index 1d7ad25..ca88a89 100644 --- a/patches/minecraft/net/minecraft/command/CommandSetSpawnpoint.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandSetSpawnpoint.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandShowSeed.edit.java b/patches/minecraft/net/minecraft/command/CommandShowSeed.edit.java index 3259a40..f22d468 100644 --- a/patches/minecraft/net/minecraft/command/CommandShowSeed.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandShowSeed.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandSpreadPlayers.edit.java b/patches/minecraft/net/minecraft/command/CommandSpreadPlayers.edit.java index cbdd7b3..532bc10 100644 --- a/patches/minecraft/net/minecraft/command/CommandSpreadPlayers.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandSpreadPlayers.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandStats.edit.java b/patches/minecraft/net/minecraft/command/CommandStats.edit.java index 24869ce..b3151ea 100644 --- a/patches/minecraft/net/minecraft/command/CommandStats.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandStats.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandTime.edit.java b/patches/minecraft/net/minecraft/command/CommandTime.edit.java index 762437b..c4123ad 100644 --- a/patches/minecraft/net/minecraft/command/CommandTime.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandTime.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandTitle.edit.java b/patches/minecraft/net/minecraft/command/CommandTitle.edit.java index c74e959..d48a5c4 100644 --- a/patches/minecraft/net/minecraft/command/CommandTitle.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandTitle.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandToggleDownfall.edit.java b/patches/minecraft/net/minecraft/command/CommandToggleDownfall.edit.java index 6d81d15..1da1c67 100644 --- a/patches/minecraft/net/minecraft/command/CommandToggleDownfall.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandToggleDownfall.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandTrigger.edit.java b/patches/minecraft/net/minecraft/command/CommandTrigger.edit.java index d9f1f3c..a776283 100644 --- a/patches/minecraft/net/minecraft/command/CommandTrigger.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandTrigger.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandWeather.edit.java b/patches/minecraft/net/minecraft/command/CommandWeather.edit.java index 5df0ee5..1b3180a 100644 --- a/patches/minecraft/net/minecraft/command/CommandWeather.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandWeather.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandWorldBorder.edit.java b/patches/minecraft/net/minecraft/command/CommandWorldBorder.edit.java index 084a84a..12f530f 100644 --- a/patches/minecraft/net/minecraft/command/CommandWorldBorder.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandWorldBorder.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/CommandXP.edit.java b/patches/minecraft/net/minecraft/command/CommandXP.edit.java index 762437b..c4123ad 100644 --- a/patches/minecraft/net/minecraft/command/CommandXP.edit.java +++ b/patches/minecraft/net/minecraft/command/CommandXP.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/EntityNotFoundException.edit.java b/patches/minecraft/net/minecraft/command/EntityNotFoundException.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/command/EntityNotFoundException.edit.java +++ b/patches/minecraft/net/minecraft/command/EntityNotFoundException.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/IAdminCommand.edit.java b/patches/minecraft/net/minecraft/command/IAdminCommand.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/command/IAdminCommand.edit.java +++ b/patches/minecraft/net/minecraft/command/IAdminCommand.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/ICommand.edit.java b/patches/minecraft/net/minecraft/command/ICommand.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/command/ICommand.edit.java +++ b/patches/minecraft/net/minecraft/command/ICommand.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/ICommandManager.edit.java b/patches/minecraft/net/minecraft/command/ICommandManager.edit.java index a7f89c1..300ea78 100644 --- a/patches/minecraft/net/minecraft/command/ICommandManager.edit.java +++ b/patches/minecraft/net/minecraft/command/ICommandManager.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/ICommandSender.edit.java b/patches/minecraft/net/minecraft/command/ICommandSender.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/command/ICommandSender.edit.java +++ b/patches/minecraft/net/minecraft/command/ICommandSender.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/NumberInvalidException.edit.java b/patches/minecraft/net/minecraft/command/NumberInvalidException.edit.java index 5d95064..73587b3 100644 --- a/patches/minecraft/net/minecraft/command/NumberInvalidException.edit.java +++ b/patches/minecraft/net/minecraft/command/NumberInvalidException.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/PlayerNotFoundException.edit.java b/patches/minecraft/net/minecraft/command/PlayerNotFoundException.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/command/PlayerNotFoundException.edit.java +++ b/patches/minecraft/net/minecraft/command/PlayerNotFoundException.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/PlayerSelector.edit.java b/patches/minecraft/net/minecraft/command/PlayerSelector.edit.java index a62133a..cbde050 100644 --- a/patches/minecraft/net/minecraft/command/PlayerSelector.edit.java +++ b/patches/minecraft/net/minecraft/command/PlayerSelector.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/ServerCommandManager.edit.java b/patches/minecraft/net/minecraft/command/ServerCommandManager.edit.java index 1db682e..e3740ac 100644 --- a/patches/minecraft/net/minecraft/command/ServerCommandManager.edit.java +++ b/patches/minecraft/net/minecraft/command/ServerCommandManager.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/SyntaxErrorException.edit.java b/patches/minecraft/net/minecraft/command/SyntaxErrorException.edit.java index 5d95064..73587b3 100644 --- a/patches/minecraft/net/minecraft/command/SyntaxErrorException.edit.java +++ b/patches/minecraft/net/minecraft/command/SyntaxErrorException.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/WrongUsageException.edit.java b/patches/minecraft/net/minecraft/command/WrongUsageException.edit.java index 5d95064..73587b3 100644 --- a/patches/minecraft/net/minecraft/command/WrongUsageException.edit.java +++ b/patches/minecraft/net/minecraft/command/WrongUsageException.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/server/CommandAchievement.edit.java b/patches/minecraft/net/minecraft/command/server/CommandAchievement.edit.java index b0d75c7..fca211f 100644 --- a/patches/minecraft/net/minecraft/command/server/CommandAchievement.edit.java +++ b/patches/minecraft/net/minecraft/command/server/CommandAchievement.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/server/CommandBlockLogic.edit.java b/patches/minecraft/net/minecraft/command/server/CommandBlockLogic.edit.java index 4de8714..7fed08e 100644 --- a/patches/minecraft/net/minecraft/command/server/CommandBlockLogic.edit.java +++ b/patches/minecraft/net/minecraft/command/server/CommandBlockLogic.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/server/CommandBroadcast.edit.java b/patches/minecraft/net/minecraft/command/server/CommandBroadcast.edit.java index f531632..ae8ec1f 100644 --- a/patches/minecraft/net/minecraft/command/server/CommandBroadcast.edit.java +++ b/patches/minecraft/net/minecraft/command/server/CommandBroadcast.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/server/CommandEmote.edit.java b/patches/minecraft/net/minecraft/command/server/CommandEmote.edit.java index 859dc3a..0d27012 100644 --- a/patches/minecraft/net/minecraft/command/server/CommandEmote.edit.java +++ b/patches/minecraft/net/minecraft/command/server/CommandEmote.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/server/CommandListPlayers.edit.java b/patches/minecraft/net/minecraft/command/server/CommandListPlayers.edit.java index e395576..a2c335e 100644 --- a/patches/minecraft/net/minecraft/command/server/CommandListPlayers.edit.java +++ b/patches/minecraft/net/minecraft/command/server/CommandListPlayers.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/server/CommandMessage.edit.java b/patches/minecraft/net/minecraft/command/server/CommandMessage.edit.java index 8a83cca..f586fb3 100644 --- a/patches/minecraft/net/minecraft/command/server/CommandMessage.edit.java +++ b/patches/minecraft/net/minecraft/command/server/CommandMessage.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/server/CommandMessageRaw.edit.java b/patches/minecraft/net/minecraft/command/server/CommandMessageRaw.edit.java index 50ed407..143c8b2 100644 --- a/patches/minecraft/net/minecraft/command/server/CommandMessageRaw.edit.java +++ b/patches/minecraft/net/minecraft/command/server/CommandMessageRaw.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/server/CommandScoreboard.edit.java b/patches/minecraft/net/minecraft/command/server/CommandScoreboard.edit.java index f72536d..66f3abc 100644 --- a/patches/minecraft/net/minecraft/command/server/CommandScoreboard.edit.java +++ b/patches/minecraft/net/minecraft/command/server/CommandScoreboard.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/server/CommandSetBlock.edit.java b/patches/minecraft/net/minecraft/command/server/CommandSetBlock.edit.java index bc82325..cc2856b 100644 --- a/patches/minecraft/net/minecraft/command/server/CommandSetBlock.edit.java +++ b/patches/minecraft/net/minecraft/command/server/CommandSetBlock.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/server/CommandSetDefaultSpawnpoint.edit.java b/patches/minecraft/net/minecraft/command/server/CommandSetDefaultSpawnpoint.edit.java index 9e4f0d7..1100c6c 100644 --- a/patches/minecraft/net/minecraft/command/server/CommandSetDefaultSpawnpoint.edit.java +++ b/patches/minecraft/net/minecraft/command/server/CommandSetDefaultSpawnpoint.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/server/CommandSummon.edit.java b/patches/minecraft/net/minecraft/command/server/CommandSummon.edit.java index 9ea56e6..df5ef95 100644 --- a/patches/minecraft/net/minecraft/command/server/CommandSummon.edit.java +++ b/patches/minecraft/net/minecraft/command/server/CommandSummon.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/server/CommandTeleport.edit.java b/patches/minecraft/net/minecraft/command/server/CommandTeleport.edit.java index 89a1d9e..6f0ed20 100644 --- a/patches/minecraft/net/minecraft/command/server/CommandTeleport.edit.java +++ b/patches/minecraft/net/minecraft/command/server/CommandTeleport.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/server/CommandTestFor.edit.java b/patches/minecraft/net/minecraft/command/server/CommandTestFor.edit.java index 89a1d9e..6f0ed20 100644 --- a/patches/minecraft/net/minecraft/command/server/CommandTestFor.edit.java +++ b/patches/minecraft/net/minecraft/command/server/CommandTestFor.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/command/server/CommandTestForBlock.edit.java b/patches/minecraft/net/minecraft/command/server/CommandTestForBlock.edit.java index bc82325..cc2856b 100644 --- a/patches/minecraft/net/minecraft/command/server/CommandTestForBlock.edit.java +++ b/patches/minecraft/net/minecraft/command/server/CommandTestForBlock.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/crash/CrashReport.edit.java b/patches/minecraft/net/minecraft/crash/CrashReport.edit.java index 5f83518..5631932 100644 --- a/patches/minecraft/net/minecraft/crash/CrashReport.edit.java +++ b/patches/minecraft/net/minecraft/crash/CrashReport.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/crash/CrashReportCategory.edit.java b/patches/minecraft/net/minecraft/crash/CrashReportCategory.edit.java index eab7d6f..c34de7e 100644 --- a/patches/minecraft/net/minecraft/crash/CrashReportCategory.edit.java +++ b/patches/minecraft/net/minecraft/crash/CrashReportCategory.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/creativetab/CreativeTabs.edit.java b/patches/minecraft/net/minecraft/creativetab/CreativeTabs.edit.java index a31eab7..47ad2fd 100644 --- a/patches/minecraft/net/minecraft/creativetab/CreativeTabs.edit.java +++ b/patches/minecraft/net/minecraft/creativetab/CreativeTabs.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/dispenser/BehaviorDefaultDispenseItem.edit.java b/patches/minecraft/net/minecraft/dispenser/BehaviorDefaultDispenseItem.edit.java index 9c2c149..a9d03a6 100644 --- a/patches/minecraft/net/minecraft/dispenser/BehaviorDefaultDispenseItem.edit.java +++ b/patches/minecraft/net/minecraft/dispenser/BehaviorDefaultDispenseItem.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/dispenser/BehaviorProjectileDispense.edit.java b/patches/minecraft/net/minecraft/dispenser/BehaviorProjectileDispense.edit.java index 9c2c149..a9d03a6 100644 --- a/patches/minecraft/net/minecraft/dispenser/BehaviorProjectileDispense.edit.java +++ b/patches/minecraft/net/minecraft/dispenser/BehaviorProjectileDispense.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/dispenser/IBehaviorDispenseItem.edit.java b/patches/minecraft/net/minecraft/dispenser/IBehaviorDispenseItem.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/dispenser/IBehaviorDispenseItem.edit.java +++ b/patches/minecraft/net/minecraft/dispenser/IBehaviorDispenseItem.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/dispenser/IBlockSource.edit.java b/patches/minecraft/net/minecraft/dispenser/IBlockSource.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/dispenser/IBlockSource.edit.java +++ b/patches/minecraft/net/minecraft/dispenser/IBlockSource.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/dispenser/ILocatableSource.edit.java b/patches/minecraft/net/minecraft/dispenser/ILocatableSource.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/dispenser/ILocatableSource.edit.java +++ b/patches/minecraft/net/minecraft/dispenser/ILocatableSource.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/dispenser/ILocation.edit.java b/patches/minecraft/net/minecraft/dispenser/ILocation.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/dispenser/ILocation.edit.java +++ b/patches/minecraft/net/minecraft/dispenser/ILocation.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/dispenser/PositionImpl.edit.java b/patches/minecraft/net/minecraft/dispenser/PositionImpl.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/dispenser/PositionImpl.edit.java +++ b/patches/minecraft/net/minecraft/dispenser/PositionImpl.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/Enchantment.edit.java b/patches/minecraft/net/minecraft/enchantment/Enchantment.edit.java index fb509b5..c5ba44b 100644 --- a/patches/minecraft/net/minecraft/enchantment/Enchantment.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/Enchantment.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowDamage.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowDamage.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowDamage.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowDamage.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowFire.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowFire.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowFire.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowFire.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowInfinite.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowInfinite.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowInfinite.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowInfinite.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowKnockback.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowKnockback.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowKnockback.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowKnockback.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentDamage.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentDamage.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentDamage.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentDamage.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentData.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentData.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentData.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentData.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentDigging.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentDigging.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentDigging.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentDigging.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentDurability.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentDurability.edit.java index aa67f3b..d837733 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentDurability.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentDurability.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentFireAspect.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentFireAspect.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentFireAspect.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentFireAspect.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentFishingSpeed.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentFishingSpeed.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentFishingSpeed.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentFishingSpeed.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentHelper.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentHelper.edit.java index 0fa0250..94351ef 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentHelper.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentHelper.edit.java @@ -1,16 +1,21 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 4 -> CHANGE 6 : 11 @ 6 : 9 +> DELETE 3 @ 3 : 4 + +> CHANGE 2 : 10 @ 2 : 5 ~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; ~ +~ import com.carrotsearch.hppc.IntIntHashMap; +~ import com.carrotsearch.hppc.IntIntMap; +~ import com.carrotsearch.hppc.cursors.IntCursor; ~ import com.google.common.collect.Lists; ~ import com.google.common.collect.Maps; ~ @@ -19,7 +24,27 @@ ~ private static final EaglercraftRandom enchantmentRand = new EaglercraftRandom(); -> CHANGE 76 : 78 @ 76 : 78 +> CHANGE 26 : 28 @ 26 : 28 + +~ public static IntIntMap getEnchantments(ItemStack stack) { +~ IntIntHashMap linkedhashmap = new IntIntHashMap(); + +> CHANGE 6 : 7 @ 6 : 7 + +~ linkedhashmap.put(short1, short2); + +> CHANGE 6 : 7 @ 6 : 7 + +~ public static void setEnchantments(IntIntMap enchMap, ItemStack stack) { + +> DELETE 1 @ 1 : 2 + +> CHANGE 1 : 3 @ 1 : 3 + +~ for (IntCursor cur : enchMap.keys()) { +~ int i = cur.value; + +> CHANGE 29 : 31 @ 29 : 31 ~ for (int k = 0; k < stacks.length; ++k) { ~ int j = getEnchantmentLevel(enchID, stacks[k]); diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentKnockback.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentKnockback.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentKnockback.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentKnockback.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentLootBonus.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentLootBonus.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentLootBonus.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentLootBonus.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentOxygen.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentOxygen.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentOxygen.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentOxygen.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentProtection.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentProtection.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentProtection.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentProtection.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentThorns.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentThorns.edit.java index ea8b5cc..e51b061 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentThorns.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentThorns.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentUntouching.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentUntouching.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentUntouching.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentUntouching.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentWaterWalker.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentWaterWalker.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentWaterWalker.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentWaterWalker.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentWaterWorker.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentWaterWorker.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/enchantment/EnchantmentWaterWorker.edit.java +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentWaterWorker.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/DataWatcher.edit.java b/patches/minecraft/net/minecraft/entity/DataWatcher.edit.java index b8048a0..92d941e 100644 --- a/patches/minecraft/net/minecraft/entity/DataWatcher.edit.java +++ b/patches/minecraft/net/minecraft/entity/DataWatcher.edit.java @@ -1,55 +1,94 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 4 -> CHANGE 4 : 10 @ 4 : 6 +> CHANGE 3 : 13 @ 3 : 6 ~ ~ import org.apache.commons.lang3.ObjectUtils; ~ +~ import com.carrotsearch.hppc.IntObjectHashMap; +~ import com.carrotsearch.hppc.IntObjectMap; +~ import com.carrotsearch.hppc.ObjectIntHashMap; +~ import com.carrotsearch.hppc.ObjectIntMap; +~ import com.carrotsearch.hppc.cursors.ObjectCursor; ~ import com.google.common.collect.Lists; -~ import com.google.common.collect.Maps; ~ > DELETE 2 @ 2 : 3 > DELETE 5 @ 5 : 6 -> DELETE 7 @ 7 : 8 +> CHANGE 4 : 6 @ 4 : 6 -> DELETE 16 @ 16 : 17 +~ private static final ObjectIntMap> dataTypes = new ObjectIntHashMap<>(); +~ private final IntObjectMap watchedObjects = new IntObjectHashMap<>(); > DELETE 1 @ 1 : 2 -> DELETE 7 @ 7 : 8 +> CHANGE 6 : 8 @ 6 : 8 -> DELETE 1 @ 1 : 2 +~ int integer = dataTypes.getOrDefault(object.getClass(), -1); +~ if (integer == -1) { + +> CHANGE 3 : 4 @ 3 : 4 + +~ } else if (this.watchedObjects.containsKey(id)) { + +> CHANGE 2 : 5 @ 2 : 7 + +~ DataWatcher.WatchableObject datawatcher$watchableobject = new DataWatcher.WatchableObject(integer, id, +~ object); +~ this.watchedObjects.put(id, datawatcher$watchableobject); + +> CHANGE 7 : 8 @ 7 : 10 + +~ this.watchedObjects.put(id, datawatcher$watchableobject); > DELETE 28 @ 28 : 30 -> DELETE 9 @ 9 : 11 +> CHANGE 2 : 3 @ 2 : 3 + +~ datawatcher$watchableobject = (DataWatcher.WatchableObject) this.watchedObjects.get(id); + +> DELETE 6 @ 6 : 8 > CHANGE 30 : 32 @ 30 : 32 ~ for (int i = 0, l = objectsList.size(); i < l; ++i) { ~ writeWatchableObjectToPacketBuffer(buffer, objectsList.get(i)); +> CHANGE 9 : 12 @ 9 : 12 + +~ for (ObjectCursor datawatcher$watchableobject_ : this.watchedObjects +~ .values()) { +~ DataWatcher.WatchableObject datawatcher$watchableobject = datawatcher$watchableobject_.value; + > DELETE 9 @ 9 : 11 -> DELETE 10 @ 10 : 12 +> CHANGE 7 : 9 @ 7 : 11 -> DELETE 7 @ 7 : 9 +~ for (ObjectCursor datawatcher$watchableobject : this.watchedObjects.values()) { +~ writeWatchableObjectToPacketBuffer(buffer, datawatcher$watchableobject.value); -> DELETE 3 @ 3 : 5 +> DELETE 1 @ 1 : 3 > DELETE 5 @ 5 : 6 -> DELETE 9 @ 9 : 10 +> CHANGE 1 : 2 @ 1 : 2 + +~ for (ObjectCursor datawatcher$watchableobject : this.watchedObjects.values()) { + +> CHANGE 4 : 5 @ 4 : 5 + +~ arraylist.add(datawatcher$watchableobject.value); + +> DELETE 2 @ 2 : 3 > DELETE 93 @ 93 : 94 @@ -58,6 +97,21 @@ ~ for (int i = 0, l = parList.size(); i < l; ++i) { ~ DataWatcher.WatchableObject datawatcher$watchableobject = parList.get(i); -> DELETE 8 @ 8 : 9 +> CHANGE 1 : 2 @ 1 : 2 + +~ .get(datawatcher$watchableobject.getDataValueId()); + +> DELETE 6 @ 6 : 7 + +> CHANGE 12 : 20 @ 12 : 20 + +~ dataTypes.put(Byte.class, 0); +~ dataTypes.put(Short.class, 1); +~ dataTypes.put(Integer.class, 2); +~ dataTypes.put(Float.class, 3); +~ dataTypes.put(String.class, 4); +~ dataTypes.put(ItemStack.class, 5); +~ dataTypes.put(BlockPos.class, 6); +~ dataTypes.put(Rotations.class, 7); > EOF diff --git a/patches/minecraft/net/minecraft/entity/Entity.edit.java b/patches/minecraft/net/minecraft/entity/Entity.edit.java index a4e6912..c26ad49 100644 --- a/patches/minecraft/net/minecraft/entity/Entity.edit.java +++ b/patches/minecraft/net/minecraft/entity/Entity.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/EntityAgeable.edit.java b/patches/minecraft/net/minecraft/entity/EntityAgeable.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/entity/EntityAgeable.edit.java +++ b/patches/minecraft/net/minecraft/entity/EntityAgeable.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/EntityBodyHelper.edit.java b/patches/minecraft/net/minecraft/entity/EntityBodyHelper.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/entity/EntityBodyHelper.edit.java +++ b/patches/minecraft/net/minecraft/entity/EntityBodyHelper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/EntityCreature.edit.java b/patches/minecraft/net/minecraft/entity/EntityCreature.edit.java index 9e1b32b..a96bb6a 100644 --- a/patches/minecraft/net/minecraft/entity/EntityCreature.edit.java +++ b/patches/minecraft/net/minecraft/entity/EntityCreature.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/EntityFlying.edit.java b/patches/minecraft/net/minecraft/entity/EntityFlying.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/EntityFlying.edit.java +++ b/patches/minecraft/net/minecraft/entity/EntityFlying.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/EntityHanging.edit.java b/patches/minecraft/net/minecraft/entity/EntityHanging.edit.java index 69e5ee3..abea59f 100644 --- a/patches/minecraft/net/minecraft/entity/EntityHanging.edit.java +++ b/patches/minecraft/net/minecraft/entity/EntityHanging.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/EntityLeashKnot.edit.java b/patches/minecraft/net/minecraft/entity/EntityLeashKnot.edit.java index 02dd920..7083051 100644 --- a/patches/minecraft/net/minecraft/entity/EntityLeashKnot.edit.java +++ b/patches/minecraft/net/minecraft/entity/EntityLeashKnot.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/EntityList.edit.java b/patches/minecraft/net/minecraft/entity/EntityList.edit.java index 7eddaaf..4fc3a9e 100644 --- a/patches/minecraft/net/minecraft/entity/EntityList.edit.java +++ b/patches/minecraft/net/minecraft/entity/EntityList.edit.java @@ -1,15 +1,19 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 4 -> CHANGE 4 : 13 @ 4 : 8 +> CHANGE 4 : 17 @ 4 : 8 ~ +~ import com.carrotsearch.hppc.IntObjectHashMap; +~ import com.carrotsearch.hppc.IntObjectMap; +~ import com.carrotsearch.hppc.ObjectIntHashMap; +~ import com.carrotsearch.hppc.ObjectIntMap; ~ import com.google.common.collect.Lists; ~ import com.google.common.collect.Maps; ~ @@ -26,42 +30,48 @@ + private static final Map> stringToConstructorMapping = Maps + .newHashMap(); -> INSERT 2 : 3 @ 2 +> CHANGE 1 : 8 @ 1 : 5 -+ private static final Map> idToConstructorMapping = Maps.newHashMap(); +~ private static final IntObjectMap> idToClassMapping = new IntObjectHashMap<>(); +~ private static final IntObjectMap> idToConstructorMapping = new IntObjectHashMap<>(); +~ private static final ObjectIntMap> classToIDMapping = new ObjectIntHashMap<>(); +~ private static final Map, EntityConstructor> classToConstructorMapping = Maps +~ .newHashMap(); +~ private static final ObjectIntMap stringToIDMapping = new ObjectIntHashMap<>(); +~ public static final IntObjectMap entityEggs = new IntObjectHashMap<>(); -> INSERT 1 : 3 @ 1 - -+ private static final Map, EntityConstructor> classToConstructorMapping = Maps -+ .newHashMap(); - -> CHANGE 3 : 5 @ 3 : 4 +> CHANGE 1 : 3 @ 1 : 2 ~ private static void addMapping(Class entityClass, ~ EntityConstructor entityConstructor, String entityName, int id) { -> INSERT 10 : 11 @ 10 +> CHANGE 2 : 3 @ 2 : 3 + +~ } else if (idToClassMapping.containsKey(id)) { + +> INSERT 7 : 8 @ 7 + stringToConstructorMapping.put(entityName, entityConstructor); -> INSERT 2 : 3 @ 2 +> CHANGE 1 : 6 @ 1 : 4 -+ idToConstructorMapping.put(Integer.valueOf(id), entityConstructor); +~ idToClassMapping.put(id, entityClass); +~ idToConstructorMapping.put(id, entityConstructor); +~ classToIDMapping.put(entityClass, id); +~ classToConstructorMapping.put(entityClass, entityConstructor); +~ stringToIDMapping.put(entityName, id); -> INSERT 1 : 2 @ 1 - -+ classToConstructorMapping.put(entityClass, entityConstructor); - -> CHANGE 4 : 6 @ 4 : 5 +> CHANGE 3 : 5 @ 3 : 4 ~ private static void addMapping(Class entityClass, ~ EntityConstructor entityConstructor, String entityName, int entityID, int baseColor, -> CHANGE 1 : 2 @ 1 : 2 +> CHANGE 1 : 3 @ 1 : 3 ~ addMapping(entityClass, entityConstructor, entityName, entityID); +~ entityEggs.put(entityID, new EntityList.EntityEggInfo(entityID, baseColor, spotColor)); -> CHANGE 7 : 10 @ 7 : 11 +> CHANGE 6 : 9 @ 6 : 10 ~ EntityConstructor constructor = stringToConstructorMapping.get(entityName); ~ if (constructor != null) { @@ -117,14 +127,36 @@ ~ logger.error("Could not create entity", exception); -> INSERT 18 : 22 @ 18 +> CHANGE 10 : 12 @ 10 : 12 + +~ int integer = classToIDMapping.getOrDefault(entityIn.getClass(), -1); +~ return integer == -1 ? 0 : integer; + +> CHANGE 3 : 4 @ 3 : 4 + +~ return idToClassMapping.get(entityID); + +> INSERT 2 : 6 @ 2 + public static EntityConstructor getConstructorFromID(int entityID) { -+ return idToConstructorMapping.get(Integer.valueOf(entityID)); ++ return idToConstructorMapping.get(entityID); + } + -> CHANGE 17 : 18 @ 17 : 18 +> CHANGE 1 : 2 @ 1 : 2 + +~ return classToStringMapping.get(entityIn.getClass()); + +> CHANGE 3 : 5 @ 3 : 5 + +~ int integer = stringToIDMapping.getOrDefault(entityName, -1); +~ return integer == -1 ? 90 : integer; + +> CHANGE 3 : 4 @ 3 : 4 + +~ return classToStringMapping.get(getClassFromID(entityID)); + +> CHANGE 6 : 7 @ 6 : 7 ~ Set set = stringToClassMapping.keySet(); diff --git a/patches/minecraft/net/minecraft/entity/EntityLiving.edit.java b/patches/minecraft/net/minecraft/entity/EntityLiving.edit.java index 5860c56..fb1b002 100644 --- a/patches/minecraft/net/minecraft/entity/EntityLiving.edit.java +++ b/patches/minecraft/net/minecraft/entity/EntityLiving.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/EntityLivingBase.edit.java b/patches/minecraft/net/minecraft/entity/EntityLivingBase.edit.java index 87a1115..6f8ec08 100644 --- a/patches/minecraft/net/minecraft/entity/EntityLivingBase.edit.java +++ b/patches/minecraft/net/minecraft/entity/EntityLivingBase.edit.java @@ -1,15 +1,22 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> INSERT 5 : 6 @ 5 +> INSERT 2 : 8 @ 2 -+ ++ import com.carrotsearch.hppc.IntArrayList; ++ import com.carrotsearch.hppc.IntObjectHashMap; ++ import com.carrotsearch.hppc.IntObjectMap; ++ import com.carrotsearch.hppc.ObjectContainer; ++ import com.carrotsearch.hppc.cursors.IntObjectCursor; ++ import com.carrotsearch.hppc.cursors.ObjectCursor; -> CHANGE 4 : 6 @ 4 : 6 +> DELETE 2 @ 2 : 5 + +> CHANGE 1 : 3 @ 1 : 4 ~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; ~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; @@ -25,7 +32,11 @@ ~ private static final EaglercraftUUID sprintingSpeedBoostModifierUUID = EaglercraftUUID ~ .fromString("662A6B8D-DA3E-4C1C-8813-96EA6097278D"); -> DELETE 120 @ 120 : 121 +> CHANGE 4 : 5 @ 4 : 5 + +~ private final IntObjectMap activePotionsMap = new IntObjectHashMap<>(); + +> DELETE 115 @ 115 : 116 > DELETE 88 @ 88 : 89 @@ -44,7 +55,92 @@ ~ for (int i = 0; i < inv.length; ++i) { ~ ItemStack itemstack1 = inv[i]; -> DELETE 941 @ 941 : 942 +> CHANGE 8 : 10 @ 8 : 10 + +~ for (ObjectCursor potioneffect : this.activePotionsMap.values()) { +~ nbttaglist.appendTag(potioneffect.value.writeCustomPotionEffectToNBT(new NBTTagCompound())); + +> CHANGE 20 : 21 @ 20 : 21 + +~ this.activePotionsMap.put(potioneffect.getPotionID(), potioneffect); + +> CHANGE 23 : 24 @ 23 : 24 + +~ IntArrayList deadPotionEffects = null; + +> CHANGE 1 : 4 @ 1 : 4 + +~ for (IntObjectCursor cur : this.activePotionsMap) { +~ int integer = cur.key; +~ PotionEffect potioneffect = cur.value; + +> CHANGE 2 : 5 @ 2 : 3 + +~ if (deadPotionEffects == null) +~ deadPotionEffects = new IntArrayList(4); +~ deadPotionEffects.add(integer); + +> INSERT 7 : 11 @ 7 + ++ if (deadPotionEffects != null) { ++ this.activePotionsMap.removeAll(deadPotionEffects); ++ } ++ + +> CHANGE 40 : 43 @ 40 : 43 + +~ ObjectContainer cc = this.activePotionsMap.values(); +~ int i = PotionHelper.calcPotionLiquidColor(cc); +~ this.dataWatcher.updateObject(8, Byte.valueOf((byte) (PotionHelper.getAreAmbient(cc) ? 1 : 0))); + +> CHANGE 12 : 14 @ 12 : 17 + +~ for (ObjectCursor cur : activePotionsMap.values()) { +~ PotionEffect potioneffect = cur.value; + +> DELETE 1 @ 1 : 2 + +> INSERT 4 : 7 @ 4 + ++ if (!this.worldObj.isRemote) { ++ activePotionsMap.clear(); ++ } + +> CHANGE 2 : 3 @ 2 : 3 + +~ public ObjectContainer getActivePotionEffects() { + +> CHANGE 4 : 5 @ 4 : 5 + +~ return this.activePotionsMap.containsKey(potionId); + +> CHANGE 3 : 4 @ 3 : 4 + +~ return this.activePotionsMap.containsKey(potionIn.id); + +> CHANGE 3 : 4 @ 3 : 4 + +~ return this.activePotionsMap.get(potionIn.id); + +> CHANGE 4 : 7 @ 4 : 9 + +~ if (this.activePotionsMap.containsKey(potioneffectIn.getPotionID())) { +~ this.activePotionsMap.get(potioneffectIn.getPotionID()).combine(potioneffectIn); +~ this.onChangedPotionEffect(this.activePotionsMap.get(potioneffectIn.getPotionID()), true); + +> CHANGE 1 : 2 @ 1 : 2 + +~ this.activePotionsMap.put(potioneffectIn.getPotionID(), potioneffectIn); + +> CHANGE 22 : 23 @ 22 : 23 + +~ this.activePotionsMap.remove(potionId); + +> CHANGE 3 : 4 @ 3 : 4 + +~ PotionEffect potioneffect = this.activePotionsMap.remove(potionId); + +> DELETE 752 @ 752 : 753 > DELETE 1 @ 1 : 3 diff --git a/patches/minecraft/net/minecraft/entity/EntityMinecartCommandBlock.edit.java b/patches/minecraft/net/minecraft/entity/EntityMinecartCommandBlock.edit.java index 68b45e4..5859be8 100644 --- a/patches/minecraft/net/minecraft/entity/EntityMinecartCommandBlock.edit.java +++ b/patches/minecraft/net/minecraft/entity/EntityMinecartCommandBlock.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/EntitySpawnPlacementRegistry.edit.java b/patches/minecraft/net/minecraft/entity/EntitySpawnPlacementRegistry.edit.java index edaa3fa..a5b8f74 100644 --- a/patches/minecraft/net/minecraft/entity/EntitySpawnPlacementRegistry.edit.java +++ b/patches/minecraft/net/minecraft/entity/EntitySpawnPlacementRegistry.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/EntityTracker.edit.java b/patches/minecraft/net/minecraft/entity/EntityTracker.edit.java index 3269066..80d6a0a 100644 --- a/patches/minecraft/net/minecraft/entity/EntityTracker.edit.java +++ b/patches/minecraft/net/minecraft/entity/EntityTracker.edit.java @@ -1,15 +1,84 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> DELETE 9 @ 9 : 12 +> INSERT 2 : 4 @ 2 -> CHANGE 30 : 32 @ 30 : 32 ++ import com.carrotsearch.hppc.IntObjectHashMap; ++ import com.carrotsearch.hppc.IntObjectMap; + +> DELETE 7 @ 7 : 10 + +> DELETE 26 @ 26 : 27 + +> CHANGE 3 : 5 @ 3 : 5 ~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +> CHANGE 5 : 6 @ 5 : 6 + +~ private IntObjectMap trackedEntityHashTable = new IntObjectHashMap<>(); + +> INSERT 8 : 12 @ 8 + ++ public void updateMaxTrackingThreshold(int dist) { ++ maxTrackingDistanceThreshold = dist; ++ } ++ + +> CHANGE 7 : 8 @ 7 : 8 + +~ entitytrackerentry.updatePlayerEntity(entityplayermp, maxTrackingDistanceThreshold); + +> DELETE 62 @ 62 : 66 + +> CHANGE 1 : 2 @ 1 : 2 + +~ if (this.trackedEntityHashTable.containsKey(entityIn.getEntityId())) { + +> CHANGE 6 : 8 @ 6 : 8 + +~ this.trackedEntityHashTable.put(entityIn.getEntityId(), entitytrackerentry); +~ entitytrackerentry.updatePlayerEntities(this.theWorld.playerEntities, maxTrackingDistanceThreshold); + +> CHANGE 16 : 17 @ 16 : 17 + +~ this.trackedEntityHashTable.get(entityIn.getEntityId()).trackedEntity + +> CHANGE 20 : 21 @ 20 : 22 + +~ EntityTrackerEntry entitytrackerentry1 = this.trackedEntityHashTable.remove(entityIn.getEntityId()); + +> CHANGE 11 : 12 @ 11 : 12 + +~ entitytrackerentry.updatePlayerList(this.theWorld.playerEntities, maxTrackingDistanceThreshold); + +> CHANGE 11 : 12 @ 11 : 12 + +~ entitytrackerentry1.updatePlayerEntity(entityplayermp, maxTrackingDistanceThreshold); + +> CHANGE 9 : 10 @ 9 : 10 + +~ entitytrackerentry.updatePlayerEntities(this.theWorld.playerEntities, maxTrackingDistanceThreshold); + +> CHANGE 1 : 2 @ 1 : 2 + +~ entitytrackerentry.updatePlayerEntity(parEntityPlayerMP, maxTrackingDistanceThreshold); + +> CHANGE 6 : 7 @ 6 : 8 + +~ EntityTrackerEntry entitytrackerentry = this.trackedEntityHashTable.get(entityIn.getEntityId()); + +> CHANGE 7 : 8 @ 7 : 9 + +~ EntityTrackerEntry entitytrackerentry = this.trackedEntityHashTable.get(entityIn.getEntityId()); + +> CHANGE 18 : 19 @ 18 : 19 + +~ entitytrackerentry.updatePlayerEntity(parEntityPlayerMP, maxTrackingDistanceThreshold); + > EOF diff --git a/patches/minecraft/net/minecraft/entity/EntityTrackerEntry.edit.java b/patches/minecraft/net/minecraft/entity/EntityTrackerEntry.edit.java index 8c1cdad..afa67e7 100644 --- a/patches/minecraft/net/minecraft/entity/EntityTrackerEntry.edit.java +++ b/patches/minecraft/net/minecraft/entity/EntityTrackerEntry.edit.java @@ -1,23 +1,76 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> DELETE 7 @ 7 : 12 +> INSERT 2 : 3 @ 2 + ++ import com.carrotsearch.hppc.cursors.ObjectCursor; + +> DELETE 5 @ 5 : 10 > CHANGE 51 : 53 @ 51 : 53 ~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -> CHANGE 78 : 80 @ 78 : 80 +> CHANGE 53 : 54 @ 53 : 54 + +~ public void updatePlayerList(List parList, int trackingDistanceMax) { + +> CHANGE 8 : 9 @ 8 : 9 + +~ this.updatePlayerEntities(parList, trackingDistanceMax); + +> CHANGE 15 : 17 @ 15 : 17 ~ for (int i = 0, l = parList.size(); i < l; ++i) { ~ EntityPlayerMP entityplayermp = (EntityPlayerMP) parList.get(i); -> CHANGE 327 : 329 @ 327 : 328 +> CHANGE 171 : 172 @ 171 : 172 + +~ public void updatePlayerEntity(EntityPlayerMP playerMP, int trackingDistanceMax) { + +> CHANGE 1 : 2 @ 1 : 2 + +~ if (this.func_180233_c(playerMP, trackingDistanceMax)) { + +> INSERT 64 : 69 @ 64 + ++ this.lastHeadMotion = MathHelper ++ .floor_float(this.trackedEntity.getRotationYawHead() * 256.0F / 360.0F); ++ playerMP.playerNetServerHandler ++ .sendPacket(new S19PacketEntityHeadLook(this.trackedEntity, (byte) lastHeadMotion)); ++ + +> CHANGE 3 : 4 @ 3 : 4 + +~ for (ObjectCursor potioneffect : entitylivingbase.getActivePotionEffects()) { + +> CHANGE 1 : 2 @ 1 : 2 + +~ new S1DPacketEntityEffect(this.trackedEntity.getEntityId(), potioneffect.value)); + +> CHANGE 11 : 13 @ 11 : 12 + +~ public boolean func_180233_c(EntityPlayerMP playerMP, int trackingDistanceMax) { +~ int i = trackingDistanceThreshold > trackingDistanceMax ? trackingDistanceMax : trackingDistanceThreshold; + +> CHANGE 2 : 3 @ 2 : 4 + +~ return d0 >= (double) (-i) && d0 <= (double) i && d1 >= (double) (-i) && d1 <= (double) i + +> CHANGE 8 : 9 @ 8 : 9 + +~ public void updatePlayerEntities(List parList, int trackingDistanceMax) { + +> CHANGE 1 : 2 @ 1 : 2 + +~ this.updatePlayerEntity((EntityPlayerMP) parList.get(i), trackingDistanceMax); + +> CHANGE 56 : 58 @ 56 : 57 ~ s0epacketspawnobject2 = new S0EPacketSpawnObject(this.trackedEntity, b0, ~ this.trackedEntity.getEntityId()); diff --git a/patches/minecraft/net/minecraft/entity/EnumCreatureType.edit.java b/patches/minecraft/net/minecraft/entity/EnumCreatureType.edit.java index 3e70454..2d9072c 100644 --- a/patches/minecraft/net/minecraft/entity/EnumCreatureType.edit.java +++ b/patches/minecraft/net/minecraft/entity/EnumCreatureType.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/IEntityOwnable.edit.java b/patches/minecraft/net/minecraft/entity/IEntityOwnable.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/entity/IEntityOwnable.edit.java +++ b/patches/minecraft/net/minecraft/entity/IEntityOwnable.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/IRangedAttackMob.edit.java b/patches/minecraft/net/minecraft/entity/IRangedAttackMob.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/entity/IRangedAttackMob.edit.java +++ b/patches/minecraft/net/minecraft/entity/IRangedAttackMob.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/NpcMerchant.edit.java b/patches/minecraft/net/minecraft/entity/NpcMerchant.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/entity/NpcMerchant.edit.java +++ b/patches/minecraft/net/minecraft/entity/NpcMerchant.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/SharedMonsterAttributes.edit.java b/patches/minecraft/net/minecraft/entity/SharedMonsterAttributes.edit.java index bd29e39..0796d61 100644 --- a/patches/minecraft/net/minecraft/entity/SharedMonsterAttributes.edit.java +++ b/patches/minecraft/net/minecraft/entity/SharedMonsterAttributes.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIArrowAttack.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIArrowAttack.edit.java index be6673d..bcd4bdc 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIArrowAttack.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIArrowAttack.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIAttackOnCollide.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIAttackOnCollide.edit.java index be6673d..bcd4bdc 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIAttackOnCollide.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIAttackOnCollide.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIAvoidEntity.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIAvoidEntity.edit.java index 40f3ee0..133f94d 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIAvoidEntity.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIAvoidEntity.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIBeg.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIBeg.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIBeg.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIBeg.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIBreakDoor.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIBreakDoor.edit.java index be6673d..bcd4bdc 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIBreakDoor.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIBreakDoor.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIControlledByPlayer.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIControlledByPlayer.edit.java index d9711e3..944d4b4 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIControlledByPlayer.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIControlledByPlayer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAICreeperSwell.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAICreeperSwell.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAICreeperSwell.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAICreeperSwell.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIDefendVillage.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIDefendVillage.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIDefendVillage.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIDefendVillage.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIDoorInteract.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIDoorInteract.edit.java index 97688dc..8ad33b5 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIDoorInteract.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIDoorInteract.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIEatGrass.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIEatGrass.edit.java index a2cda1e..d33891a 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIEatGrass.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIEatGrass.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIFindEntityNearest.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIFindEntityNearest.edit.java index 817fbbb..31520db 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIFindEntityNearest.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIFindEntityNearest.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIFindEntityNearestPlayer.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIFindEntityNearestPlayer.edit.java index aad8294..b0341ee 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIFindEntityNearestPlayer.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIFindEntityNearestPlayer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIFleeSun.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIFleeSun.edit.java index cb64cdb..4b917a3 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIFleeSun.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIFleeSun.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIFollowGolem.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIFollowGolem.edit.java index b13fc62..6a44f19 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIFollowGolem.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIFollowGolem.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIFollowOwner.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIFollowOwner.edit.java index be6673d..bcd4bdc 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIFollowOwner.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIFollowOwner.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIFollowParent.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIFollowParent.edit.java index e561514..ff964f6 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIFollowParent.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIFollowParent.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIHarvestFarmland.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIHarvestFarmland.edit.java index be6673d..bcd4bdc 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIHarvestFarmland.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIHarvestFarmland.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIHurtByTarget.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIHurtByTarget.edit.java index efe9567..1a69878 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIHurtByTarget.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIHurtByTarget.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAILeapAtTarget.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAILeapAtTarget.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAILeapAtTarget.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAILeapAtTarget.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAILookAtTradePlayer.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAILookAtTradePlayer.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAILookAtTradePlayer.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAILookAtTradePlayer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAILookAtVillager.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAILookAtVillager.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAILookAtVillager.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAILookAtVillager.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAILookIdle.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAILookIdle.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAILookIdle.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAILookIdle.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIMate.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIMate.edit.java index 9abd8a1..3e1faf5 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIMate.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIMate.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveIndoors.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveIndoors.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveIndoors.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveIndoors.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveThroughVillage.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveThroughVillage.edit.java index 069abd3..9157c9b 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveThroughVillage.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveThroughVillage.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveToBlock.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveToBlock.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveToBlock.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveToBlock.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveTowardsRestriction.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveTowardsRestriction.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveTowardsRestriction.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveTowardsRestriction.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveTowardsTarget.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveTowardsTarget.edit.java index a7f89c1..300ea78 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveTowardsTarget.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIMoveTowardsTarget.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAINearestAttackableTarget.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAINearestAttackableTarget.edit.java index 166779e..0921084 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAINearestAttackableTarget.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAINearestAttackableTarget.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIOcelotAttack.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIOcelotAttack.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIOcelotAttack.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIOcelotAttack.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIOcelotSit.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIOcelotSit.edit.java index be6673d..bcd4bdc 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIOcelotSit.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIOcelotSit.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIOpenDoor.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIOpenDoor.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIOpenDoor.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIOpenDoor.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIOwnerHurtByTarget.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIOwnerHurtByTarget.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIOwnerHurtByTarget.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIOwnerHurtByTarget.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIOwnerHurtTarget.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIOwnerHurtTarget.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIOwnerHurtTarget.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIOwnerHurtTarget.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIPanic.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIPanic.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIPanic.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIPanic.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIPlay.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIPlay.edit.java index dda0fb0..703be27 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIPlay.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIPlay.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIRestrictOpenDoor.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIRestrictOpenDoor.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIRestrictOpenDoor.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIRestrictOpenDoor.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIRestrictSun.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIRestrictSun.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIRestrictSun.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIRestrictSun.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIRunAroundLikeCrazy.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIRunAroundLikeCrazy.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIRunAroundLikeCrazy.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIRunAroundLikeCrazy.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAISit.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAISit.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAISit.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAISit.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAISwimming.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAISwimming.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAISwimming.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAISwimming.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAITarget.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAITarget.edit.java index 6e876c6..d3779c7 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAITarget.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAITarget.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAITargetNonTamed.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAITargetNonTamed.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAITargetNonTamed.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAITargetNonTamed.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAITasks.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAITasks.edit.java index ff0dbb7..40a712e 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAITasks.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAITasks.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAITempt.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAITempt.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAITempt.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAITempt.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAITradePlayer.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAITradePlayer.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAITradePlayer.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAITradePlayer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIVillagerInteract.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIVillagerInteract.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIVillagerInteract.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIVillagerInteract.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIVillagerMate.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIVillagerMate.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIVillagerMate.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIVillagerMate.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIWander.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIWander.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIWander.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIWander.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIWatchClosest.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIWatchClosest.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIWatchClosest.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIWatchClosest.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntityAIWatchClosest2.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntityAIWatchClosest2.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntityAIWatchClosest2.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntityAIWatchClosest2.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/EntitySenses.edit.java b/patches/minecraft/net/minecraft/entity/ai/EntitySenses.edit.java index 4c2454b..1fd26d6 100644 --- a/patches/minecraft/net/minecraft/entity/ai/EntitySenses.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/EntitySenses.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/RandomPositionGenerator.edit.java b/patches/minecraft/net/minecraft/entity/ai/RandomPositionGenerator.edit.java index 34c253e..07d8161 100644 --- a/patches/minecraft/net/minecraft/entity/ai/RandomPositionGenerator.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/RandomPositionGenerator.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/attributes/AttributeModifier.edit.java b/patches/minecraft/net/minecraft/entity/ai/attributes/AttributeModifier.edit.java index 77abcc1..f8952f4 100644 --- a/patches/minecraft/net/minecraft/entity/ai/attributes/AttributeModifier.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/attributes/AttributeModifier.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/attributes/BaseAttribute.edit.java b/patches/minecraft/net/minecraft/entity/ai/attributes/BaseAttribute.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/entity/ai/attributes/BaseAttribute.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/attributes/BaseAttribute.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/attributes/BaseAttributeMap.edit.java b/patches/minecraft/net/minecraft/entity/ai/attributes/BaseAttributeMap.edit.java index 78c6c8e..f00310d 100644 --- a/patches/minecraft/net/minecraft/entity/ai/attributes/BaseAttributeMap.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/attributes/BaseAttributeMap.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/attributes/IAttributeInstance.edit.java b/patches/minecraft/net/minecraft/entity/ai/attributes/IAttributeInstance.edit.java index c005fd0..e7f8564 100644 --- a/patches/minecraft/net/minecraft/entity/ai/attributes/IAttributeInstance.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/attributes/IAttributeInstance.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/attributes/ModifiableAttributeInstance.edit.java b/patches/minecraft/net/minecraft/entity/ai/attributes/ModifiableAttributeInstance.edit.java index 9932ccb..ee27f4f 100644 --- a/patches/minecraft/net/minecraft/entity/ai/attributes/ModifiableAttributeInstance.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/attributes/ModifiableAttributeInstance.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude @@ -15,18 +15,32 @@ ~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; -> INSERT 1 : 5 @ 1 +> INSERT 1 : 7 @ 1 ++ import com.carrotsearch.hppc.IntObjectHashMap; ++ import com.carrotsearch.hppc.IntObjectMap; + import com.google.common.collect.Lists; + import com.google.common.collect.Maps; + import com.google.common.collect.Sets; + -> CHANGE 5 : 6 @ 5 : 6 +> CHANGE 3 : 4 @ 3 : 4 + +~ private final IntObjectMap> mapByOperation = new IntObjectHashMap<>(); + +> CHANGE 1 : 2 @ 1 : 2 ~ private final Map mapByUUID = Maps.newHashMap(); -> CHANGE 44 : 45 @ 44 : 45 +> CHANGE 10 : 11 @ 10 : 11 + +~ this.mapByOperation.put(i, Sets.newHashSet()); + +> CHANGE 20 : 21 @ 20 : 21 + +~ return this.mapByOperation.get(i); + +> CHANGE 12 : 13 @ 12 : 13 ~ public AttributeModifier getModifier(EaglercraftUUID uuid) { @@ -34,7 +48,15 @@ ~ Set object = (Set) this.mapByName.get(attributemodifier.getName()); -> CHANGE 38 : 39 @ 38 : 39 +> CHANGE 5 : 6 @ 5 : 6 + +~ ((Set) this.mapByOperation.get(attributemodifier.getOperation())).add(attributemodifier); + +> CHANGE 13 : 14 @ 13 : 14 + +~ Set set = (Set) this.mapByOperation.get(i); + +> CHANGE 18 : 19 @ 18 : 19 ~ for (AttributeModifier attributemodifier : (List) Lists.newArrayList(collection)) { diff --git a/patches/minecraft/net/minecraft/entity/ai/attributes/RangedAttribute.edit.java b/patches/minecraft/net/minecraft/entity/ai/attributes/RangedAttribute.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/entity/ai/attributes/RangedAttribute.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/attributes/RangedAttribute.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/ai/attributes/ServersideAttributeMap.edit.java b/patches/minecraft/net/minecraft/entity/ai/attributes/ServersideAttributeMap.edit.java index 8e16278..df70ae7 100644 --- a/patches/minecraft/net/minecraft/entity/ai/attributes/ServersideAttributeMap.edit.java +++ b/patches/minecraft/net/minecraft/entity/ai/attributes/ServersideAttributeMap.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/boss/BossStatus.edit.java b/patches/minecraft/net/minecraft/entity/boss/BossStatus.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/entity/boss/BossStatus.edit.java +++ b/patches/minecraft/net/minecraft/entity/boss/BossStatus.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/boss/EntityDragon.edit.java b/patches/minecraft/net/minecraft/entity/boss/EntityDragon.edit.java index eb74b7a..a393918 100644 --- a/patches/minecraft/net/minecraft/entity/boss/EntityDragon.edit.java +++ b/patches/minecraft/net/minecraft/entity/boss/EntityDragon.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/boss/EntityWither.edit.java b/patches/minecraft/net/minecraft/entity/boss/EntityWither.edit.java index 8d6e6b9..be97217 100644 --- a/patches/minecraft/net/minecraft/entity/boss/EntityWither.edit.java +++ b/patches/minecraft/net/minecraft/entity/boss/EntityWither.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/effect/EntityLightningBolt.edit.java b/patches/minecraft/net/minecraft/entity/effect/EntityLightningBolt.edit.java index be6673d..bcd4bdc 100644 --- a/patches/minecraft/net/minecraft/entity/effect/EntityLightningBolt.edit.java +++ b/patches/minecraft/net/minecraft/entity/effect/EntityLightningBolt.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/item/EntityArmorStand.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityArmorStand.edit.java index 57614da..ab6452f 100644 --- a/patches/minecraft/net/minecraft/entity/item/EntityArmorStand.edit.java +++ b/patches/minecraft/net/minecraft/entity/item/EntityArmorStand.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/item/EntityBoat.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityBoat.edit.java index 1ebc4b2..5f9d820 100644 --- a/patches/minecraft/net/minecraft/entity/item/EntityBoat.edit.java +++ b/patches/minecraft/net/minecraft/entity/item/EntityBoat.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/item/EntityEnderEye.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityEnderEye.edit.java index 4c1585c..b99f051 100644 --- a/patches/minecraft/net/minecraft/entity/item/EntityEnderEye.edit.java +++ b/patches/minecraft/net/minecraft/entity/item/EntityEnderEye.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/item/EntityExpBottle.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityExpBottle.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/item/EntityExpBottle.edit.java +++ b/patches/minecraft/net/minecraft/entity/item/EntityExpBottle.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/item/EntityFallingBlock.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityFallingBlock.edit.java index 6bc1f1b..e929fe1 100644 --- a/patches/minecraft/net/minecraft/entity/item/EntityFallingBlock.edit.java +++ b/patches/minecraft/net/minecraft/entity/item/EntityFallingBlock.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/item/EntityFireworkRocket.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityFireworkRocket.edit.java index 8a4467a..58dfed6 100644 --- a/patches/minecraft/net/minecraft/entity/item/EntityFireworkRocket.edit.java +++ b/patches/minecraft/net/minecraft/entity/item/EntityFireworkRocket.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/item/EntityItem.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityItem.edit.java index ddd7664..a6d04c7 100644 --- a/patches/minecraft/net/minecraft/entity/item/EntityItem.edit.java +++ b/patches/minecraft/net/minecraft/entity/item/EntityItem.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/item/EntityItemFrame.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityItemFrame.edit.java index 1f829a9..4f9769b 100644 --- a/patches/minecraft/net/minecraft/entity/item/EntityItemFrame.edit.java +++ b/patches/minecraft/net/minecraft/entity/item/EntityItemFrame.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/item/EntityMinecart.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityMinecart.edit.java index cab34da..b9fc6bc 100644 --- a/patches/minecraft/net/minecraft/entity/item/EntityMinecart.edit.java +++ b/patches/minecraft/net/minecraft/entity/item/EntityMinecart.edit.java @@ -1,19 +1,15 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> CHANGE 2 : 3 @ 2 : 3 +> CHANGE 2 : 5 @ 2 : 4 ~ import java.util.List; - -> INSERT 1 : 4 @ 1 - -+ -+ import com.google.common.collect.Maps; -+ +~ import com.carrotsearch.hppc.IntObjectHashMap; +~ import com.carrotsearch.hppc.IntObjectMap; > DELETE 8 @ 8 : 13 @@ -50,10 +46,18 @@ + } + -> CHANGE 29 : 32 @ 29 : 32 +> CHANGE 5 : 6 @ 5 : 6 + +~ private static final IntObjectMap ID_LOOKUP = new IntObjectHashMap<>(); + +> CHANGE 17 : 18 @ 17 : 19 + +~ EntityMinecart.EnumMinecartType entityminecart$enumminecarttype = ID_LOOKUP.get(id); + +> CHANGE 4 : 7 @ 4 : 7 ~ EntityMinecart.EnumMinecartType[] types = values(); ~ for (int i = 0; i < types.length; ++i) { -~ ID_LOOKUP.put(Integer.valueOf(types[i].getNetworkID()), types[i]); +~ ID_LOOKUP.put(types[i].getNetworkID(), types[i]); > EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityMinecartChest.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityMinecartChest.edit.java index a7f89c1..300ea78 100644 --- a/patches/minecraft/net/minecraft/entity/item/EntityMinecartChest.edit.java +++ b/patches/minecraft/net/minecraft/entity/item/EntityMinecartChest.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/item/EntityMinecartContainer.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityMinecartContainer.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/entity/item/EntityMinecartContainer.edit.java +++ b/patches/minecraft/net/minecraft/entity/item/EntityMinecartContainer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/item/EntityMinecartEmpty.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityMinecartEmpty.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/item/EntityMinecartEmpty.edit.java +++ b/patches/minecraft/net/minecraft/entity/item/EntityMinecartEmpty.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/item/EntityMinecartFurnace.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityMinecartFurnace.edit.java index 1b45b06..a02feb5 100644 --- a/patches/minecraft/net/minecraft/entity/item/EntityMinecartFurnace.edit.java +++ b/patches/minecraft/net/minecraft/entity/item/EntityMinecartFurnace.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/item/EntityMinecartHopper.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityMinecartHopper.edit.java index 2586cd9..c2210fe 100644 --- a/patches/minecraft/net/minecraft/entity/item/EntityMinecartHopper.edit.java +++ b/patches/minecraft/net/minecraft/entity/item/EntityMinecartHopper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/item/EntityMinecartTNT.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityMinecartTNT.edit.java index 9b0490a..1835771 100644 --- a/patches/minecraft/net/minecraft/entity/item/EntityMinecartTNT.edit.java +++ b/patches/minecraft/net/minecraft/entity/item/EntityMinecartTNT.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/item/EntityPainting.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityPainting.edit.java index b5e22f3..7c341c2 100644 --- a/patches/minecraft/net/minecraft/entity/item/EntityPainting.edit.java +++ b/patches/minecraft/net/minecraft/entity/item/EntityPainting.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/item/EntityTNTPrimed.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityTNTPrimed.edit.java index 96792af..214c329 100644 --- a/patches/minecraft/net/minecraft/entity/item/EntityTNTPrimed.edit.java +++ b/patches/minecraft/net/minecraft/entity/item/EntityTNTPrimed.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/item/EntityXPOrb.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityXPOrb.edit.java index dba6a86..b898969 100644 --- a/patches/minecraft/net/minecraft/entity/item/EntityXPOrb.edit.java +++ b/patches/minecraft/net/minecraft/entity/item/EntityXPOrb.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityBlaze.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityBlaze.edit.java index b64736a..d51baf9 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntityBlaze.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntityBlaze.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityCaveSpider.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityCaveSpider.edit.java index 97688dc..8ad33b5 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntityCaveSpider.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntityCaveSpider.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityCreeper.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityCreeper.edit.java index 8e40818..f98ee0f 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntityCreeper.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntityCreeper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityEnderman.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityEnderman.edit.java index 832f020..d854ea5 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntityEnderman.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntityEnderman.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude @@ -34,8 +34,21 @@ ~ EaglercraftRandom random = this.enderman.getRNG(); -> CHANGE 36 : 37 @ 36 : 37 +> CHANGE 5 : 9 @ 5 : 6 + +~ IBlockState iblockstate = world.getBlockState(blockpos); +~ if (iblockstate == null) +~ return; +~ Block block = iblockstate.getBlock(); + +> CHANGE 30 : 31 @ 30 : 31 ~ EaglercraftRandom random = this.enderman.getRNG(); +> CHANGE 5 : 8 @ 5 : 6 + +~ IBlockState iblockstate = world.getBlockStateIfLoaded(blockpos); +~ if (iblockstate == null) +~ return; + > EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityEndermite.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityEndermite.edit.java index 128526c..a208ff9 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntityEndermite.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntityEndermite.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityGhast.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityGhast.edit.java index 8e04921..bdaf8ae 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntityGhast.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntityGhast.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityGiantZombie.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityGiantZombie.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntityGiantZombie.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntityGiantZombie.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityGuardian.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityGuardian.edit.java index 061a46e..83c98e2 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntityGuardian.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntityGuardian.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityIronGolem.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityIronGolem.edit.java index 293623c..f1daa69 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntityIronGolem.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntityIronGolem.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityMagmaCube.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityMagmaCube.edit.java index 19567d5..1d71946 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntityMagmaCube.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntityMagmaCube.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityMob.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityMob.edit.java index 57614da..ab6452f 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntityMob.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntityMob.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityPigZombie.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityPigZombie.edit.java index 86c6b21..dcd3cea 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntityPigZombie.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntityPigZombie.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/EntitySilverfish.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntitySilverfish.edit.java index 4577c80..daa46cb 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntitySilverfish.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntitySilverfish.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/EntitySkeleton.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntitySkeleton.edit.java index cd8062b..dbf92b4 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntitySkeleton.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntitySkeleton.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/EntitySlime.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntitySlime.edit.java index 8433a56..e238e7a 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntitySlime.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntitySlime.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/EntitySnowman.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntitySnowman.edit.java index b92cc67..e3399de 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntitySnowman.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntitySnowman.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/EntitySpider.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntitySpider.edit.java index b491c23..52e6d73 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntitySpider.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntitySpider.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityWitch.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityWitch.edit.java index bfce8ee..2a522e1 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntityWitch.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntityWitch.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityZombie.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityZombie.edit.java index 20f8f7c..a1c5fd8 100644 --- a/patches/minecraft/net/minecraft/entity/monster/EntityZombie.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/EntityZombie.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/monster/IMob.edit.java b/patches/minecraft/net/minecraft/entity/monster/IMob.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/entity/monster/IMob.edit.java +++ b/patches/minecraft/net/minecraft/entity/monster/IMob.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityAmbientCreature.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityAmbientCreature.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/passive/EntityAmbientCreature.edit.java +++ b/patches/minecraft/net/minecraft/entity/passive/EntityAmbientCreature.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityAnimal.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityAnimal.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/entity/passive/EntityAnimal.edit.java +++ b/patches/minecraft/net/minecraft/entity/passive/EntityAnimal.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityBat.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityBat.edit.java index d6538ee..672dba6 100644 --- a/patches/minecraft/net/minecraft/entity/passive/EntityBat.edit.java +++ b/patches/minecraft/net/minecraft/entity/passive/EntityBat.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityChicken.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityChicken.edit.java index 5464f90..be0202c 100644 --- a/patches/minecraft/net/minecraft/entity/passive/EntityChicken.edit.java +++ b/patches/minecraft/net/minecraft/entity/passive/EntityChicken.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityCow.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityCow.edit.java index 66c6944..ad8a750 100644 --- a/patches/minecraft/net/minecraft/entity/passive/EntityCow.edit.java +++ b/patches/minecraft/net/minecraft/entity/passive/EntityCow.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityHorse.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityHorse.edit.java index 1bc3f89..7ac102f 100644 --- a/patches/minecraft/net/minecraft/entity/passive/EntityHorse.edit.java +++ b/patches/minecraft/net/minecraft/entity/passive/EntityHorse.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityMooshroom.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityMooshroom.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/entity/passive/EntityMooshroom.edit.java +++ b/patches/minecraft/net/minecraft/entity/passive/EntityMooshroom.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityOcelot.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityOcelot.edit.java index 76e70e8..8e6d588 100644 --- a/patches/minecraft/net/minecraft/entity/passive/EntityOcelot.edit.java +++ b/patches/minecraft/net/minecraft/entity/passive/EntityOcelot.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityPig.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityPig.edit.java index 32b22a3..adb20e3 100644 --- a/patches/minecraft/net/minecraft/entity/passive/EntityPig.edit.java +++ b/patches/minecraft/net/minecraft/entity/passive/EntityPig.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityRabbit.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityRabbit.edit.java index a1aeef4..615d8c4 100644 --- a/patches/minecraft/net/minecraft/entity/passive/EntityRabbit.edit.java +++ b/patches/minecraft/net/minecraft/entity/passive/EntityRabbit.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/passive/EntitySheep.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntitySheep.edit.java index 33f8552..6c2100c 100644 --- a/patches/minecraft/net/minecraft/entity/passive/EntitySheep.edit.java +++ b/patches/minecraft/net/minecraft/entity/passive/EntitySheep.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/passive/EntitySquid.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntitySquid.edit.java index be6673d..bcd4bdc 100644 --- a/patches/minecraft/net/minecraft/entity/passive/EntitySquid.edit.java +++ b/patches/minecraft/net/minecraft/entity/passive/EntitySquid.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityTameable.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityTameable.edit.java index cd23459..fc638da 100644 --- a/patches/minecraft/net/minecraft/entity/passive/EntityTameable.edit.java +++ b/patches/minecraft/net/minecraft/entity/passive/EntityTameable.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityVillager.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityVillager.edit.java index 731fb6f..01d0100 100644 --- a/patches/minecraft/net/minecraft/entity/passive/EntityVillager.edit.java +++ b/patches/minecraft/net/minecraft/entity/passive/EntityVillager.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude @@ -12,7 +12,9 @@ ~ ~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; -> CHANGE 77 : 78 @ 77 : 229 +> DELETE 53 @ 53 : 54 + +> CHANGE 23 : 24 @ 23 : 175 ~ private static List>>> DEFAULT_TRADE_LIST_MAP = null; @@ -297,8 +299,32 @@ ~ public void modifyMerchantRecipeList(MerchantRecipeList merchantrecipelist, EaglercraftRandom random) { -> CHANGE 24 : 25 @ 24 : 25 +> CHANGE 19 : 23 @ 19 : 20 -~ public int getPrice(EaglercraftRandom rand) { +~ static class PriceInfo { +~ private int a; +~ private int b; +~ + +> CHANGE 1 : 3 @ 1 : 2 + +~ this.a = parInt1; +~ this.b = parInt2; + +> CHANGE 2 : 4 @ 2 : 7 + +~ public int getFirst() { +~ return this.a; + +> INSERT 1 : 9 @ 1 + ++ ++ public int getSecond() { ++ return this.b; ++ } ++ ++ public int getPrice(EaglercraftRandom rand) { ++ return (a >= b) ? a : a + rand.nextInt(b - a + 1); ++ } > EOF diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityWaterMob.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityWaterMob.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/entity/passive/EntityWaterMob.edit.java +++ b/patches/minecraft/net/minecraft/entity/passive/EntityWaterMob.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityWolf.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityWolf.edit.java index 83e359c..f5cf868 100644 --- a/patches/minecraft/net/minecraft/entity/passive/EntityWolf.edit.java +++ b/patches/minecraft/net/minecraft/entity/passive/EntityWolf.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/player/EntityPlayer.edit.java b/patches/minecraft/net/minecraft/entity/player/EntityPlayer.edit.java index dade11d..a1dd5b4 100644 --- a/patches/minecraft/net/minecraft/entity/player/EntityPlayer.edit.java +++ b/patches/minecraft/net/minecraft/entity/player/EntityPlayer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/player/EntityPlayerMP.edit.java b/patches/minecraft/net/minecraft/entity/player/EntityPlayerMP.edit.java index 8963e4d..c24c98b 100644 --- a/patches/minecraft/net/minecraft/entity/player/EntityPlayerMP.edit.java +++ b/patches/minecraft/net/minecraft/entity/player/EntityPlayerMP.edit.java @@ -1,11 +1,20 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> CHANGE 4 : 6 @ 4 : 6 +> INSERT 2 : 8 @ 2 + ++ import com.carrotsearch.hppc.IntArrayDeque; ++ import com.carrotsearch.hppc.IntDeque; ++ import com.carrotsearch.hppc.LongHashSet; ++ import com.carrotsearch.hppc.LongSet; ++ import com.carrotsearch.hppc.cursors.IntCursor; ++ import com.carrotsearch.hppc.cursors.LongCursor; + +> CHANGE 2 : 4 @ 2 : 4 ~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; ~ import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; @@ -14,7 +23,9 @@ > DELETE 51 @ 51 : 52 -> CHANGE 22 : 26 @ 22 : 24 +> DELETE 15 @ 15 : 16 + +> CHANGE 6 : 10 @ 6 : 8 ~ import net.lax1dude.eaglercraft.v1_8.EagRuntime; ~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; @@ -25,7 +36,12 @@ + -> CHANGE 18 : 19 @ 18 : 19 +> CHANGE 7 : 9 @ 7 : 9 + +~ public final LongSet loadedChunks = new LongHashSet(); +~ private final IntDeque destroyedItemsNetCache = new IntArrayDeque(); + +> CHANGE 9 : 10 @ 9 : 10 ~ private long playerLastActiveTime = EagRuntime.steadyTimeMillis(); @@ -38,17 +54,56 @@ ~ if (!this.openContainer.canInteractWith(this)) { -> CHANGE 51 : 53 @ 51 : 53 +> DELETE 7 @ 7 : 8 + +> CHANGE 2 : 4 @ 2 : 5 + +~ while (!destroyedItemsNetCache.isEmpty() && j < i) { +~ aint[j++] = destroyedItemsNetCache.removeFirst(); + +> CHANGE 6 : 9 @ 6 : 9 + +~ ArrayList arraylist = Lists.newArrayList(); +~ Iterator iterator1 = this.loadedChunks.iterator(); +~ ArrayList arraylist1 = Lists.newArrayList(); + +> CHANGE 2 : 11 @ 2 : 15 + +~ long l = iterator1.next().value; +~ int chunkXPos = (int) (l & 4294967295L); +~ int chunkZPos = (int) (l >>> 32); +~ if (this.worldObj.isBlockLoaded(new BlockPos(chunkXPos << 4, 0, chunkZPos << 4))) { +~ Chunk chunk = this.worldObj.getChunkFromChunkCoords(chunkXPos, chunkZPos); +~ if (chunk.isPopulated()) { +~ arraylist.add(chunk); +~ arraylist1.addAll(((WorldServer) this.worldObj).getTileEntitiesIn(chunkXPos * 16, 0, +~ chunkZPos * 16, chunkXPos * 16 + 16, 256, chunkZPos * 16 + 16)); + +> DELETE 1 @ 1 : 3 + +> CHANGE 5 : 6 @ 5 : 7 + +~ this.playerNetServerHandler.sendPacket(new S21PacketChunkData(arraylist.get(0), true, '\uffff')); + +> CHANGE 4 : 6 @ 4 : 6 ~ for (int i = 0, l = arraylist1.size(); i < l; ++i) { -~ this.sendTileEntityUpdate((TileEntity) arraylist1.get(i)); +~ this.sendTileEntityUpdate(arraylist1.get(i)); -> CHANGE 2 : 4 @ 2 : 4 +> CHANGE 2 : 6 @ 2 : 4 ~ for (int i = 0, l = arraylist.size(); i < l; ++i) { -~ this.getServerForPlayer().getEntityTracker().func_85172_a(this, (Chunk) arraylist.get(i)); +~ Chunk c = arraylist.get(i); +~ this.getServerForPlayer().getEntityTracker().func_85172_a(this, c); +~ this.loadedChunks.removeAll(c.getChunkCoordLong()); -> CHANGE 581 : 582 @ 581 : 582 +> CHANGE 518 : 521 @ 518 : 519 + +~ for (IntCursor cur : ((EntityPlayerMP) oldPlayer).destroyedItemsNetCache) { +~ destroyedItemsNetCache.addLast(cur.value); +~ } + +> CHANGE 62 : 63 @ 62 : 63 ~ if ("seed".equals(s)) { @@ -64,4 +119,8 @@ + this.mcServer.getConfigurationManager().updatePlayerViewDistance(this, packetIn.getViewDistance()); +> CHANGE 27 : 28 @ 27 : 28 + +~ this.destroyedItemsNetCache.addLast(parEntity.getEntityId()); + > EOF diff --git a/patches/minecraft/net/minecraft/entity/player/EnumPlayerModelParts.edit.java b/patches/minecraft/net/minecraft/entity/player/EnumPlayerModelParts.edit.java index da9aee3..69865c2 100644 --- a/patches/minecraft/net/minecraft/entity/player/EnumPlayerModelParts.edit.java +++ b/patches/minecraft/net/minecraft/entity/player/EnumPlayerModelParts.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/player/InventoryPlayer.edit.java b/patches/minecraft/net/minecraft/entity/player/InventoryPlayer.edit.java index 70ecccf..802820c 100644 --- a/patches/minecraft/net/minecraft/entity/player/InventoryPlayer.edit.java +++ b/patches/minecraft/net/minecraft/entity/player/InventoryPlayer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntityArrow.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntityArrow.edit.java index 057f232..a6cb133 100644 --- a/patches/minecraft/net/minecraft/entity/projectile/EntityArrow.edit.java +++ b/patches/minecraft/net/minecraft/entity/projectile/EntityArrow.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntityEgg.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntityEgg.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/entity/projectile/EntityEgg.edit.java +++ b/patches/minecraft/net/minecraft/entity/projectile/EntityEgg.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntityFireball.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntityFireball.edit.java index 32fe8ad..3ce28d7 100644 --- a/patches/minecraft/net/minecraft/entity/projectile/EntityFireball.edit.java +++ b/patches/minecraft/net/minecraft/entity/projectile/EntityFireball.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntityLargeFireball.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntityLargeFireball.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/entity/projectile/EntityLargeFireball.edit.java +++ b/patches/minecraft/net/minecraft/entity/projectile/EntityLargeFireball.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntityPotion.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntityPotion.edit.java index de1a02d..c962e15 100644 --- a/patches/minecraft/net/minecraft/entity/projectile/EntityPotion.edit.java +++ b/patches/minecraft/net/minecraft/entity/projectile/EntityPotion.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntitySmallFireball.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntitySmallFireball.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/entity/projectile/EntitySmallFireball.edit.java +++ b/patches/minecraft/net/minecraft/entity/projectile/EntitySmallFireball.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntitySnowball.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntitySnowball.edit.java index 440554e..deff983 100644 --- a/patches/minecraft/net/minecraft/entity/projectile/EntitySnowball.edit.java +++ b/patches/minecraft/net/minecraft/entity/projectile/EntitySnowball.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntityThrowable.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntityThrowable.edit.java index 7b85b73..acfc27e 100644 --- a/patches/minecraft/net/minecraft/entity/projectile/EntityThrowable.edit.java +++ b/patches/minecraft/net/minecraft/entity/projectile/EntityThrowable.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntityWitherSkull.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntityWitherSkull.edit.java index 97688dc..8ad33b5 100644 --- a/patches/minecraft/net/minecraft/entity/projectile/EntityWitherSkull.edit.java +++ b/patches/minecraft/net/minecraft/entity/projectile/EntityWitherSkull.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/event/ClickEvent.edit.java b/patches/minecraft/net/minecraft/event/ClickEvent.edit.java index 70d5174..679072e 100644 --- a/patches/minecraft/net/minecraft/event/ClickEvent.edit.java +++ b/patches/minecraft/net/minecraft/event/ClickEvent.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/event/HoverEvent.edit.java b/patches/minecraft/net/minecraft/event/HoverEvent.edit.java index c8b1da0..2d12a25 100644 --- a/patches/minecraft/net/minecraft/event/HoverEvent.edit.java +++ b/patches/minecraft/net/minecraft/event/HoverEvent.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/init/Blocks.edit.java b/patches/minecraft/net/minecraft/init/Blocks.edit.java index c42d27b..9eba0dd 100644 --- a/patches/minecraft/net/minecraft/init/Blocks.edit.java +++ b/patches/minecraft/net/minecraft/init/Blocks.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/init/Bootstrap.edit.java b/patches/minecraft/net/minecraft/init/Bootstrap.edit.java index a257df0..7b18b1d 100644 --- a/patches/minecraft/net/minecraft/init/Bootstrap.edit.java +++ b/patches/minecraft/net/minecraft/init/Bootstrap.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/init/Items.edit.java b/patches/minecraft/net/minecraft/init/Items.edit.java index 3a74e8a..fc41567 100644 --- a/patches/minecraft/net/minecraft/init/Items.edit.java +++ b/patches/minecraft/net/minecraft/init/Items.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/AnimalChest.edit.java b/patches/minecraft/net/minecraft/inventory/AnimalChest.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/inventory/AnimalChest.edit.java +++ b/patches/minecraft/net/minecraft/inventory/AnimalChest.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/Container.edit.java b/patches/minecraft/net/minecraft/inventory/Container.edit.java index 7f92d03..c7e32bc 100644 --- a/patches/minecraft/net/minecraft/inventory/Container.edit.java +++ b/patches/minecraft/net/minecraft/inventory/Container.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/ContainerBeacon.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerBeacon.edit.java index 5c93954..9e61eac 100644 --- a/patches/minecraft/net/minecraft/inventory/ContainerBeacon.edit.java +++ b/patches/minecraft/net/minecraft/inventory/ContainerBeacon.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/ContainerBrewingStand.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerBrewingStand.edit.java index b298ca7..ad4e1b8 100644 --- a/patches/minecraft/net/minecraft/inventory/ContainerBrewingStand.edit.java +++ b/patches/minecraft/net/minecraft/inventory/ContainerBrewingStand.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/ContainerChest.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerChest.edit.java index 9c2c149..a9d03a6 100644 --- a/patches/minecraft/net/minecraft/inventory/ContainerChest.edit.java +++ b/patches/minecraft/net/minecraft/inventory/ContainerChest.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/ContainerDispenser.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerDispenser.edit.java index 9c2c149..a9d03a6 100644 --- a/patches/minecraft/net/minecraft/inventory/ContainerDispenser.edit.java +++ b/patches/minecraft/net/minecraft/inventory/ContainerDispenser.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/ContainerEnchantment.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerEnchantment.edit.java index f0714ce..ed08108 100644 --- a/patches/minecraft/net/minecraft/inventory/ContainerEnchantment.edit.java +++ b/patches/minecraft/net/minecraft/inventory/ContainerEnchantment.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/ContainerFurnace.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerFurnace.edit.java index 6a066c5..6352617 100644 --- a/patches/minecraft/net/minecraft/inventory/ContainerFurnace.edit.java +++ b/patches/minecraft/net/minecraft/inventory/ContainerFurnace.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/ContainerHopper.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerHopper.edit.java index 0401ba9..8fdc3b6 100644 --- a/patches/minecraft/net/minecraft/inventory/ContainerHopper.edit.java +++ b/patches/minecraft/net/minecraft/inventory/ContainerHopper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/ContainerHorseInventory.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerHorseInventory.edit.java index 3e397c6..e49d91f 100644 --- a/patches/minecraft/net/minecraft/inventory/ContainerHorseInventory.edit.java +++ b/patches/minecraft/net/minecraft/inventory/ContainerHorseInventory.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/ContainerMerchant.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerMerchant.edit.java index 6f470b4..88f3c02 100644 --- a/patches/minecraft/net/minecraft/inventory/ContainerMerchant.edit.java +++ b/patches/minecraft/net/minecraft/inventory/ContainerMerchant.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/ContainerPlayer.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerPlayer.edit.java index afed65a..c5c7034 100644 --- a/patches/minecraft/net/minecraft/inventory/ContainerPlayer.edit.java +++ b/patches/minecraft/net/minecraft/inventory/ContainerPlayer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/ContainerRepair.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerRepair.edit.java index 0499261..5141052 100644 --- a/patches/minecraft/net/minecraft/inventory/ContainerRepair.edit.java +++ b/patches/minecraft/net/minecraft/inventory/ContainerRepair.edit.java @@ -1,15 +1,51 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> DELETE 12 @ 12 : 18 +> DELETE 2 @ 2 : 4 -> CHANGE 4 : 6 @ 4 : 6 +> DELETE 8 @ 8 : 14 -~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; -~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +> DELETE 4 @ 4 : 6 + +> INSERT 1 : 8 @ 1 + ++ import com.carrotsearch.hppc.IntIntMap; ++ import com.carrotsearch.hppc.cursors.IntCursor; ++ import com.carrotsearch.hppc.cursors.IntIntCursor; ++ ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ + +> CHANGE 118 : 119 @ 118 : 119 + +~ IntIntMap map = EnchantmentHelper.getEnchantments(itemstack1); + +> CHANGE 47 : 48 @ 47 : 49 + +~ IntIntMap map1 = EnchantmentHelper.getEnchantments(itemstack2); + +> CHANGE 1 : 3 @ 1 : 3 + +~ for (IntIntCursor cur : map1) { +~ int i3 = cur.key; + +> CHANGE 2 : 4 @ 2 : 6 + +~ int k3 = map.getOrDefault(i3, 0); +~ int l1 = cur.value; + +> CHANGE 15 : 17 @ 15 : 19 + +~ for (IntCursor curr : map.keys()) { +~ int i2 = curr.value; + +> CHANGE 11 : 12 @ 11 : 12 + +~ map.put(i3, l1); > EOF diff --git a/patches/minecraft/net/minecraft/inventory/ContainerWorkbench.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerWorkbench.edit.java index 850a8e0..3b51785 100644 --- a/patches/minecraft/net/minecraft/inventory/ContainerWorkbench.edit.java +++ b/patches/minecraft/net/minecraft/inventory/ContainerWorkbench.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/ICrafting.edit.java b/patches/minecraft/net/minecraft/inventory/ICrafting.edit.java index 7ecd9a8..49457c1 100644 --- a/patches/minecraft/net/minecraft/inventory/ICrafting.edit.java +++ b/patches/minecraft/net/minecraft/inventory/ICrafting.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/IInvBasic.edit.java b/patches/minecraft/net/minecraft/inventory/IInvBasic.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/inventory/IInvBasic.edit.java +++ b/patches/minecraft/net/minecraft/inventory/IInvBasic.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/ISidedInventory.edit.java b/patches/minecraft/net/minecraft/inventory/ISidedInventory.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/inventory/ISidedInventory.edit.java +++ b/patches/minecraft/net/minecraft/inventory/ISidedInventory.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/InventoryBasic.edit.java b/patches/minecraft/net/minecraft/inventory/InventoryBasic.edit.java index ce79f45..f6a4810 100644 --- a/patches/minecraft/net/minecraft/inventory/InventoryBasic.edit.java +++ b/patches/minecraft/net/minecraft/inventory/InventoryBasic.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/InventoryCraftResult.edit.java b/patches/minecraft/net/minecraft/inventory/InventoryCraftResult.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/inventory/InventoryCraftResult.edit.java +++ b/patches/minecraft/net/minecraft/inventory/InventoryCraftResult.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/InventoryCrafting.edit.java b/patches/minecraft/net/minecraft/inventory/InventoryCrafting.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/inventory/InventoryCrafting.edit.java +++ b/patches/minecraft/net/minecraft/inventory/InventoryCrafting.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/InventoryEnderChest.edit.java b/patches/minecraft/net/minecraft/inventory/InventoryEnderChest.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/inventory/InventoryEnderChest.edit.java +++ b/patches/minecraft/net/minecraft/inventory/InventoryEnderChest.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/InventoryHelper.edit.java b/patches/minecraft/net/minecraft/inventory/InventoryHelper.edit.java index f366889..a21c9f2 100644 --- a/patches/minecraft/net/minecraft/inventory/InventoryHelper.edit.java +++ b/patches/minecraft/net/minecraft/inventory/InventoryHelper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/InventoryLargeChest.edit.java b/patches/minecraft/net/minecraft/inventory/InventoryLargeChest.edit.java index 0401ba9..8fdc3b6 100644 --- a/patches/minecraft/net/minecraft/inventory/InventoryLargeChest.edit.java +++ b/patches/minecraft/net/minecraft/inventory/InventoryLargeChest.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/InventoryMerchant.edit.java b/patches/minecraft/net/minecraft/inventory/InventoryMerchant.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/inventory/InventoryMerchant.edit.java +++ b/patches/minecraft/net/minecraft/inventory/InventoryMerchant.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/Slot.edit.java b/patches/minecraft/net/minecraft/inventory/Slot.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/inventory/Slot.edit.java +++ b/patches/minecraft/net/minecraft/inventory/Slot.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/SlotCrafting.edit.java b/patches/minecraft/net/minecraft/inventory/SlotCrafting.edit.java index 3e397c6..e49d91f 100644 --- a/patches/minecraft/net/minecraft/inventory/SlotCrafting.edit.java +++ b/patches/minecraft/net/minecraft/inventory/SlotCrafting.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/SlotFurnaceFuel.edit.java b/patches/minecraft/net/minecraft/inventory/SlotFurnaceFuel.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/inventory/SlotFurnaceFuel.edit.java +++ b/patches/minecraft/net/minecraft/inventory/SlotFurnaceFuel.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/SlotFurnaceOutput.edit.java b/patches/minecraft/net/minecraft/inventory/SlotFurnaceOutput.edit.java index 41da09f..86bf348 100644 --- a/patches/minecraft/net/minecraft/inventory/SlotFurnaceOutput.edit.java +++ b/patches/minecraft/net/minecraft/inventory/SlotFurnaceOutput.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/inventory/SlotMerchantResult.edit.java b/patches/minecraft/net/minecraft/inventory/SlotMerchantResult.edit.java index a7f89c1..300ea78 100644 --- a/patches/minecraft/net/minecraft/inventory/SlotMerchantResult.edit.java +++ b/patches/minecraft/net/minecraft/inventory/SlotMerchantResult.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/EnumDyeColor.edit.java b/patches/minecraft/net/minecraft/item/EnumDyeColor.edit.java index 6e75ce5..b64a70a 100644 --- a/patches/minecraft/net/minecraft/item/EnumDyeColor.edit.java +++ b/patches/minecraft/net/minecraft/item/EnumDyeColor.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/Item.edit.java b/patches/minecraft/net/minecraft/item/Item.edit.java index b0c1e92..bc24b36 100644 --- a/patches/minecraft/net/minecraft/item/Item.edit.java +++ b/patches/minecraft/net/minecraft/item/Item.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemAnvilBlock.edit.java b/patches/minecraft/net/minecraft/item/ItemAnvilBlock.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/item/ItemAnvilBlock.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemAnvilBlock.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemAppleGold.edit.java b/patches/minecraft/net/minecraft/item/ItemAppleGold.edit.java index b298ca7..ad4e1b8 100644 --- a/patches/minecraft/net/minecraft/item/ItemAppleGold.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemAppleGold.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemArmor.edit.java b/patches/minecraft/net/minecraft/item/ItemArmor.edit.java index 1284162..d15652f 100644 --- a/patches/minecraft/net/minecraft/item/ItemArmor.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemArmor.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemArmorStand.edit.java b/patches/minecraft/net/minecraft/item/ItemArmorStand.edit.java index 67f9ab1..127bca3 100644 --- a/patches/minecraft/net/minecraft/item/ItemArmorStand.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemArmorStand.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemAxe.edit.java b/patches/minecraft/net/minecraft/item/ItemAxe.edit.java index 7eb97e3..634c650 100644 --- a/patches/minecraft/net/minecraft/item/ItemAxe.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemAxe.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemBanner.edit.java b/patches/minecraft/net/minecraft/item/ItemBanner.edit.java index 40a6641..1b2a3e7 100644 --- a/patches/minecraft/net/minecraft/item/ItemBanner.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemBanner.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemBed.edit.java b/patches/minecraft/net/minecraft/item/ItemBed.edit.java index b723c56..a3680e3 100644 --- a/patches/minecraft/net/minecraft/item/ItemBed.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemBed.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemBlock.edit.java b/patches/minecraft/net/minecraft/item/ItemBlock.edit.java index 34701ff..767af7f 100644 --- a/patches/minecraft/net/minecraft/item/ItemBlock.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemBlock.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemBoat.edit.java b/patches/minecraft/net/minecraft/item/ItemBoat.edit.java index 7caedb4..b851bf4 100644 --- a/patches/minecraft/net/minecraft/item/ItemBoat.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemBoat.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemBook.edit.java b/patches/minecraft/net/minecraft/item/ItemBook.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/item/ItemBook.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemBook.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemBow.edit.java b/patches/minecraft/net/minecraft/item/ItemBow.edit.java index 3f5c9b6..38b5da1 100644 --- a/patches/minecraft/net/minecraft/item/ItemBow.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemBow.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemBucket.edit.java b/patches/minecraft/net/minecraft/item/ItemBucket.edit.java index 2e74d30..79251a6 100644 --- a/patches/minecraft/net/minecraft/item/ItemBucket.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemBucket.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemBucketMilk.edit.java b/patches/minecraft/net/minecraft/item/ItemBucketMilk.edit.java index 3e397c6..e49d91f 100644 --- a/patches/minecraft/net/minecraft/item/ItemBucketMilk.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemBucketMilk.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemCarrotOnAStick.edit.java b/patches/minecraft/net/minecraft/item/ItemCarrotOnAStick.edit.java index 6422a38..77b3843 100644 --- a/patches/minecraft/net/minecraft/item/ItemCarrotOnAStick.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemCarrotOnAStick.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemCloth.edit.java b/patches/minecraft/net/minecraft/item/ItemCloth.edit.java index 9c2c149..a9d03a6 100644 --- a/patches/minecraft/net/minecraft/item/ItemCloth.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemCloth.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemCoal.edit.java b/patches/minecraft/net/minecraft/item/ItemCoal.edit.java index 9a4a33d..e10898a 100644 --- a/patches/minecraft/net/minecraft/item/ItemCoal.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemCoal.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemColored.edit.java b/patches/minecraft/net/minecraft/item/ItemColored.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/item/ItemColored.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemColored.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemDoor.edit.java b/patches/minecraft/net/minecraft/item/ItemDoor.edit.java index 81d6176..7d09693 100644 --- a/patches/minecraft/net/minecraft/item/ItemDoor.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemDoor.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemDoublePlant.edit.java b/patches/minecraft/net/minecraft/item/ItemDoublePlant.edit.java index 132f52d..fd14b8b 100644 --- a/patches/minecraft/net/minecraft/item/ItemDoublePlant.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemDoublePlant.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemDye.edit.java b/patches/minecraft/net/minecraft/item/ItemDye.edit.java index d7ebedc..1d96c0b 100644 --- a/patches/minecraft/net/minecraft/item/ItemDye.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemDye.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemEditableBook.edit.java b/patches/minecraft/net/minecraft/item/ItemEditableBook.edit.java index 0dbac23..490b033 100644 --- a/patches/minecraft/net/minecraft/item/ItemEditableBook.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemEditableBook.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemEgg.edit.java b/patches/minecraft/net/minecraft/item/ItemEgg.edit.java index e49d647..a56dc3b 100644 --- a/patches/minecraft/net/minecraft/item/ItemEgg.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemEgg.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemEmptyMap.edit.java b/patches/minecraft/net/minecraft/item/ItemEmptyMap.edit.java index 3e397c6..e49d91f 100644 --- a/patches/minecraft/net/minecraft/item/ItemEmptyMap.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemEmptyMap.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemEnchantedBook.edit.java b/patches/minecraft/net/minecraft/item/ItemEnchantedBook.edit.java index e60197d..f0a4703 100644 --- a/patches/minecraft/net/minecraft/item/ItemEnchantedBook.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemEnchantedBook.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemEnderEye.edit.java b/patches/minecraft/net/minecraft/item/ItemEnderEye.edit.java index 97f5521..73fb6ec 100644 --- a/patches/minecraft/net/minecraft/item/ItemEnderEye.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemEnderEye.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemEnderPearl.edit.java b/patches/minecraft/net/minecraft/item/ItemEnderPearl.edit.java index 76ddab4..bddc487 100644 --- a/patches/minecraft/net/minecraft/item/ItemEnderPearl.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemEnderPearl.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemExpBottle.edit.java b/patches/minecraft/net/minecraft/item/ItemExpBottle.edit.java index 85734f1..5cf9003 100644 --- a/patches/minecraft/net/minecraft/item/ItemExpBottle.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemExpBottle.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemFireball.edit.java b/patches/minecraft/net/minecraft/item/ItemFireball.edit.java index 6422a38..77b3843 100644 --- a/patches/minecraft/net/minecraft/item/ItemFireball.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemFireball.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemFirework.edit.java b/patches/minecraft/net/minecraft/item/ItemFirework.edit.java index f9c9b4d..a4a2e47 100644 --- a/patches/minecraft/net/minecraft/item/ItemFirework.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemFirework.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemFireworkCharge.edit.java b/patches/minecraft/net/minecraft/item/ItemFireworkCharge.edit.java index 1d13cfe..b25f10d 100644 --- a/patches/minecraft/net/minecraft/item/ItemFireworkCharge.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemFireworkCharge.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemFishFood.edit.java b/patches/minecraft/net/minecraft/item/ItemFishFood.edit.java index 3eb8c4b..c6158dd 100644 --- a/patches/minecraft/net/minecraft/item/ItemFishFood.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemFishFood.edit.java @@ -1,17 +1,18 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 3 -> INSERT 2 : 5 @ 2 +> CHANGE 1 : 5 @ 1 : 2 -+ -+ import com.google.common.collect.Maps; -+ +~ +~ import com.carrotsearch.hppc.IntObjectHashMap; +~ import com.carrotsearch.hppc.IntObjectMap; +~ > DELETE 2 @ 2 : 5 @@ -21,10 +22,18 @@ ~ for (int i = 0; i < types.length; ++i) { ~ ItemFishFood.FishType itemfishfood$fishtype = types[i]; -> CHANGE 86 : 89 @ 86 : 88 +> CHANGE 17 : 18 @ 17 : 18 + +~ private static final IntObjectMap META_LOOKUP = new IntObjectHashMap<>(); + +> CHANGE 58 : 59 @ 58 : 60 + +~ ItemFishFood.FishType itemfishfood$fishtype = META_LOOKUP.get(meta); + +> CHANGE 8 : 11 @ 8 : 10 ~ ItemFishFood.FishType[] types = values(); ~ for (int i = 0; i < types.length; ++i) { -~ META_LOOKUP.put(Integer.valueOf(types[i].getMetadata()), types[i]); +~ META_LOOKUP.put(types[i].getMetadata(), types[i]); > EOF diff --git a/patches/minecraft/net/minecraft/item/ItemFishingRod.edit.java b/patches/minecraft/net/minecraft/item/ItemFishingRod.edit.java index 41da09f..86bf348 100644 --- a/patches/minecraft/net/minecraft/item/ItemFishingRod.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemFishingRod.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemFlintAndSteel.edit.java b/patches/minecraft/net/minecraft/item/ItemFlintAndSteel.edit.java index 6422a38..77b3843 100644 --- a/patches/minecraft/net/minecraft/item/ItemFlintAndSteel.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemFlintAndSteel.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemFood.edit.java b/patches/minecraft/net/minecraft/item/ItemFood.edit.java index 0401ba9..8fdc3b6 100644 --- a/patches/minecraft/net/minecraft/item/ItemFood.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemFood.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemGlassBottle.edit.java b/patches/minecraft/net/minecraft/item/ItemGlassBottle.edit.java index 6422a38..77b3843 100644 --- a/patches/minecraft/net/minecraft/item/ItemGlassBottle.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemGlassBottle.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemHangingEntity.edit.java b/patches/minecraft/net/minecraft/item/ItemHangingEntity.edit.java index f8c581e..1f521f9 100644 --- a/patches/minecraft/net/minecraft/item/ItemHangingEntity.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemHangingEntity.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemHoe.edit.java b/patches/minecraft/net/minecraft/item/ItemHoe.edit.java index 37072b7..f7fbcd0 100644 --- a/patches/minecraft/net/minecraft/item/ItemHoe.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemHoe.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemLead.edit.java b/patches/minecraft/net/minecraft/item/ItemLead.edit.java index 6fb0a30..3ec1757 100644 --- a/patches/minecraft/net/minecraft/item/ItemLead.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemLead.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemLeaves.edit.java b/patches/minecraft/net/minecraft/item/ItemLeaves.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/item/ItemLeaves.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemLeaves.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemLilyPad.edit.java b/patches/minecraft/net/minecraft/item/ItemLilyPad.edit.java index 3f5c9b6..38b5da1 100644 --- a/patches/minecraft/net/minecraft/item/ItemLilyPad.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemLilyPad.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemMap.edit.java b/patches/minecraft/net/minecraft/item/ItemMap.edit.java index 660fcbf..d6f5e87 100644 --- a/patches/minecraft/net/minecraft/item/ItemMap.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemMap.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemMapBase.edit.java b/patches/minecraft/net/minecraft/item/ItemMapBase.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/item/ItemMapBase.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemMapBase.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemMinecart.edit.java b/patches/minecraft/net/minecraft/item/ItemMinecart.edit.java index b92cc67..e3399de 100644 --- a/patches/minecraft/net/minecraft/item/ItemMinecart.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemMinecart.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemMonsterPlacer.edit.java b/patches/minecraft/net/minecraft/item/ItemMonsterPlacer.edit.java index f72c226..663b6df 100644 --- a/patches/minecraft/net/minecraft/item/ItemMonsterPlacer.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemMonsterPlacer.edit.java @@ -1,10 +1,21 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> DELETE 14 @ 14 : 16 +> INSERT 3 : 6 @ 3 + ++ ++ import com.carrotsearch.hppc.cursors.ObjectCursor; ++ + +> DELETE 11 @ 11 : 13 + +> CHANGE 147 : 149 @ 147 : 149 + +~ for (ObjectCursor entitylist$entityegginfo : EntityList.entityEggs.values()) { +~ list.add(new ItemStack(item, 1, entitylist$entityegginfo.value.spawnedID)); > EOF diff --git a/patches/minecraft/net/minecraft/item/ItemMultiTexture.edit.java b/patches/minecraft/net/minecraft/item/ItemMultiTexture.edit.java index 9a4a33d..e10898a 100644 --- a/patches/minecraft/net/minecraft/item/ItemMultiTexture.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemMultiTexture.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemNameTag.edit.java b/patches/minecraft/net/minecraft/item/ItemNameTag.edit.java index 6422a38..77b3843 100644 --- a/patches/minecraft/net/minecraft/item/ItemNameTag.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemNameTag.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemPickaxe.edit.java b/patches/minecraft/net/minecraft/item/ItemPickaxe.edit.java index 3b7281d..fa0933b 100644 --- a/patches/minecraft/net/minecraft/item/ItemPickaxe.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemPickaxe.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemPiston.edit.java b/patches/minecraft/net/minecraft/item/ItemPiston.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/item/ItemPiston.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemPiston.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemPotion.edit.java b/patches/minecraft/net/minecraft/item/ItemPotion.edit.java index 48537b9..9962528 100644 --- a/patches/minecraft/net/minecraft/item/ItemPotion.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemPotion.edit.java @@ -1,24 +1,51 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 5 -> INSERT 5 : 11 @ 5 +> DELETE 1 @ 1 : 2 + +> INSERT 3 : 13 @ 3 + import java.util.Set; + ++ import com.carrotsearch.hppc.IntObjectHashMap; ++ import com.carrotsearch.hppc.IntObjectMap; ++ import com.carrotsearch.hppc.ObjectIntHashMap; ++ import com.carrotsearch.hppc.ObjectIntMap; ++ import com.carrotsearch.hppc.cursors.IntCursor; + import com.google.common.collect.HashMultimap; + import com.google.common.collect.Lists; -+ import com.google.common.collect.Maps; + > DELETE 6 @ 6 : 9 -> CHANGE 62 : 63 @ 62 : 63 +> CHANGE 11 : 13 @ 11 : 13 + +~ private IntObjectMap> effectCache = new IntObjectHashMap<>(); +~ private static final ObjectIntMap> SUB_ITEMS_CACHE = new ObjectIntHashMap<>(); + +> CHANGE 23 : 24 @ 23 : 24 + +~ List list = this.effectCache.get(stack.getMetadata()); + +> CHANGE 2 : 3 @ 2 : 3 + +~ this.effectCache.put(stack.getMetadata(), list); + +> CHANGE 7 : 8 @ 7 : 8 + +~ List list = this.effectCache.get(meta); + +> CHANGE 2 : 3 @ 2 : 3 + +~ this.effectCache.put(meta, list); + +> CHANGE 11 : 12 @ 11 : 12 ~ List list = this.getEffects(itemstack); @@ -53,4 +80,13 @@ ~ for (Entry entry1 : (Set>) hashmultimap.entries()) { +> CHANGE 56 : 57 @ 56 : 57 + +~ SUB_ITEMS_CACHE.put(list, i1); + +> CHANGE 6 : 8 @ 6 : 11 + +~ for (IntCursor cur : SUB_ITEMS_CACHE.values()) { +~ subItems.add(new ItemStack(itemIn, 1, cur.value)); + > EOF diff --git a/patches/minecraft/net/minecraft/item/ItemRecord.edit.java b/patches/minecraft/net/minecraft/item/ItemRecord.edit.java index 01394b0..b6f6562 100644 --- a/patches/minecraft/net/minecraft/item/ItemRecord.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemRecord.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemRedstone.edit.java b/patches/minecraft/net/minecraft/item/ItemRedstone.edit.java index 81d6176..7d09693 100644 --- a/patches/minecraft/net/minecraft/item/ItemRedstone.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemRedstone.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemReed.edit.java b/patches/minecraft/net/minecraft/item/ItemReed.edit.java index 3f5c9b6..38b5da1 100644 --- a/patches/minecraft/net/minecraft/item/ItemReed.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemReed.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemSaddle.edit.java b/patches/minecraft/net/minecraft/item/ItemSaddle.edit.java index 6422a38..77b3843 100644 --- a/patches/minecraft/net/minecraft/item/ItemSaddle.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemSaddle.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemSeedFood.edit.java b/patches/minecraft/net/minecraft/item/ItemSeedFood.edit.java index a7f89c1..300ea78 100644 --- a/patches/minecraft/net/minecraft/item/ItemSeedFood.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemSeedFood.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemSeeds.edit.java b/patches/minecraft/net/minecraft/item/ItemSeeds.edit.java index 41da09f..86bf348 100644 --- a/patches/minecraft/net/minecraft/item/ItemSeeds.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemSeeds.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemShears.edit.java b/patches/minecraft/net/minecraft/item/ItemShears.edit.java index 81d6176..7d09693 100644 --- a/patches/minecraft/net/minecraft/item/ItemShears.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemShears.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemSign.edit.java b/patches/minecraft/net/minecraft/item/ItemSign.edit.java index c153820..ebd04dc 100644 --- a/patches/minecraft/net/minecraft/item/ItemSign.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemSign.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemSimpleFoiled.edit.java b/patches/minecraft/net/minecraft/item/ItemSimpleFoiled.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/item/ItemSimpleFoiled.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemSimpleFoiled.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemSkull.edit.java b/patches/minecraft/net/minecraft/item/ItemSkull.edit.java index 83d3f63..ac6e4e8 100644 --- a/patches/minecraft/net/minecraft/item/ItemSkull.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemSkull.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemSlab.edit.java b/patches/minecraft/net/minecraft/item/ItemSlab.edit.java index defbc84..6686c96 100644 --- a/patches/minecraft/net/minecraft/item/ItemSlab.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemSlab.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemSnow.edit.java b/patches/minecraft/net/minecraft/item/ItemSnow.edit.java index 6422a38..77b3843 100644 --- a/patches/minecraft/net/minecraft/item/ItemSnow.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemSnow.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemSnowball.edit.java b/patches/minecraft/net/minecraft/item/ItemSnowball.edit.java index e49d647..a56dc3b 100644 --- a/patches/minecraft/net/minecraft/item/ItemSnowball.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemSnowball.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemSoup.edit.java b/patches/minecraft/net/minecraft/item/ItemSoup.edit.java index a7f89c1..300ea78 100644 --- a/patches/minecraft/net/minecraft/item/ItemSoup.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemSoup.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemSpade.edit.java b/patches/minecraft/net/minecraft/item/ItemSpade.edit.java index 422a7fb..4f5cd27 100644 --- a/patches/minecraft/net/minecraft/item/ItemSpade.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemSpade.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemStack.edit.java b/patches/minecraft/net/minecraft/item/ItemStack.edit.java index 2133a4c..5e631a9 100644 --- a/patches/minecraft/net/minecraft/item/ItemStack.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemStack.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemSword.edit.java b/patches/minecraft/net/minecraft/item/ItemSword.edit.java index a6c6599..6aa30dd 100644 --- a/patches/minecraft/net/minecraft/item/ItemSword.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemSword.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemTool.edit.java b/patches/minecraft/net/minecraft/item/ItemTool.edit.java index 448c486..e0dbbb8 100644 --- a/patches/minecraft/net/minecraft/item/ItemTool.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemTool.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/ItemWritableBook.edit.java b/patches/minecraft/net/minecraft/item/ItemWritableBook.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/item/ItemWritableBook.edit.java +++ b/patches/minecraft/net/minecraft/item/ItemWritableBook.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/crafting/CraftingManager.edit.java b/patches/minecraft/net/minecraft/item/crafting/CraftingManager.edit.java index 74ed1a3..1b14806 100644 --- a/patches/minecraft/net/minecraft/item/crafting/CraftingManager.edit.java +++ b/patches/minecraft/net/minecraft/item/crafting/CraftingManager.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/crafting/FurnaceRecipes.edit.java b/patches/minecraft/net/minecraft/item/crafting/FurnaceRecipes.edit.java index 715fced..3a8d6d9 100644 --- a/patches/minecraft/net/minecraft/item/crafting/FurnaceRecipes.edit.java +++ b/patches/minecraft/net/minecraft/item/crafting/FurnaceRecipes.edit.java @@ -1,15 +1,18 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 3 -> INSERT 2 : 5 @ 2 +> INSERT 2 : 8 @ 2 + ++ import com.carrotsearch.hppc.ObjectFloatHashMap; ++ import com.carrotsearch.hppc.ObjectFloatMap; ++ import com.carrotsearch.hppc.cursors.ObjectFloatCursor; + import com.google.common.collect.Maps; + @@ -17,7 +20,11 @@ ~ private static FurnaceRecipes smeltingBase; -> INSERT 4 : 7 @ 4 +> CHANGE 1 : 2 @ 1 : 2 + +~ private ObjectFloatMap experienceList = new ObjectFloatHashMap<>(); + +> INSERT 2 : 5 @ 2 + if (smeltingBase == null) { + smeltingBase = new FurnaceRecipes(); @@ -29,4 +36,14 @@ ~ for (int i = 0; i < types.length; ++i) { ~ ItemFishFood.FishType itemfishfood$fishtype = types[i]; +> CHANGE 23 : 24 @ 23 : 24 + +~ this.experienceList.put(stack, experience); + +> CHANGE 22 : 25 @ 22 : 25 + +~ for (ObjectFloatCursor entry : this.experienceList) { +~ if (this.compareItemStacks(stack, entry.key)) { +~ return entry.value; + > EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipeBookCloning.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipeBookCloning.edit.java index 97688dc..8ad33b5 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipeBookCloning.edit.java +++ b/patches/minecraft/net/minecraft/item/crafting/RecipeBookCloning.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipeFireworks.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipeFireworks.edit.java index 6134258..5b40457 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipeFireworks.edit.java +++ b/patches/minecraft/net/minecraft/item/crafting/RecipeFireworks.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipeRepairItem.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipeRepairItem.edit.java index 59aefe3..e81a706 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipeRepairItem.edit.java +++ b/patches/minecraft/net/minecraft/item/crafting/RecipeRepairItem.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesArmor.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesArmor.edit.java index be6673d..bcd4bdc 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipesArmor.edit.java +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesArmor.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.edit.java index c03136a..efca3be 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.edit.java +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesBanners.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesBanners.edit.java index 3ee9ac1..841b25f 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipesBanners.edit.java +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesBanners.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesCrafting.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesCrafting.edit.java index 32b22a3..adb20e3 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipesCrafting.edit.java +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesCrafting.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesDyes.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesDyes.edit.java index a2cda1e..d33891a 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipesDyes.edit.java +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesDyes.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesFood.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesFood.edit.java index 97688dc..8ad33b5 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipesFood.edit.java +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesFood.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesIngots.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesIngots.edit.java index 5fe0a79..1588476 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipesIngots.edit.java +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesIngots.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesMapCloning.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesMapCloning.edit.java index be6673d..bcd4bdc 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipesMapCloning.edit.java +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesMapCloning.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesMapExtending.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesMapExtending.edit.java index be6673d..bcd4bdc 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipesMapExtending.edit.java +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesMapExtending.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesTools.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesTools.edit.java index f009aa2..a9a0039 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipesTools.edit.java +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesTools.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesWeapons.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesWeapons.edit.java index c146c0c..95bc6b9 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipesWeapons.edit.java +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesWeapons.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/crafting/ShapedRecipes.edit.java b/patches/minecraft/net/minecraft/item/crafting/ShapedRecipes.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/item/crafting/ShapedRecipes.edit.java +++ b/patches/minecraft/net/minecraft/item/crafting/ShapedRecipes.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/item/crafting/ShapelessRecipes.edit.java b/patches/minecraft/net/minecraft/item/crafting/ShapelessRecipes.edit.java index a97fcc5..572d2e8 100644 --- a/patches/minecraft/net/minecraft/item/crafting/ShapelessRecipes.edit.java +++ b/patches/minecraft/net/minecraft/item/crafting/ShapelessRecipes.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/nbt/CompressedStreamTools.edit.java b/patches/minecraft/net/minecraft/nbt/CompressedStreamTools.edit.java index e91b1ec..9f6b3fb 100644 --- a/patches/minecraft/net/minecraft/nbt/CompressedStreamTools.edit.java +++ b/patches/minecraft/net/minecraft/nbt/CompressedStreamTools.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/nbt/JsonToNBT.edit.java b/patches/minecraft/net/minecraft/nbt/JsonToNBT.edit.java index 770b089..6d7e753 100644 --- a/patches/minecraft/net/minecraft/nbt/JsonToNBT.edit.java +++ b/patches/minecraft/net/minecraft/nbt/JsonToNBT.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/nbt/NBTBase.edit.java b/patches/minecraft/net/minecraft/nbt/NBTBase.edit.java index ded03e3..389a7e3 100644 --- a/patches/minecraft/net/minecraft/nbt/NBTBase.edit.java +++ b/patches/minecraft/net/minecraft/nbt/NBTBase.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagByte.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagByte.edit.java index 41da09f..86bf348 100644 --- a/patches/minecraft/net/minecraft/nbt/NBTTagByte.edit.java +++ b/patches/minecraft/net/minecraft/nbt/NBTTagByte.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagByteArray.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagByteArray.edit.java index 6422a38..77b3843 100644 --- a/patches/minecraft/net/minecraft/nbt/NBTTagByteArray.edit.java +++ b/patches/minecraft/net/minecraft/nbt/NBTTagByteArray.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagCompound.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagCompound.edit.java index 8cea115..31148d9 100644 --- a/patches/minecraft/net/minecraft/nbt/NBTTagCompound.edit.java +++ b/patches/minecraft/net/minecraft/nbt/NBTTagCompound.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagDouble.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagDouble.edit.java index ccba26d..e92261d 100644 --- a/patches/minecraft/net/minecraft/nbt/NBTTagDouble.edit.java +++ b/patches/minecraft/net/minecraft/nbt/NBTTagDouble.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagEnd.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagEnd.edit.java index 41da09f..86bf348 100644 --- a/patches/minecraft/net/minecraft/nbt/NBTTagEnd.edit.java +++ b/patches/minecraft/net/minecraft/nbt/NBTTagEnd.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagFloat.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagFloat.edit.java index ccba26d..e92261d 100644 --- a/patches/minecraft/net/minecraft/nbt/NBTTagFloat.edit.java +++ b/patches/minecraft/net/minecraft/nbt/NBTTagFloat.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagInt.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagInt.edit.java index 41da09f..86bf348 100644 --- a/patches/minecraft/net/minecraft/nbt/NBTTagInt.edit.java +++ b/patches/minecraft/net/minecraft/nbt/NBTTagInt.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagIntArray.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagIntArray.edit.java index 491599b..0f58f54 100644 --- a/patches/minecraft/net/minecraft/nbt/NBTTagIntArray.edit.java +++ b/patches/minecraft/net/minecraft/nbt/NBTTagIntArray.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagList.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagList.edit.java index af98c60..1ac4651 100644 --- a/patches/minecraft/net/minecraft/nbt/NBTTagList.edit.java +++ b/patches/minecraft/net/minecraft/nbt/NBTTagList.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagLong.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagLong.edit.java index 41da09f..86bf348 100644 --- a/patches/minecraft/net/minecraft/nbt/NBTTagLong.edit.java +++ b/patches/minecraft/net/minecraft/nbt/NBTTagLong.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagShort.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagShort.edit.java index 41da09f..86bf348 100644 --- a/patches/minecraft/net/minecraft/nbt/NBTTagShort.edit.java +++ b/patches/minecraft/net/minecraft/nbt/NBTTagShort.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagString.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagString.edit.java index 41da09f..86bf348 100644 --- a/patches/minecraft/net/minecraft/nbt/NBTTagString.edit.java +++ b/patches/minecraft/net/minecraft/nbt/NBTTagString.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/nbt/NBTUtil.edit.java b/patches/minecraft/net/minecraft/nbt/NBTUtil.edit.java index 3e1b6fb..0014eb6 100644 --- a/patches/minecraft/net/minecraft/nbt/NBTUtil.edit.java +++ b/patches/minecraft/net/minecraft/nbt/NBTUtil.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/EnumConnectionState.edit.java b/patches/minecraft/net/minecraft/network/EnumConnectionState.edit.java index b7537cb..8ae0038 100644 --- a/patches/minecraft/net/minecraft/network/EnumConnectionState.edit.java +++ b/patches/minecraft/net/minecraft/network/EnumConnectionState.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/NetHandlerPlayServer.edit.java b/patches/minecraft/net/minecraft/network/NetHandlerPlayServer.edit.java index 63c6b73..9a7c160 100644 --- a/patches/minecraft/net/minecraft/network/NetHandlerPlayServer.edit.java +++ b/patches/minecraft/net/minecraft/network/NetHandlerPlayServer.edit.java @@ -1,11 +1,16 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> CHANGE 5 : 10 @ 5 : 9 +> INSERT 2 : 4 @ 2 + ++ import com.carrotsearch.hppc.IntShortHashMap; ++ import com.carrotsearch.hppc.IntShortMap; + +> CHANGE 3 : 8 @ 3 : 7 ~ import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol; ~ import net.lax1dude.eaglercraft.v1_8.socket.protocol.client.GameProtocolMessageController; @@ -25,7 +30,9 @@ > DELETE 35 @ 35 : 36 -> INSERT 16 : 18 @ 16 +> DELETE 13 @ 13 : 14 + +> INSERT 2 : 4 @ 2 + import net.lax1dude.eaglercraft.v1_8.sp.server.socket.IntegratedServerPlayerNetworkManager; + @@ -48,7 +55,11 @@ ~ public final IntegratedServerPlayerNetworkManager netManager; ~ public final MinecraftServer serverController; -> INSERT 15 : 17 @ 15 +> CHANGE 10 : 11 @ 10 : 11 + +~ private IntShortMap field_147372_n = new IntShortHashMap(); + +> INSERT 4 : 6 @ 4 + private boolean hasDisconnected = false; + private GameProtocolMessageController eaglerMessageController = null; @@ -161,13 +172,25 @@ > DELETE 4 @ 4 : 5 -> DELETE 41 @ 41 : 42 +> CHANGE 22 : 24 @ 22 : 24 + +~ this.field_147372_n.put(this.playerEntity.openContainer.windowId, +~ c0epacketclickwindow.getActionNumber()); + +> DELETE 17 @ 17 : 18 > DELETE 10 @ 10 : 12 -> DELETE 48 @ 48 : 50 +> CHANGE 48 : 52 @ 48 : 53 -> DELETE 11 @ 11 : 12 +~ int windowId = this.playerEntity.openContainer.windowId; +~ if (this.field_147372_n.containsKey(windowId) +~ && c0fpacketconfirmtransaction.getUid() == this.field_147372_n.get(windowId) +~ && windowId == c0fpacketconfirmtransaction.getWindowId() + +> DELETE 4 @ 4 : 5 + +> DELETE 3 @ 3 : 4 > CHANGE 19 : 25 @ 19 : 21 diff --git a/patches/minecraft/net/minecraft/network/Packet.edit.java b/patches/minecraft/net/minecraft/network/Packet.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/network/Packet.edit.java +++ b/patches/minecraft/net/minecraft/network/Packet.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/PacketBuffer.edit.java b/patches/minecraft/net/minecraft/network/PacketBuffer.edit.java index 080c66d..ee22160 100644 --- a/patches/minecraft/net/minecraft/network/PacketBuffer.edit.java +++ b/patches/minecraft/net/minecraft/network/PacketBuffer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/ServerStatusResponse.edit.java b/patches/minecraft/net/minecraft/network/ServerStatusResponse.edit.java index 762d060..03df055 100644 --- a/patches/minecraft/net/minecraft/network/ServerStatusResponse.edit.java +++ b/patches/minecraft/net/minecraft/network/ServerStatusResponse.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/handshake/client/C00Handshake.edit.java b/patches/minecraft/net/minecraft/network/handshake/client/C00Handshake.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/handshake/client/C00Handshake.edit.java +++ b/patches/minecraft/net/minecraft/network/handshake/client/C00Handshake.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/login/client/C00PacketLoginStart.edit.java b/patches/minecraft/net/minecraft/network/login/client/C00PacketLoginStart.edit.java index f45ef6a..186056b 100644 --- a/patches/minecraft/net/minecraft/network/login/client/C00PacketLoginStart.edit.java +++ b/patches/minecraft/net/minecraft/network/login/client/C00PacketLoginStart.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/login/client/C01PacketEncryptionResponse.edit.java b/patches/minecraft/net/minecraft/network/login/client/C01PacketEncryptionResponse.edit.java index 076066f..0f72c6f 100644 --- a/patches/minecraft/net/minecraft/network/login/client/C01PacketEncryptionResponse.edit.java +++ b/patches/minecraft/net/minecraft/network/login/client/C01PacketEncryptionResponse.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/login/server/S00PacketDisconnect.edit.java b/patches/minecraft/net/minecraft/network/login/server/S00PacketDisconnect.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/login/server/S00PacketDisconnect.edit.java +++ b/patches/minecraft/net/minecraft/network/login/server/S00PacketDisconnect.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/login/server/S01PacketEncryptionRequest.edit.java b/patches/minecraft/net/minecraft/network/login/server/S01PacketEncryptionRequest.edit.java index 2bd0ce9..b0b63a8 100644 --- a/patches/minecraft/net/minecraft/network/login/server/S01PacketEncryptionRequest.edit.java +++ b/patches/minecraft/net/minecraft/network/login/server/S01PacketEncryptionRequest.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/login/server/S02PacketLoginSuccess.edit.java b/patches/minecraft/net/minecraft/network/login/server/S02PacketLoginSuccess.edit.java index 4893bd4..17383d2 100644 --- a/patches/minecraft/net/minecraft/network/login/server/S02PacketLoginSuccess.edit.java +++ b/patches/minecraft/net/minecraft/network/login/server/S02PacketLoginSuccess.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/login/server/S03PacketEnableCompression.edit.java b/patches/minecraft/net/minecraft/network/login/server/S03PacketEnableCompression.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/login/server/S03PacketEnableCompression.edit.java +++ b/patches/minecraft/net/minecraft/network/login/server/S03PacketEnableCompression.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C00PacketKeepAlive.edit.java b/patches/minecraft/net/minecraft/network/play/client/C00PacketKeepAlive.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C00PacketKeepAlive.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C00PacketKeepAlive.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C01PacketChatMessage.edit.java b/patches/minecraft/net/minecraft/network/play/client/C01PacketChatMessage.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C01PacketChatMessage.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C01PacketChatMessage.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C02PacketUseEntity.edit.java b/patches/minecraft/net/minecraft/network/play/client/C02PacketUseEntity.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C02PacketUseEntity.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C02PacketUseEntity.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C03PacketPlayer.edit.java b/patches/minecraft/net/minecraft/network/play/client/C03PacketPlayer.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C03PacketPlayer.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C03PacketPlayer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C07PacketPlayerDigging.edit.java b/patches/minecraft/net/minecraft/network/play/client/C07PacketPlayerDigging.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C07PacketPlayerDigging.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C07PacketPlayerDigging.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C08PacketPlayerBlockPlacement.edit.java b/patches/minecraft/net/minecraft/network/play/client/C08PacketPlayerBlockPlacement.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C08PacketPlayerBlockPlacement.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C08PacketPlayerBlockPlacement.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C09PacketHeldItemChange.edit.java b/patches/minecraft/net/minecraft/network/play/client/C09PacketHeldItemChange.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C09PacketHeldItemChange.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C09PacketHeldItemChange.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C0APacketAnimation.edit.java b/patches/minecraft/net/minecraft/network/play/client/C0APacketAnimation.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C0APacketAnimation.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C0APacketAnimation.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C0BPacketEntityAction.edit.java b/patches/minecraft/net/minecraft/network/play/client/C0BPacketEntityAction.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C0BPacketEntityAction.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C0BPacketEntityAction.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C0CPacketInput.edit.java b/patches/minecraft/net/minecraft/network/play/client/C0CPacketInput.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C0CPacketInput.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C0CPacketInput.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C0DPacketCloseWindow.edit.java b/patches/minecraft/net/minecraft/network/play/client/C0DPacketCloseWindow.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C0DPacketCloseWindow.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C0DPacketCloseWindow.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C0EPacketClickWindow.edit.java b/patches/minecraft/net/minecraft/network/play/client/C0EPacketClickWindow.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C0EPacketClickWindow.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C0EPacketClickWindow.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C0FPacketConfirmTransaction.edit.java b/patches/minecraft/net/minecraft/network/play/client/C0FPacketConfirmTransaction.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C0FPacketConfirmTransaction.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C0FPacketConfirmTransaction.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C10PacketCreativeInventoryAction.edit.java b/patches/minecraft/net/minecraft/network/play/client/C10PacketCreativeInventoryAction.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C10PacketCreativeInventoryAction.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C10PacketCreativeInventoryAction.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C11PacketEnchantItem.edit.java b/patches/minecraft/net/minecraft/network/play/client/C11PacketEnchantItem.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C11PacketEnchantItem.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C11PacketEnchantItem.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C12PacketUpdateSign.edit.java b/patches/minecraft/net/minecraft/network/play/client/C12PacketUpdateSign.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C12PacketUpdateSign.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C12PacketUpdateSign.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C13PacketPlayerAbilities.edit.java b/patches/minecraft/net/minecraft/network/play/client/C13PacketPlayerAbilities.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C13PacketPlayerAbilities.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C13PacketPlayerAbilities.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C14PacketTabComplete.edit.java b/patches/minecraft/net/minecraft/network/play/client/C14PacketTabComplete.edit.java index 92e388a..e8d3161 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C14PacketTabComplete.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C14PacketTabComplete.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C15PacketClientSettings.edit.java b/patches/minecraft/net/minecraft/network/play/client/C15PacketClientSettings.edit.java index 8f6b137..90d4e8f 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C15PacketClientSettings.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C15PacketClientSettings.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C16PacketClientStatus.edit.java b/patches/minecraft/net/minecraft/network/play/client/C16PacketClientStatus.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C16PacketClientStatus.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C16PacketClientStatus.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C17PacketCustomPayload.edit.java b/patches/minecraft/net/minecraft/network/play/client/C17PacketCustomPayload.edit.java index 8cf4611..afa9057 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C17PacketCustomPayload.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C17PacketCustomPayload.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C18PacketSpectate.edit.java b/patches/minecraft/net/minecraft/network/play/client/C18PacketSpectate.edit.java index b3c0e2f..fafce51 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C18PacketSpectate.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C18PacketSpectate.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/client/C19PacketResourcePackStatus.edit.java b/patches/minecraft/net/minecraft/network/play/client/C19PacketResourcePackStatus.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/client/C19PacketResourcePackStatus.edit.java +++ b/patches/minecraft/net/minecraft/network/play/client/C19PacketResourcePackStatus.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S00PacketKeepAlive.edit.java b/patches/minecraft/net/minecraft/network/play/server/S00PacketKeepAlive.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S00PacketKeepAlive.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S00PacketKeepAlive.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S01PacketJoinGame.edit.java b/patches/minecraft/net/minecraft/network/play/server/S01PacketJoinGame.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S01PacketJoinGame.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S01PacketJoinGame.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S02PacketChat.edit.java b/patches/minecraft/net/minecraft/network/play/server/S02PacketChat.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S02PacketChat.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S02PacketChat.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S03PacketTimeUpdate.edit.java b/patches/minecraft/net/minecraft/network/play/server/S03PacketTimeUpdate.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S03PacketTimeUpdate.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S03PacketTimeUpdate.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S04PacketEntityEquipment.edit.java b/patches/minecraft/net/minecraft/network/play/server/S04PacketEntityEquipment.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S04PacketEntityEquipment.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S04PacketEntityEquipment.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S05PacketSpawnPosition.edit.java b/patches/minecraft/net/minecraft/network/play/server/S05PacketSpawnPosition.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S05PacketSpawnPosition.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S05PacketSpawnPosition.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S06PacketUpdateHealth.edit.java b/patches/minecraft/net/minecraft/network/play/server/S06PacketUpdateHealth.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S06PacketUpdateHealth.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S06PacketUpdateHealth.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S07PacketRespawn.edit.java b/patches/minecraft/net/minecraft/network/play/server/S07PacketRespawn.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S07PacketRespawn.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S07PacketRespawn.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S08PacketPlayerPosLook.edit.java b/patches/minecraft/net/minecraft/network/play/server/S08PacketPlayerPosLook.edit.java index 7a7a763..b2ba51c 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S08PacketPlayerPosLook.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S08PacketPlayerPosLook.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S09PacketHeldItemChange.edit.java b/patches/minecraft/net/minecraft/network/play/server/S09PacketHeldItemChange.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S09PacketHeldItemChange.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S09PacketHeldItemChange.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S0APacketUseBed.edit.java b/patches/minecraft/net/minecraft/network/play/server/S0APacketUseBed.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S0APacketUseBed.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S0APacketUseBed.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S0BPacketAnimation.edit.java b/patches/minecraft/net/minecraft/network/play/server/S0BPacketAnimation.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S0BPacketAnimation.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S0BPacketAnimation.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S0CPacketSpawnPlayer.edit.java b/patches/minecraft/net/minecraft/network/play/server/S0CPacketSpawnPlayer.edit.java index 671f361..9121157 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S0CPacketSpawnPlayer.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S0CPacketSpawnPlayer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S0DPacketCollectItem.edit.java b/patches/minecraft/net/minecraft/network/play/server/S0DPacketCollectItem.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S0DPacketCollectItem.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S0DPacketCollectItem.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S0EPacketSpawnObject.edit.java b/patches/minecraft/net/minecraft/network/play/server/S0EPacketSpawnObject.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S0EPacketSpawnObject.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S0EPacketSpawnObject.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S0FPacketSpawnMob.edit.java b/patches/minecraft/net/minecraft/network/play/server/S0FPacketSpawnMob.edit.java index 1307512..0e0f484 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S0FPacketSpawnMob.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S0FPacketSpawnMob.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S10PacketSpawnPainting.edit.java b/patches/minecraft/net/minecraft/network/play/server/S10PacketSpawnPainting.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S10PacketSpawnPainting.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S10PacketSpawnPainting.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S11PacketSpawnExperienceOrb.edit.java b/patches/minecraft/net/minecraft/network/play/server/S11PacketSpawnExperienceOrb.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S11PacketSpawnExperienceOrb.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S11PacketSpawnExperienceOrb.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S12PacketEntityVelocity.edit.java b/patches/minecraft/net/minecraft/network/play/server/S12PacketEntityVelocity.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S12PacketEntityVelocity.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S12PacketEntityVelocity.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S13PacketDestroyEntities.edit.java b/patches/minecraft/net/minecraft/network/play/server/S13PacketDestroyEntities.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S13PacketDestroyEntities.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S13PacketDestroyEntities.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S14PacketEntity.edit.java b/patches/minecraft/net/minecraft/network/play/server/S14PacketEntity.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S14PacketEntity.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S14PacketEntity.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S18PacketEntityTeleport.edit.java b/patches/minecraft/net/minecraft/network/play/server/S18PacketEntityTeleport.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S18PacketEntityTeleport.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S18PacketEntityTeleport.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S19PacketEntityHeadLook.edit.java b/patches/minecraft/net/minecraft/network/play/server/S19PacketEntityHeadLook.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S19PacketEntityHeadLook.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S19PacketEntityHeadLook.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S19PacketEntityStatus.edit.java b/patches/minecraft/net/minecraft/network/play/server/S19PacketEntityStatus.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S19PacketEntityStatus.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S19PacketEntityStatus.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S1BPacketEntityAttach.edit.java b/patches/minecraft/net/minecraft/network/play/server/S1BPacketEntityAttach.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S1BPacketEntityAttach.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S1BPacketEntityAttach.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S1CPacketEntityMetadata.edit.java b/patches/minecraft/net/minecraft/network/play/server/S1CPacketEntityMetadata.edit.java index 1307512..0e0f484 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S1CPacketEntityMetadata.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S1CPacketEntityMetadata.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S1DPacketEntityEffect.edit.java b/patches/minecraft/net/minecraft/network/play/server/S1DPacketEntityEffect.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S1DPacketEntityEffect.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S1DPacketEntityEffect.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S1EPacketRemoveEntityEffect.edit.java b/patches/minecraft/net/minecraft/network/play/server/S1EPacketRemoveEntityEffect.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S1EPacketRemoveEntityEffect.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S1EPacketRemoveEntityEffect.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S1FPacketSetExperience.edit.java b/patches/minecraft/net/minecraft/network/play/server/S1FPacketSetExperience.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S1FPacketSetExperience.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S1FPacketSetExperience.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S20PacketEntityProperties.edit.java b/patches/minecraft/net/minecraft/network/play/server/S20PacketEntityProperties.edit.java index 8f9ea8e..aa021f2 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S20PacketEntityProperties.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S20PacketEntityProperties.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S21PacketChunkData.edit.java b/patches/minecraft/net/minecraft/network/play/server/S21PacketChunkData.edit.java index b6512af..1cee180 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S21PacketChunkData.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S21PacketChunkData.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S22PacketMultiBlockChange.edit.java b/patches/minecraft/net/minecraft/network/play/server/S22PacketMultiBlockChange.edit.java index 78646b0..9dd6478 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S22PacketMultiBlockChange.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S22PacketMultiBlockChange.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S23PacketBlockChange.edit.java b/patches/minecraft/net/minecraft/network/play/server/S23PacketBlockChange.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S23PacketBlockChange.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S23PacketBlockChange.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S24PacketBlockAction.edit.java b/patches/minecraft/net/minecraft/network/play/server/S24PacketBlockAction.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S24PacketBlockAction.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S24PacketBlockAction.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S25PacketBlockBreakAnim.edit.java b/patches/minecraft/net/minecraft/network/play/server/S25PacketBlockBreakAnim.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S25PacketBlockBreakAnim.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S25PacketBlockBreakAnim.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S26PacketMapChunkBulk.edit.java b/patches/minecraft/net/minecraft/network/play/server/S26PacketMapChunkBulk.edit.java index 0079e55..2cfec11 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S26PacketMapChunkBulk.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S26PacketMapChunkBulk.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S27PacketExplosion.edit.java b/patches/minecraft/net/minecraft/network/play/server/S27PacketExplosion.edit.java index 15c4c3d..ef923aa 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S27PacketExplosion.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S27PacketExplosion.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S28PacketEffect.edit.java b/patches/minecraft/net/minecraft/network/play/server/S28PacketEffect.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S28PacketEffect.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S28PacketEffect.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S29PacketSoundEffect.edit.java b/patches/minecraft/net/minecraft/network/play/server/S29PacketSoundEffect.edit.java index eac2e46..0042950 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S29PacketSoundEffect.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S29PacketSoundEffect.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S2APacketParticles.edit.java b/patches/minecraft/net/minecraft/network/play/server/S2APacketParticles.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S2APacketParticles.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S2APacketParticles.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S2BPacketChangeGameState.edit.java b/patches/minecraft/net/minecraft/network/play/server/S2BPacketChangeGameState.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S2BPacketChangeGameState.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S2BPacketChangeGameState.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S2CPacketSpawnGlobalEntity.edit.java b/patches/minecraft/net/minecraft/network/play/server/S2CPacketSpawnGlobalEntity.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S2CPacketSpawnGlobalEntity.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S2CPacketSpawnGlobalEntity.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S2DPacketOpenWindow.edit.java b/patches/minecraft/net/minecraft/network/play/server/S2DPacketOpenWindow.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S2DPacketOpenWindow.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S2DPacketOpenWindow.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S2EPacketCloseWindow.edit.java b/patches/minecraft/net/minecraft/network/play/server/S2EPacketCloseWindow.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S2EPacketCloseWindow.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S2EPacketCloseWindow.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S2FPacketSetSlot.edit.java b/patches/minecraft/net/minecraft/network/play/server/S2FPacketSetSlot.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S2FPacketSetSlot.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S2FPacketSetSlot.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S30PacketWindowItems.edit.java b/patches/minecraft/net/minecraft/network/play/server/S30PacketWindowItems.edit.java index 4ba17a6..62f42cc 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S30PacketWindowItems.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S30PacketWindowItems.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S31PacketWindowProperty.edit.java b/patches/minecraft/net/minecraft/network/play/server/S31PacketWindowProperty.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S31PacketWindowProperty.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S31PacketWindowProperty.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S32PacketConfirmTransaction.edit.java b/patches/minecraft/net/minecraft/network/play/server/S32PacketConfirmTransaction.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S32PacketConfirmTransaction.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S32PacketConfirmTransaction.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S33PacketUpdateSign.edit.java b/patches/minecraft/net/minecraft/network/play/server/S33PacketUpdateSign.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S33PacketUpdateSign.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S33PacketUpdateSign.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S34PacketMaps.edit.java b/patches/minecraft/net/minecraft/network/play/server/S34PacketMaps.edit.java index 4ee8f5c..010a11f 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S34PacketMaps.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S34PacketMaps.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S35PacketUpdateTileEntity.edit.java b/patches/minecraft/net/minecraft/network/play/server/S35PacketUpdateTileEntity.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S35PacketUpdateTileEntity.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S35PacketUpdateTileEntity.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S36PacketSignEditorOpen.edit.java b/patches/minecraft/net/minecraft/network/play/server/S36PacketSignEditorOpen.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S36PacketSignEditorOpen.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S36PacketSignEditorOpen.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S37PacketStatistics.edit.java b/patches/minecraft/net/minecraft/network/play/server/S37PacketStatistics.edit.java index cae4d67..c89f81c 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S37PacketStatistics.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S37PacketStatistics.edit.java @@ -1,16 +1,44 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 3 -> INSERT 3 : 6 @ 3 +> CHANGE 1 : 6 @ 1 : 3 -+ -+ import com.google.common.collect.Maps; -+ +~ +~ import com.carrotsearch.hppc.ObjectIntHashMap; +~ import com.carrotsearch.hppc.ObjectIntMap; +~ import com.carrotsearch.hppc.cursors.ObjectIntCursor; +~ + +> CHANGE 7 : 8 @ 7 : 8 + +~ private ObjectIntMap field_148976_a; + +> CHANGE 4 : 5 @ 4 : 5 + +~ public S37PacketStatistics(ObjectIntMap parMap) { + +> CHANGE 9 : 10 @ 9 : 10 + +~ this.field_148976_a = new ObjectIntHashMap<>(); + +> CHANGE 5 : 6 @ 5 : 6 + +~ this.field_148976_a.put(statbase, k); + +> CHANGE 8 : 11 @ 8 : 11 + +~ for (ObjectIntCursor entry : this.field_148976_a) { +~ parPacketBuffer.writeString(entry.key.statId); +~ parPacketBuffer.writeVarIntToBuffer(entry.value); + +> CHANGE 4 : 5 @ 4 : 5 + +~ public ObjectIntMap func_148974_c() { > EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S38PacketPlayerListItem.edit.java b/patches/minecraft/net/minecraft/network/play/server/S38PacketPlayerListItem.edit.java index d4e3644..09b76c6 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S38PacketPlayerListItem.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S38PacketPlayerListItem.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S39PacketPlayerAbilities.edit.java b/patches/minecraft/net/minecraft/network/play/server/S39PacketPlayerAbilities.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S39PacketPlayerAbilities.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S39PacketPlayerAbilities.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S3APacketTabComplete.edit.java b/patches/minecraft/net/minecraft/network/play/server/S3APacketTabComplete.edit.java index 7ff4850..e078e9c 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S3APacketTabComplete.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S3APacketTabComplete.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S3BPacketScoreboardObjective.edit.java b/patches/minecraft/net/minecraft/network/play/server/S3BPacketScoreboardObjective.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S3BPacketScoreboardObjective.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S3BPacketScoreboardObjective.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S3CPacketUpdateScore.edit.java b/patches/minecraft/net/minecraft/network/play/server/S3CPacketUpdateScore.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S3CPacketUpdateScore.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S3CPacketUpdateScore.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S3DPacketDisplayScoreboard.edit.java b/patches/minecraft/net/minecraft/network/play/server/S3DPacketDisplayScoreboard.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S3DPacketDisplayScoreboard.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S3DPacketDisplayScoreboard.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S3EPacketTeams.edit.java b/patches/minecraft/net/minecraft/network/play/server/S3EPacketTeams.edit.java index 56ec8e8..27abba4 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S3EPacketTeams.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S3EPacketTeams.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S3FPacketCustomPayload.edit.java b/patches/minecraft/net/minecraft/network/play/server/S3FPacketCustomPayload.edit.java index 8cf4611..afa9057 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S3FPacketCustomPayload.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S3FPacketCustomPayload.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S40PacketDisconnect.edit.java b/patches/minecraft/net/minecraft/network/play/server/S40PacketDisconnect.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S40PacketDisconnect.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S40PacketDisconnect.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S41PacketServerDifficulty.edit.java b/patches/minecraft/net/minecraft/network/play/server/S41PacketServerDifficulty.edit.java index 2647d52..99be070 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S41PacketServerDifficulty.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S41PacketServerDifficulty.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S42PacketCombatEvent.edit.java b/patches/minecraft/net/minecraft/network/play/server/S42PacketCombatEvent.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S42PacketCombatEvent.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S42PacketCombatEvent.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S43PacketCamera.edit.java b/patches/minecraft/net/minecraft/network/play/server/S43PacketCamera.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S43PacketCamera.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S43PacketCamera.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S44PacketWorldBorder.edit.java b/patches/minecraft/net/minecraft/network/play/server/S44PacketWorldBorder.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S44PacketWorldBorder.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S44PacketWorldBorder.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S45PacketTitle.edit.java b/patches/minecraft/net/minecraft/network/play/server/S45PacketTitle.edit.java index 55f010c..4a0ad8b 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S45PacketTitle.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S45PacketTitle.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S46PacketSetCompressionLevel.edit.java b/patches/minecraft/net/minecraft/network/play/server/S46PacketSetCompressionLevel.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S46PacketSetCompressionLevel.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S46PacketSetCompressionLevel.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S47PacketPlayerListHeaderFooter.edit.java b/patches/minecraft/net/minecraft/network/play/server/S47PacketPlayerListHeaderFooter.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S47PacketPlayerListHeaderFooter.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S47PacketPlayerListHeaderFooter.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S48PacketResourcePackSend.edit.java b/patches/minecraft/net/minecraft/network/play/server/S48PacketResourcePackSend.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S48PacketResourcePackSend.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S48PacketResourcePackSend.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/network/play/server/S49PacketUpdateEntityNBT.edit.java b/patches/minecraft/net/minecraft/network/play/server/S49PacketUpdateEntityNBT.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/network/play/server/S49PacketUpdateEntityNBT.edit.java +++ b/patches/minecraft/net/minecraft/network/play/server/S49PacketUpdateEntityNBT.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/pathfinding/Path.edit.java b/patches/minecraft/net/minecraft/pathfinding/Path.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/pathfinding/Path.edit.java +++ b/patches/minecraft/net/minecraft/pathfinding/Path.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/pathfinding/PathEntity.edit.java b/patches/minecraft/net/minecraft/pathfinding/PathEntity.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/pathfinding/PathEntity.edit.java +++ b/patches/minecraft/net/minecraft/pathfinding/PathEntity.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/pathfinding/PathFinder.edit.java b/patches/minecraft/net/minecraft/pathfinding/PathFinder.edit.java index 9c2c149..a9d03a6 100644 --- a/patches/minecraft/net/minecraft/pathfinding/PathFinder.edit.java +++ b/patches/minecraft/net/minecraft/pathfinding/PathFinder.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/pathfinding/PathNavigate.edit.java b/patches/minecraft/net/minecraft/pathfinding/PathNavigate.edit.java index f3e387b..0976899 100644 --- a/patches/minecraft/net/minecraft/pathfinding/PathNavigate.edit.java +++ b/patches/minecraft/net/minecraft/pathfinding/PathNavigate.edit.java @@ -1,13 +1,20 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> DELETE 7 @ 7 : 9 +> CHANGE 7 : 8 @ 7 : 9 -> DELETE 45 @ 45 : 46 +~ import net.minecraft.server.MinecraftServer; + +> INSERT 15 : 17 @ 15 + ++ private int lastFailure = 0; ++ private int pathfindFailures = 0; + +> DELETE 30 @ 30 : 31 > DELETE 4 @ 4 : 5 @@ -15,7 +22,29 @@ > DELETE 5 @ 5 : 6 -> CHANGE 57 : 58 @ 57 : 58 +> INSERT 5 : 11 @ 5 + ++ int i = -1; ++ if (this.pathfindFailures > 10 && this.currentPath == null ++ && (i = (int) (MinecraftServer.getCurrentTimeMillis() / 50l)) < this.lastFailure + 40) { ++ return false; ++ } ++ + +> CHANGE 1 : 11 @ 1 : 2 + +~ +~ if (pathentity != null && this.setPath(pathentity, speedIn)) { +~ this.lastFailure = 0; +~ this.pathfindFailures = 0; +~ return true; +~ } else { +~ this.pathfindFailures++; +~ this.lastFailure = i == -1 ? (int) (MinecraftServer.getCurrentTimeMillis() / 50l) : i; +~ return false; +~ } + +> CHANGE 50 : 51 @ 50 : 51 ~ List list = this.worldObj.getCollidingBoundingBoxes(this.theEntity, @@ -24,4 +53,9 @@ ~ for (int i = 0, l = list.size(); i < l; ++i) { ~ AxisAlignedBB axisalignedbb = list.get(i); +> INSERT 61 : 63 @ 61 + ++ this.pathfindFailures = 0; ++ this.lastFailure = 0; + > EOF diff --git a/patches/minecraft/net/minecraft/pathfinding/PathNavigateClimber.edit.java b/patches/minecraft/net/minecraft/pathfinding/PathNavigateClimber.edit.java index a7f89c1..300ea78 100644 --- a/patches/minecraft/net/minecraft/pathfinding/PathNavigateClimber.edit.java +++ b/patches/minecraft/net/minecraft/pathfinding/PathNavigateClimber.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/pathfinding/PathNavigateGround.edit.java b/patches/minecraft/net/minecraft/pathfinding/PathNavigateGround.edit.java index bbc7202..83145c7 100644 --- a/patches/minecraft/net/minecraft/pathfinding/PathNavigateGround.edit.java +++ b/patches/minecraft/net/minecraft/pathfinding/PathNavigateGround.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/pathfinding/PathNavigateSwimmer.edit.java b/patches/minecraft/net/minecraft/pathfinding/PathNavigateSwimmer.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/pathfinding/PathNavigateSwimmer.edit.java +++ b/patches/minecraft/net/minecraft/pathfinding/PathNavigateSwimmer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/potion/Potion.edit.java b/patches/minecraft/net/minecraft/potion/Potion.edit.java index a7314cb..bb81335 100644 --- a/patches/minecraft/net/minecraft/potion/Potion.edit.java +++ b/patches/minecraft/net/minecraft/potion/Potion.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/potion/PotionAbsorption.edit.java b/patches/minecraft/net/minecraft/potion/PotionAbsorption.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/potion/PotionAbsorption.edit.java +++ b/patches/minecraft/net/minecraft/potion/PotionAbsorption.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/potion/PotionAttackDamage.edit.java b/patches/minecraft/net/minecraft/potion/PotionAttackDamage.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/potion/PotionAttackDamage.edit.java +++ b/patches/minecraft/net/minecraft/potion/PotionAttackDamage.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/potion/PotionEffect.edit.java b/patches/minecraft/net/minecraft/potion/PotionEffect.edit.java index 4b1923b..1cc7741 100644 --- a/patches/minecraft/net/minecraft/potion/PotionEffect.edit.java +++ b/patches/minecraft/net/minecraft/potion/PotionEffect.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/potion/PotionHealth.edit.java b/patches/minecraft/net/minecraft/potion/PotionHealth.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/potion/PotionHealth.edit.java +++ b/patches/minecraft/net/minecraft/potion/PotionHealth.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/potion/PotionHealthBoost.edit.java b/patches/minecraft/net/minecraft/potion/PotionHealthBoost.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/potion/PotionHealthBoost.edit.java +++ b/patches/minecraft/net/minecraft/potion/PotionHealthBoost.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/potion/PotionHelper.edit.java b/patches/minecraft/net/minecraft/potion/PotionHelper.edit.java index d21b1f6..0fd8193 100644 --- a/patches/minecraft/net/minecraft/potion/PotionHelper.edit.java +++ b/patches/minecraft/net/minecraft/potion/PotionHelper.edit.java @@ -1,22 +1,110 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 4 -> CHANGE 4 : 8 @ 4 : 6 +> DELETE 3 @ 3 : 7 -~ -~ import com.google.common.collect.Lists; -~ import com.google.common.collect.Maps; -~ +> INSERT 1 : 9 @ 1 -> CHANGE 270 : 272 @ 270 : 271 ++ import com.carrotsearch.hppc.IntIntHashMap; ++ import com.carrotsearch.hppc.IntIntMap; ++ import com.carrotsearch.hppc.IntObjectHashMap; ++ import com.carrotsearch.hppc.IntObjectMap; ++ import com.carrotsearch.hppc.ObjectContainer; ++ import com.carrotsearch.hppc.cursors.ObjectCursor; ++ import com.google.common.collect.Lists; ++ + +> CHANGE 15 : 18 @ 15 : 18 + +~ private static final IntObjectMap potionRequirements = new IntObjectHashMap<>(); +~ private static final IntObjectMap potionAmplifiers = new IntObjectHashMap<>(); +~ private static final IntIntMap DATAVALUE_COLORS = new IntIntHashMap(); + +> INSERT 60 : 95 @ 60 + ++ public static int calcPotionLiquidColor(ObjectContainer parCollection) { ++ int i = 3694022; ++ if (parCollection != null && !parCollection.isEmpty()) { ++ float f = 0.0F; ++ float f1 = 0.0F; ++ float f2 = 0.0F; ++ float f3 = 0.0F; ++ ++ for (ObjectCursor potioneffect_ : parCollection) { ++ PotionEffect potioneffect = potioneffect_.value; ++ if (potioneffect.getIsShowParticles()) { ++ int j = Potion.potionTypes[potioneffect.getPotionID()].getLiquidColor(); ++ ++ for (int k = 0; k <= potioneffect.getAmplifier(); ++k) { ++ f += (float) (j >> 16 & 255) / 255.0F; ++ f1 += (float) (j >> 8 & 255) / 255.0F; ++ f2 += (float) (j >> 0 & 255) / 255.0F; ++ ++f3; ++ } ++ } ++ } ++ ++ if (f3 == 0.0F) { ++ return 0; ++ } else { ++ f = f / f3 * 255.0F; ++ f1 = f1 / f3 * 255.0F; ++ f2 = f2 / f3 * 255.0F; ++ return (int) f << 16 | (int) f1 << 8 | (int) f2; ++ } ++ } else { ++ return i; ++ } ++ } ++ + +> INSERT 10 : 20 @ 10 + ++ public static boolean getAreAmbient(ObjectContainer potionEffects) { ++ for (ObjectCursor potioneffect : potionEffects) { ++ if (!potioneffect.value.getIsAmbient()) { ++ return false; ++ } ++ } ++ ++ return true; ++ } ++ + +> DELETE 1 @ 1 : 2 + +> CHANGE 1 : 4 @ 1 : 3 + +~ int i = DATAVALUE_COLORS.getOrDefault(dataValue, -1); +~ if (i != -1) { +~ return i; + +> CHANGE 1 : 3 @ 1 : 3 + +~ i = calcPotionLiquidColor(getPotionEffects(dataValue, false)); +~ DATAVALUE_COLORS.put(dataValue, i); + +> CHANGE 3 : 4 @ 3 : 4 + +~ return calcPotionLiquidColor(getPotionEffects(dataValue, true)); + +> CHANGE 168 : 170 @ 168 : 169 ~ for (int k = 0; k < Potion.potionTypes.length; ++k) { ~ Potion potion = Potion.potionTypes[k]; +> CHANGE 1 : 2 @ 1 : 2 + +~ String s = potionRequirements.get(potion.getId()); + +> CHANGE 4 : 5 @ 4 : 5 + +~ String s1 = potionAmplifiers.get(potion.getId()); + > EOF diff --git a/patches/minecraft/net/minecraft/scoreboard/GoalColor.edit.java b/patches/minecraft/net/minecraft/scoreboard/GoalColor.edit.java index 4d0a732..8c9d894 100644 --- a/patches/minecraft/net/minecraft/scoreboard/GoalColor.edit.java +++ b/patches/minecraft/net/minecraft/scoreboard/GoalColor.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/scoreboard/IScoreObjectiveCriteria.edit.java b/patches/minecraft/net/minecraft/scoreboard/IScoreObjectiveCriteria.edit.java index 7bf453f..283a46d 100644 --- a/patches/minecraft/net/minecraft/scoreboard/IScoreObjectiveCriteria.edit.java +++ b/patches/minecraft/net/minecraft/scoreboard/IScoreObjectiveCriteria.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/scoreboard/Score.edit.java b/patches/minecraft/net/minecraft/scoreboard/Score.edit.java index 75e74f6..f582943 100644 --- a/patches/minecraft/net/minecraft/scoreboard/Score.edit.java +++ b/patches/minecraft/net/minecraft/scoreboard/Score.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/scoreboard/ScoreDummyCriteria.edit.java b/patches/minecraft/net/minecraft/scoreboard/ScoreDummyCriteria.edit.java index 4d0a732..8c9d894 100644 --- a/patches/minecraft/net/minecraft/scoreboard/ScoreDummyCriteria.edit.java +++ b/patches/minecraft/net/minecraft/scoreboard/ScoreDummyCriteria.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/scoreboard/ScoreHealthCriteria.edit.java b/patches/minecraft/net/minecraft/scoreboard/ScoreHealthCriteria.edit.java index 8448ec4..5ebda13 100644 --- a/patches/minecraft/net/minecraft/scoreboard/ScoreHealthCriteria.edit.java +++ b/patches/minecraft/net/minecraft/scoreboard/ScoreHealthCriteria.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/scoreboard/ScoreObjective.edit.java b/patches/minecraft/net/minecraft/scoreboard/ScoreObjective.edit.java index 791e0c6..1c1b9a8 100644 --- a/patches/minecraft/net/minecraft/scoreboard/ScoreObjective.edit.java +++ b/patches/minecraft/net/minecraft/scoreboard/ScoreObjective.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/scoreboard/ScorePlayerTeam.edit.java b/patches/minecraft/net/minecraft/scoreboard/ScorePlayerTeam.edit.java index 6771c4a..8f0bc2d 100644 --- a/patches/minecraft/net/minecraft/scoreboard/ScorePlayerTeam.edit.java +++ b/patches/minecraft/net/minecraft/scoreboard/ScorePlayerTeam.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/scoreboard/Scoreboard.edit.java b/patches/minecraft/net/minecraft/scoreboard/Scoreboard.edit.java index f5af795..4304fc2 100644 --- a/patches/minecraft/net/minecraft/scoreboard/Scoreboard.edit.java +++ b/patches/minecraft/net/minecraft/scoreboard/Scoreboard.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/scoreboard/ScoreboardSaveData.edit.java b/patches/minecraft/net/minecraft/scoreboard/ScoreboardSaveData.edit.java index 8843b11..b854418 100644 --- a/patches/minecraft/net/minecraft/scoreboard/ScoreboardSaveData.edit.java +++ b/patches/minecraft/net/minecraft/scoreboard/ScoreboardSaveData.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/scoreboard/ServerScoreboard.edit.java b/patches/minecraft/net/minecraft/scoreboard/ServerScoreboard.edit.java index ceaacce..54ef890 100644 --- a/patches/minecraft/net/minecraft/scoreboard/ServerScoreboard.edit.java +++ b/patches/minecraft/net/minecraft/scoreboard/ServerScoreboard.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/scoreboard/Team.edit.java b/patches/minecraft/net/minecraft/scoreboard/Team.edit.java index 48178f1..ea153b3 100644 --- a/patches/minecraft/net/minecraft/scoreboard/Team.edit.java +++ b/patches/minecraft/net/minecraft/scoreboard/Team.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/server/MinecraftServer.edit.java b/patches/minecraft/net/minecraft/server/MinecraftServer.edit.java index 6244d53..eeb3889 100644 --- a/patches/minecraft/net/minecraft/server/MinecraftServer.edit.java +++ b/patches/minecraft/net/minecraft/server/MinecraftServer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/server/management/LowerStringMap.edit.java b/patches/minecraft/net/minecraft/server/management/LowerStringMap.edit.java index 56b0e66..53ac0c1 100644 --- a/patches/minecraft/net/minecraft/server/management/LowerStringMap.edit.java +++ b/patches/minecraft/net/minecraft/server/management/LowerStringMap.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/server/management/PlayerManager.edit.java b/patches/minecraft/net/minecraft/server/management/PlayerManager.edit.java index 98c95bc..c67a8c4 100644 --- a/patches/minecraft/net/minecraft/server/management/PlayerManager.edit.java +++ b/patches/minecraft/net/minecraft/server/management/PlayerManager.edit.java @@ -1,19 +1,86 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> CHANGE 18 : 20 @ 18 : 20 +> INSERT 2 : 6 @ 2 + ++ import com.carrotsearch.hppc.LongHashSet; ++ import com.carrotsearch.hppc.LongObjectHashMap; ++ import com.carrotsearch.hppc.LongObjectMap; ++ import com.carrotsearch.hppc.LongSet; + +> DELETE 1 @ 1 : 2 + +> DELETE 8 @ 8 : 9 + +> CHANGE 5 : 7 @ 5 : 7 ~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -> CHANGE 206 : 209 @ 206 : 207 +> CHANGE 5 : 6 @ 5 : 6 + +~ private final LongObjectMap playerInstances = new LongObjectHashMap<>(); + +> CHANGE 46 : 47 @ 46 : 47 + +~ return this.playerInstances.get(i) != null; + +> CHANGE 4 : 5 @ 4 : 6 + +~ PlayerManager.PlayerInstance playermanager$playerinstance = this.playerInstances.get(i); + +> CHANGE 2 : 3 @ 2 : 3 + +~ this.playerInstances.put(i, playermanager$playerinstance); + +> CHANGE 33 : 34 @ 33 : 34 + +~ LongSet arraylist = new LongHashSet(player.loadedChunks); + +> CHANGE 6 : 7 @ 6 : 7 + +~ long chunkcoordintpair = this.getPlayerInstance(k, l, true).chunkCoordsHash; + +> CHANGE 12 : 13 @ 12 : 13 + +~ chunkcoordintpair = this.getPlayerInstance(k + i1, l + j1, true).chunkCoordsHash; + +> CHANGE 12 : 13 @ 12 : 13 + +~ chunkcoordintpair = this.getPlayerInstance(k + i1, l + j1, true).chunkCoordsHash; + +> CHANGE 69 : 70 @ 69 : 70 + +~ && !player.loadedChunks.contains(playermanager$playerinstance.chunkCoordsHash); + +> CHANGE 7 : 10 @ 7 : 8 ~ List playerz = Lists.newArrayList(this.players); ~ for (int m = 0, n = playerz.size(); m < n; ++m) { ~ EntityPlayerMP entityplayermp = playerz.get(m); +> INSERT 34 : 35 @ 34 + ++ private final long chunkCoordsHash; + +> INSERT 7 : 8 @ 7 + ++ this.chunkCoordsHash = ChunkCoordIntPair.chunkXZ2Int(chunkX, chunkZ); + +> CHANGE 14 : 15 @ 14 : 15 + +~ player.loadedChunks.add(this.chunkCoordsHash); + +> CHANGE 12 : 13 @ 12 : 13 + +~ player.loadedChunks.removeAll(this.chunkCoordsHash); + +> CHANGE 51 : 52 @ 51 : 52 + +~ if (!entityplayermp.loadedChunks.contains(this.chunkCoordsHash)) { + > EOF diff --git a/patches/minecraft/net/minecraft/server/management/ServerConfigurationManager.edit.java b/patches/minecraft/net/minecraft/server/management/ServerConfigurationManager.edit.java index 95bed2d..779ac88 100644 --- a/patches/minecraft/net/minecraft/server/management/ServerConfigurationManager.edit.java +++ b/patches/minecraft/net/minecraft/server/management/ServerConfigurationManager.edit.java @@ -1,11 +1,15 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> CHANGE 5 : 8 @ 5 : 9 +> INSERT 2 : 3 @ 2 + ++ import com.carrotsearch.hppc.cursors.ObjectCursor; + +> CHANGE 3 : 6 @ 3 : 7 ~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; ~ import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; @@ -104,7 +108,12 @@ + playerIn.addChatMessage(shaderF4Msg); + } -> INSERT 23 : 31 @ 23 +> CHANGE 8 : 10 @ 8 : 10 + +~ for (ObjectCursor potioneffect : playerIn.getActivePotionEffects()) { +~ nethandlerplayserver.sendPacket(new S1DPacketEntityEffect(playerIn.getEntityId(), potioneffect.value)); + +> INSERT 13 : 21 @ 13 + if (EagRuntime.getConfiguration().allowUpdateSvc()) { + for (int i = 0, l = playerEntityList.size(); i < l; ++i) { @@ -190,7 +199,13 @@ + entityplayermp.updateCertificate = playerIn.updateCertificate; + entityplayermp.clientBrandUUID = playerIn.clientBrandUUID; -> DELETE 75 @ 75 : 76 +> CHANGE 63 : 66 @ 63 : 65 + +~ for (ObjectCursor potioneffect : playerIn.getActivePotionEffects()) { +~ playerIn.playerNetServerHandler +~ .sendPacket(new S1DPacketEntityEffect(playerIn.getEntityId(), potioneffect.value)); + +> DELETE 10 @ 10 : 11 > DELETE 35 @ 35 : 36 @@ -270,13 +285,21 @@ ~ this.playerStatFiles.put(name, statisticsfile); -> CHANGE 8 : 11 @ 8 : 9 +> INSERT 7 : 8 @ 7 + ++ int entityViewDist = getEntityViewDistance(); + +> CHANGE 1 : 4 @ 1 : 2 ~ WorldServer[] srv = this.mcServer.worldServers; ~ for (int i = 0; i < srv.length; ++i) { ~ WorldServer worldserver = srv[i]; -> DELETE 4 @ 4 : 5 +> INSERT 2 : 3 @ 2 + ++ worldserver.getEntityTracker().updateMaxTrackingThreshold(entityViewDist); + +> DELETE 2 @ 2 : 3 > CHANGE 7 : 8 @ 7 : 8 diff --git a/patches/minecraft/net/minecraft/server/network/NetHandlerLoginServer.edit.java b/patches/minecraft/net/minecraft/server/network/NetHandlerLoginServer.edit.java index 3570a71..7601a84 100644 --- a/patches/minecraft/net/minecraft/server/network/NetHandlerLoginServer.edit.java +++ b/patches/minecraft/net/minecraft/server/network/NetHandlerLoginServer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/stats/Achievement.edit.java b/patches/minecraft/net/minecraft/stats/Achievement.edit.java index f320f8c..89b077e 100644 --- a/patches/minecraft/net/minecraft/stats/Achievement.edit.java +++ b/patches/minecraft/net/minecraft/stats/Achievement.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/stats/AchievementList.edit.java b/patches/minecraft/net/minecraft/stats/AchievementList.edit.java index 6bfad4d..2021418 100644 --- a/patches/minecraft/net/minecraft/stats/AchievementList.edit.java +++ b/patches/minecraft/net/minecraft/stats/AchievementList.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/stats/ObjectiveStat.edit.java b/patches/minecraft/net/minecraft/stats/ObjectiveStat.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/stats/ObjectiveStat.edit.java +++ b/patches/minecraft/net/minecraft/stats/ObjectiveStat.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/stats/StatBase.edit.java b/patches/minecraft/net/minecraft/stats/StatBase.edit.java index 969e1aa..189921c 100644 --- a/patches/minecraft/net/minecraft/stats/StatBase.edit.java +++ b/patches/minecraft/net/minecraft/stats/StatBase.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/stats/StatBasic.edit.java b/patches/minecraft/net/minecraft/stats/StatBasic.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/stats/StatBasic.edit.java +++ b/patches/minecraft/net/minecraft/stats/StatBasic.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/stats/StatCrafting.edit.java b/patches/minecraft/net/minecraft/stats/StatCrafting.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/stats/StatCrafting.edit.java +++ b/patches/minecraft/net/minecraft/stats/StatCrafting.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/stats/StatFileWriter.edit.java b/patches/minecraft/net/minecraft/stats/StatFileWriter.edit.java index e45fc10..ed5547d 100644 --- a/patches/minecraft/net/minecraft/stats/StatFileWriter.edit.java +++ b/patches/minecraft/net/minecraft/stats/StatFileWriter.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/stats/StatList.edit.java b/patches/minecraft/net/minecraft/stats/StatList.edit.java index c3d7f70..475d3c1 100644 --- a/patches/minecraft/net/minecraft/stats/StatList.edit.java +++ b/patches/minecraft/net/minecraft/stats/StatList.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/stats/StatisticsFile.edit.java b/patches/minecraft/net/minecraft/stats/StatisticsFile.edit.java index 9457120..2ba04f0 100644 --- a/patches/minecraft/net/minecraft/stats/StatisticsFile.edit.java +++ b/patches/minecraft/net/minecraft/stats/StatisticsFile.edit.java @@ -1,11 +1,16 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> DELETE 4 @ 4 : 11 +> INSERT 2 : 4 @ 2 + ++ import com.carrotsearch.hppc.ObjectIntHashMap; ++ import com.carrotsearch.hppc.ObjectIntMap; + +> DELETE 2 @ 2 : 9 > DELETE 9 @ 9 : 14 @@ -93,9 +98,25 @@ ~ jsonobject.put(((StatBase) entry.getKey()).statId, -> CHANGE 31 : 33 @ 31 : 32 +> CHANGE 16 : 17 @ 16 : 17 + +~ ObjectIntMap hashmap = new ObjectIntHashMap<>(); + +> CHANGE 4 : 5 @ 4 : 5 + +~ hashmap.put(statbase, this.readStat(statbase)); + +> CHANGE 7 : 8 @ 7 : 8 + +~ ObjectIntMap hashmap = new ObjectIntHashMap<>(); + +> CHANGE 1 : 3 @ 1 : 2 ~ for (int i = 0, l = AchievementList.achievementList.size(); i < l; ++i) { ~ Achievement achievement = AchievementList.achievementList.get(i); +> CHANGE 1 : 2 @ 1 : 2 + +~ hashmap.put(achievement, this.readStat(achievement)); + > EOF diff --git a/patches/minecraft/net/minecraft/tileentity/MobSpawnerBaseLogic.edit.java b/patches/minecraft/net/minecraft/tileentity/MobSpawnerBaseLogic.edit.java index 91b59f0..e450aa2 100644 --- a/patches/minecraft/net/minecraft/tileentity/MobSpawnerBaseLogic.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/MobSpawnerBaseLogic.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntity.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntity.edit.java index 56d5576..0208a25 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntity.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntity.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityBanner.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityBanner.edit.java index b3990b8..ccad597 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityBanner.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityBanner.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityBeacon.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityBeacon.edit.java index c2ed709..6c9533f 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityBeacon.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityBeacon.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityBrewingStand.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityBrewingStand.edit.java index a66a645..8a029af 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityBrewingStand.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityBrewingStand.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityChest.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityChest.edit.java index 9e9ff2e..476cf7a 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityChest.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityChest.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityCommandBlock.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityCommandBlock.edit.java index 501a633..062e346 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityCommandBlock.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityCommandBlock.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityComparator.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityComparator.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityComparator.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityComparator.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityDaylightDetector.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityDaylightDetector.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityDaylightDetector.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityDaylightDetector.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityDispenser.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityDispenser.edit.java index 1e8b282..395fdac 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityDispenser.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityDispenser.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityDropper.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityDropper.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityDropper.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityDropper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityEnchantmentTable.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityEnchantmentTable.edit.java index a0adc14..f4ce0d1 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityEnchantmentTable.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityEnchantmentTable.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityEndPortal.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityEndPortal.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityEndPortal.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityEndPortal.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityEnderChest.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityEnderChest.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityEnderChest.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityEnderChest.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityFlowerPot.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityFlowerPot.edit.java index 97688dc..8ad33b5 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityFlowerPot.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityFlowerPot.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityFurnace.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityFurnace.edit.java index 51ecb45..9231b8a 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityFurnace.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityFurnace.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityHopper.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityHopper.edit.java index e82004a..e5bdfd1 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityHopper.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityHopper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityLockable.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityLockable.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityLockable.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityLockable.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityMobSpawner.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityMobSpawner.edit.java index 6422a38..77b3843 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityMobSpawner.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityMobSpawner.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityNote.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityNote.edit.java index be6673d..bcd4bdc 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityNote.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityNote.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityPiston.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityPiston.edit.java index ff226ef..626a9b3 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntityPiston.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityPiston.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntitySign.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntitySign.edit.java index 1605d22..7555418 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntitySign.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntitySign.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntitySkull.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntitySkull.edit.java index caeefe0..8235750 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntitySkull.edit.java +++ b/patches/minecraft/net/minecraft/tileentity/TileEntitySkull.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/AxisAlignedBB.edit.java b/patches/minecraft/net/minecraft/util/AxisAlignedBB.edit.java index fc4c050..1c57f4a 100644 --- a/patches/minecraft/net/minecraft/util/AxisAlignedBB.edit.java +++ b/patches/minecraft/net/minecraft/util/AxisAlignedBB.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/BlockPos.edit.java b/patches/minecraft/net/minecraft/util/BlockPos.edit.java index 6f0d53f..0b0ddaf 100644 --- a/patches/minecraft/net/minecraft/util/BlockPos.edit.java +++ b/patches/minecraft/net/minecraft/util/BlockPos.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/Cartesian.edit.java b/patches/minecraft/net/minecraft/util/Cartesian.edit.java index e7fc3b9..3daf9f8 100644 --- a/patches/minecraft/net/minecraft/util/Cartesian.edit.java +++ b/patches/minecraft/net/minecraft/util/Cartesian.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/ChatAllowedCharacters.edit.java b/patches/minecraft/net/minecraft/util/ChatAllowedCharacters.edit.java index 84633fe..f80f2cd 100644 --- a/patches/minecraft/net/minecraft/util/ChatAllowedCharacters.edit.java +++ b/patches/minecraft/net/minecraft/util/ChatAllowedCharacters.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/ChatComponentProcessor.edit.java b/patches/minecraft/net/minecraft/util/ChatComponentProcessor.edit.java index 44cd8dc..cf7fec5 100644 --- a/patches/minecraft/net/minecraft/util/ChatComponentProcessor.edit.java +++ b/patches/minecraft/net/minecraft/util/ChatComponentProcessor.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/ChatComponentScore.edit.java b/patches/minecraft/net/minecraft/util/ChatComponentScore.edit.java index ef88104..3d90144 100644 --- a/patches/minecraft/net/minecraft/util/ChatComponentScore.edit.java +++ b/patches/minecraft/net/minecraft/util/ChatComponentScore.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/ChatComponentSelector.edit.java b/patches/minecraft/net/minecraft/util/ChatComponentSelector.edit.java index fba8f3c..e07b89d 100644 --- a/patches/minecraft/net/minecraft/util/ChatComponentSelector.edit.java +++ b/patches/minecraft/net/minecraft/util/ChatComponentSelector.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/ChatComponentStyle.edit.java b/patches/minecraft/net/minecraft/util/ChatComponentStyle.edit.java index 41e7323..92a7888 100644 --- a/patches/minecraft/net/minecraft/util/ChatComponentStyle.edit.java +++ b/patches/minecraft/net/minecraft/util/ChatComponentStyle.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/ChatComponentText.edit.java b/patches/minecraft/net/minecraft/util/ChatComponentText.edit.java index b87b087..83d652c 100644 --- a/patches/minecraft/net/minecraft/util/ChatComponentText.edit.java +++ b/patches/minecraft/net/minecraft/util/ChatComponentText.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/ChatComponentTranslation.edit.java b/patches/minecraft/net/minecraft/util/ChatComponentTranslation.edit.java index b07634b..512b278 100644 --- a/patches/minecraft/net/minecraft/util/ChatComponentTranslation.edit.java +++ b/patches/minecraft/net/minecraft/util/ChatComponentTranslation.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/ChatComponentTranslationFormatException.edit.java b/patches/minecraft/net/minecraft/util/ChatComponentTranslationFormatException.edit.java index bf67108..a4444ef 100644 --- a/patches/minecraft/net/minecraft/util/ChatComponentTranslationFormatException.edit.java +++ b/patches/minecraft/net/minecraft/util/ChatComponentTranslationFormatException.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/ChatStyle.edit.java b/patches/minecraft/net/minecraft/util/ChatStyle.edit.java index d4a9a95..8940a35 100644 --- a/patches/minecraft/net/minecraft/util/ChatStyle.edit.java +++ b/patches/minecraft/net/minecraft/util/ChatStyle.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/ClassInheritanceMultiMap.edit.java b/patches/minecraft/net/minecraft/util/ClassInheritanceMultiMap.edit.java index 84bd861..286993b 100644 --- a/patches/minecraft/net/minecraft/util/ClassInheritanceMultiMap.edit.java +++ b/patches/minecraft/net/minecraft/util/ClassInheritanceMultiMap.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/CombatEntry.edit.java b/patches/minecraft/net/minecraft/util/CombatEntry.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/util/CombatEntry.edit.java +++ b/patches/minecraft/net/minecraft/util/CombatEntry.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/CombatTracker.edit.java b/patches/minecraft/net/minecraft/util/CombatTracker.edit.java index 5010667..616920d 100644 --- a/patches/minecraft/net/minecraft/util/CombatTracker.edit.java +++ b/patches/minecraft/net/minecraft/util/CombatTracker.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/DamageSource.edit.java b/patches/minecraft/net/minecraft/util/DamageSource.edit.java index 8e16278..df70ae7 100644 --- a/patches/minecraft/net/minecraft/util/DamageSource.edit.java +++ b/patches/minecraft/net/minecraft/util/DamageSource.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/EnchantmentNameParts.edit.java b/patches/minecraft/net/minecraft/util/EnchantmentNameParts.edit.java index 3a2b8e7..d4666a8 100644 --- a/patches/minecraft/net/minecraft/util/EnchantmentNameParts.edit.java +++ b/patches/minecraft/net/minecraft/util/EnchantmentNameParts.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/EntityDamageSource.edit.java b/patches/minecraft/net/minecraft/util/EntityDamageSource.edit.java index dd356a0..24f8fbd 100644 --- a/patches/minecraft/net/minecraft/util/EntityDamageSource.edit.java +++ b/patches/minecraft/net/minecraft/util/EntityDamageSource.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/EntityDamageSourceIndirect.edit.java b/patches/minecraft/net/minecraft/util/EntityDamageSourceIndirect.edit.java index b298ca7..ad4e1b8 100644 --- a/patches/minecraft/net/minecraft/util/EntityDamageSourceIndirect.edit.java +++ b/patches/minecraft/net/minecraft/util/EntityDamageSourceIndirect.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/EntitySelectors.edit.java b/patches/minecraft/net/minecraft/util/EntitySelectors.edit.java index 6d88f89..49a4784 100644 --- a/patches/minecraft/net/minecraft/util/EntitySelectors.edit.java +++ b/patches/minecraft/net/minecraft/util/EntitySelectors.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/EnumChatFormatting.edit.java b/patches/minecraft/net/minecraft/util/EnumChatFormatting.edit.java index 98eae41..4a3b042 100644 --- a/patches/minecraft/net/minecraft/util/EnumChatFormatting.edit.java +++ b/patches/minecraft/net/minecraft/util/EnumChatFormatting.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/EnumFacing.edit.java b/patches/minecraft/net/minecraft/util/EnumFacing.edit.java index 7b3e917..3e4fc29 100644 --- a/patches/minecraft/net/minecraft/util/EnumFacing.edit.java +++ b/patches/minecraft/net/minecraft/util/EnumFacing.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/EnumParticleTypes.edit.java b/patches/minecraft/net/minecraft/util/EnumParticleTypes.edit.java index 8f8afeb..0ecfef0 100644 --- a/patches/minecraft/net/minecraft/util/EnumParticleTypes.edit.java +++ b/patches/minecraft/net/minecraft/util/EnumParticleTypes.edit.java @@ -1,16 +1,19 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 2 @ 2 : 4 -> INSERT 3 : 6 @ 3 +> DELETE 1 @ 1 : 2 +> INSERT 1 : 5 @ 1 + ++ import com.carrotsearch.hppc.IntObjectHashMap; ++ import com.carrotsearch.hppc.IntObjectMap; + import com.google.common.collect.Lists; -+ import com.google.common.collect.Maps; + > INSERT 17 : 19 @ 17 @@ -18,7 +21,15 @@ + public static final EnumParticleTypes[] _VALUES = values(); + -> CHANGE 49 : 52 @ 49 : 50 +> CHANGE 4 : 5 @ 4 : 5 + +~ private static final IntObjectMap PARTICLES = new IntObjectHashMap<>(); + +> CHANGE 38 : 39 @ 38 : 39 + +~ return PARTICLES.get(Integer.valueOf(particleId)); + +> CHANGE 5 : 8 @ 5 : 6 ~ EnumParticleTypes[] types = values(); ~ for (int i = 0; i < types.length; ++i) { diff --git a/patches/minecraft/net/minecraft/util/EnumWorldBlockLayer.edit.java b/patches/minecraft/net/minecraft/util/EnumWorldBlockLayer.edit.java index 2d4fc70..d92394a 100644 --- a/patches/minecraft/net/minecraft/util/EnumWorldBlockLayer.edit.java +++ b/patches/minecraft/net/minecraft/util/EnumWorldBlockLayer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/FoodStats.edit.java b/patches/minecraft/net/minecraft/util/FoodStats.edit.java index 97688dc..8ad33b5 100644 --- a/patches/minecraft/net/minecraft/util/FoodStats.edit.java +++ b/patches/minecraft/net/minecraft/util/FoodStats.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/IChatComponent.edit.java b/patches/minecraft/net/minecraft/util/IChatComponent.edit.java index 5809f25..d8f0ade 100644 --- a/patches/minecraft/net/minecraft/util/IChatComponent.edit.java +++ b/patches/minecraft/net/minecraft/util/IChatComponent.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/IJsonSerializable.edit.java b/patches/minecraft/net/minecraft/util/IJsonSerializable.edit.java index 4616e96..32395e3 100644 --- a/patches/minecraft/net/minecraft/util/IJsonSerializable.edit.java +++ b/patches/minecraft/net/minecraft/util/IJsonSerializable.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/IThreadListener.edit.java b/patches/minecraft/net/minecraft/util/IThreadListener.edit.java index 49b3e73..4e7eac6 100644 --- a/patches/minecraft/net/minecraft/util/IThreadListener.edit.java +++ b/patches/minecraft/net/minecraft/util/IThreadListener.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/IntHashMap.edit.java b/patches/minecraft/net/minecraft/util/IntHashMap.edit.java deleted file mode 100644 index b7c292b..0000000 --- a/patches/minecraft/net/minecraft/util/IntHashMap.edit.java +++ /dev/null @@ -1,12 +0,0 @@ - -# Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. - -# Version: 1.0 -# Author: lax1dude - -> CHANGE 23 : 24 @ 23 : 24 - -~ return (V) inthashmap$entry.valueEntry; - -> EOF diff --git a/patches/minecraft/net/minecraft/util/JsonSerializableSet.edit.java b/patches/minecraft/net/minecraft/util/JsonSerializableSet.edit.java index 6773a64..7662aa9 100644 --- a/patches/minecraft/net/minecraft/util/JsonSerializableSet.edit.java +++ b/patches/minecraft/net/minecraft/util/JsonSerializableSet.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/LoggingPrintStream.edit.java b/patches/minecraft/net/minecraft/util/LoggingPrintStream.edit.java index 8ee26e1..341653d 100644 --- a/patches/minecraft/net/minecraft/util/LoggingPrintStream.edit.java +++ b/patches/minecraft/net/minecraft/util/LoggingPrintStream.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/LongHashMap.edit.java b/patches/minecraft/net/minecraft/util/LongHashMap.edit.java deleted file mode 100644 index b2fee40..0000000 --- a/patches/minecraft/net/minecraft/util/LongHashMap.edit.java +++ /dev/null @@ -1,12 +0,0 @@ - -# Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. - -# Version: 1.0 -# Author: lax1dude - -> CHANGE 37 : 38 @ 37 : 38 - -~ return (V) longhashmap$entry.value; - -> EOF diff --git a/patches/minecraft/net/minecraft/util/MapPopulator.edit.java b/patches/minecraft/net/minecraft/util/MapPopulator.edit.java index f558ca4..ac06a1b 100644 --- a/patches/minecraft/net/minecraft/util/MapPopulator.edit.java +++ b/patches/minecraft/net/minecraft/util/MapPopulator.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/MathHelper.edit.java b/patches/minecraft/net/minecraft/util/MathHelper.edit.java index f132000..f22013c 100644 --- a/patches/minecraft/net/minecraft/util/MathHelper.edit.java +++ b/patches/minecraft/net/minecraft/util/MathHelper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/Matrix4f.edit.java b/patches/minecraft/net/minecraft/util/Matrix4f.edit.java index fb521aa..bec119c 100644 --- a/patches/minecraft/net/minecraft/util/Matrix4f.edit.java +++ b/patches/minecraft/net/minecraft/util/Matrix4f.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/MouseHelper.edit.java b/patches/minecraft/net/minecraft/util/MouseHelper.edit.java index 38ec949..d24f969 100644 --- a/patches/minecraft/net/minecraft/util/MouseHelper.edit.java +++ b/patches/minecraft/net/minecraft/util/MouseHelper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/MovementInputFromOptions.edit.java b/patches/minecraft/net/minecraft/util/MovementInputFromOptions.edit.java index 20acaad..7ef9a72 100644 --- a/patches/minecraft/net/minecraft/util/MovementInputFromOptions.edit.java +++ b/patches/minecraft/net/minecraft/util/MovementInputFromOptions.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/MovingObjectPosition.edit.java b/patches/minecraft/net/minecraft/util/MovingObjectPosition.edit.java index 9c2c149..a9d03a6 100644 --- a/patches/minecraft/net/minecraft/util/MovingObjectPosition.edit.java +++ b/patches/minecraft/net/minecraft/util/MovingObjectPosition.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/ObjectIntIdentityMap.edit.java b/patches/minecraft/net/minecraft/util/ObjectIntIdentityMap.edit.java index d158072..737582e 100644 --- a/patches/minecraft/net/minecraft/util/ObjectIntIdentityMap.edit.java +++ b/patches/minecraft/net/minecraft/util/ObjectIntIdentityMap.edit.java @@ -1,23 +1,33 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> DELETE 2 @ 2 : 5 +> INSERT 2 : 6 @ 2 -> DELETE 3 @ 3 : 4 - -> INSERT 1 : 5 @ 1 - -+ import com.google.common.base.Predicates; -+ import com.google.common.collect.Iterators; -+ import com.google.common.collect.Lists; ++ import java.util.Iterator; ++ import java.util.List; + ++ import com.carrotsearch.hppc.ObjectIntIdentityHashMap; -> CHANGE 8 : 9 @ 8 : 9 +> DELETE 3 @ 3 : 7 + +> CHANGE 2 : 3 @ 2 : 3 + +~ private final ObjectIntIdentityHashMap identityMap = new ObjectIntIdentityHashMap<>(512); + +> CHANGE 3 : 4 @ 3 : 4 + +~ this.identityMap.put(key, value); + +> CHANGE 2 : 3 @ 2 : 3 ~ this.objectList.add((T) null); +> CHANGE 6 : 7 @ 6 : 8 + +~ return this.identityMap.getOrDefault(key, -1); + > EOF diff --git a/patches/minecraft/net/minecraft/util/RegistryDefaulted.edit.java b/patches/minecraft/net/minecraft/util/RegistryDefaulted.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/util/RegistryDefaulted.edit.java +++ b/patches/minecraft/net/minecraft/util/RegistryDefaulted.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/RegistryNamespaced.edit.java b/patches/minecraft/net/minecraft/util/RegistryNamespaced.edit.java index 8ecd1df..b2184c9 100644 --- a/patches/minecraft/net/minecraft/util/RegistryNamespaced.edit.java +++ b/patches/minecraft/net/minecraft/util/RegistryNamespaced.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/RegistryNamespacedDefaultedByKey.edit.java b/patches/minecraft/net/minecraft/util/RegistryNamespacedDefaultedByKey.edit.java index ad2d1ab..b58313f 100644 --- a/patches/minecraft/net/minecraft/util/RegistryNamespacedDefaultedByKey.edit.java +++ b/patches/minecraft/net/minecraft/util/RegistryNamespacedDefaultedByKey.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/RegistrySimple.edit.java b/patches/minecraft/net/minecraft/util/RegistrySimple.edit.java index e941e56..b1b8305 100644 --- a/patches/minecraft/net/minecraft/util/RegistrySimple.edit.java +++ b/patches/minecraft/net/minecraft/util/RegistrySimple.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/ResourceLocation.edit.java b/patches/minecraft/net/minecraft/util/ResourceLocation.edit.java index 176a479..ea7ea1a 100644 --- a/patches/minecraft/net/minecraft/util/ResourceLocation.edit.java +++ b/patches/minecraft/net/minecraft/util/ResourceLocation.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/ScreenShotHelper.edit.java b/patches/minecraft/net/minecraft/util/ScreenShotHelper.edit.java index 970470d..13e0abf 100644 --- a/patches/minecraft/net/minecraft/util/ScreenShotHelper.edit.java +++ b/patches/minecraft/net/minecraft/util/ScreenShotHelper.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/Session.edit.java b/patches/minecraft/net/minecraft/util/Session.edit.java index 6dc8981..cc3830b 100644 --- a/patches/minecraft/net/minecraft/util/Session.edit.java +++ b/patches/minecraft/net/minecraft/util/Session.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/StatCollector.edit.java b/patches/minecraft/net/minecraft/util/StatCollector.edit.java index 7352171..3b1269c 100644 --- a/patches/minecraft/net/minecraft/util/StatCollector.edit.java +++ b/patches/minecraft/net/minecraft/util/StatCollector.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/StringTranslate.edit.java b/patches/minecraft/net/minecraft/util/StringTranslate.edit.java index b9f1116..f3a2348 100644 --- a/patches/minecraft/net/minecraft/util/StringTranslate.edit.java +++ b/patches/minecraft/net/minecraft/util/StringTranslate.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/StringUtils.edit.java b/patches/minecraft/net/minecraft/util/StringUtils.edit.java index 3f2fc2d..d0049cd 100644 --- a/patches/minecraft/net/minecraft/util/StringUtils.edit.java +++ b/patches/minecraft/net/minecraft/util/StringUtils.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/Timer.edit.java b/patches/minecraft/net/minecraft/util/Timer.edit.java index e5c2d1b..b428c24 100644 --- a/patches/minecraft/net/minecraft/util/Timer.edit.java +++ b/patches/minecraft/net/minecraft/util/Timer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/TupleIntJsonSerializable.edit.java b/patches/minecraft/net/minecraft/util/TupleIntJsonSerializable.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/util/TupleIntJsonSerializable.edit.java +++ b/patches/minecraft/net/minecraft/util/TupleIntJsonSerializable.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/Util.edit.java b/patches/minecraft/net/minecraft/util/Util.edit.java index a6a4070..04a087e 100644 --- a/patches/minecraft/net/minecraft/util/Util.edit.java +++ b/patches/minecraft/net/minecraft/util/Util.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/Vec3.edit.java b/patches/minecraft/net/minecraft/util/Vec3.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/util/Vec3.edit.java +++ b/patches/minecraft/net/minecraft/util/Vec3.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/Vec3i.edit.java b/patches/minecraft/net/minecraft/util/Vec3i.edit.java index f7b26f5..778777b 100644 --- a/patches/minecraft/net/minecraft/util/Vec3i.edit.java +++ b/patches/minecraft/net/minecraft/util/Vec3i.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/WeightedRandom.edit.java b/patches/minecraft/net/minecraft/util/WeightedRandom.edit.java index ca98709..e71a206 100644 --- a/patches/minecraft/net/minecraft/util/WeightedRandom.edit.java +++ b/patches/minecraft/net/minecraft/util/WeightedRandom.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/WeightedRandomChestContent.edit.java b/patches/minecraft/net/minecraft/util/WeightedRandomChestContent.edit.java index ea9fc6f..29cdd96 100644 --- a/patches/minecraft/net/minecraft/util/WeightedRandomChestContent.edit.java +++ b/patches/minecraft/net/minecraft/util/WeightedRandomChestContent.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/util/WeightedRandomFishable.edit.java b/patches/minecraft/net/minecraft/util/WeightedRandomFishable.edit.java index 16fb50f..d9c7295 100644 --- a/patches/minecraft/net/minecraft/util/WeightedRandomFishable.edit.java +++ b/patches/minecraft/net/minecraft/util/WeightedRandomFishable.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/village/MerchantRecipeList.edit.java b/patches/minecraft/net/minecraft/village/MerchantRecipeList.edit.java index a1fa0c1..7ab0bd9 100644 --- a/patches/minecraft/net/minecraft/village/MerchantRecipeList.edit.java +++ b/patches/minecraft/net/minecraft/village/MerchantRecipeList.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/village/Village.edit.java b/patches/minecraft/net/minecraft/village/Village.edit.java index 7ca92ce..acb103f 100644 --- a/patches/minecraft/net/minecraft/village/Village.edit.java +++ b/patches/minecraft/net/minecraft/village/Village.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude @@ -13,7 +13,38 @@ > DELETE 4 @ 4 : 5 -> CHANGE 139 : 141 @ 139 : 140 +> INSERT 23 : 45 @ 23 + ++ private BlockPos[] positions = null; ++ ++ private void calculateNewCheckPositions() { ++ if (this.center == null || this.center.equals(BlockPos.ORIGIN)) { ++ this.positions = null; ++ } else { ++ this.positions = new BlockPos[] { this.center.add(-this.villageRadius, 0, -this.villageRadius), ++ this.center.add(-this.villageRadius, 0, this.villageRadius), ++ this.center.add(this.villageRadius, 0, -this.villageRadius), ++ this.center.add(this.villageRadius, 0, this.villageRadius), this.center }; ++ } ++ } ++ ++ public boolean isVillageAreaLoaded() { ++ for (int i = 0; this.positions != null && i < this.positions.length; i++) { ++ if (this.worldObj.isBlockLoaded(this.positions[i])) { ++ return true; ++ } ++ } ++ return false; ++ } ++ + +> INSERT 5 : 8 @ 5 + ++ if (!isVillageAreaLoaded()) { ++ return; ++ } + +> CHANGE 111 : 113 @ 111 : 112 ~ for (int m = 0, n = this.villageDoorInfoList.size(); m < n; ++m) { ~ VillageDoorInfo villagedoorinfo1 = this.villageDoorInfoList.get(m); @@ -38,11 +69,20 @@ ~ for (int m = 0, n = this.villageDoorInfoList.size(); m < n; ++m) { ~ j = Math.max(this.villageDoorInfoList.get(m).getDistanceToDoorBlockSq(this.center), j); -> CHANGE 49 : 50 @ 49 : 57 +> INSERT 4 : 6 @ 4 + ++ ++ calculateNewCheckPositions(); + +> CHANGE 45 : 46 @ 45 : 53 ~ if (nbttagcompound1.hasKey("Name")) { -> CHANGE 22 : 24 @ 22 : 23 +> INSERT 5 : 6 @ 5 + ++ calculateNewCheckPositions(); + +> CHANGE 17 : 19 @ 17 : 18 ~ for (int m = 0, n = this.villageDoorInfoList.size(); m < n; ++m) { ~ VillageDoorInfo villagedoorinfo = this.villageDoorInfoList.get(m); diff --git a/patches/minecraft/net/minecraft/village/VillageCollection.edit.java b/patches/minecraft/net/minecraft/village/VillageCollection.edit.java index 4ebbb3b..672dafc 100644 --- a/patches/minecraft/net/minecraft/village/VillageCollection.edit.java +++ b/patches/minecraft/net/minecraft/village/VillageCollection.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/village/VillageSiege.edit.java b/patches/minecraft/net/minecraft/village/VillageSiege.edit.java index b9e201a..7fec0cf 100644 --- a/patches/minecraft/net/minecraft/village/VillageSiege.edit.java +++ b/patches/minecraft/net/minecraft/village/VillageSiege.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/ChunkCache.edit.java b/patches/minecraft/net/minecraft/world/ChunkCache.edit.java index 3595759..4095d5d 100644 --- a/patches/minecraft/net/minecraft/world/ChunkCache.edit.java +++ b/patches/minecraft/net/minecraft/world/ChunkCache.edit.java @@ -1,13 +1,20 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 8 @ 8 : 12 -> CHANGE 77 : 78 @ 77 : 78 +> INSERT 75 : 79 @ 75 + ++ public int getBiomeColorForCoords(BlockPos var1, int index) { ++ return this.worldObj.getBiomeColorForCoords(var1, index); ++ } ++ + +> CHANGE 2 : 3 @ 2 : 3 ~ return Chunk.getNoSkyLightValue(); diff --git a/patches/minecraft/net/minecraft/world/DifficultyInstance.edit.java b/patches/minecraft/net/minecraft/world/DifficultyInstance.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/world/DifficultyInstance.edit.java +++ b/patches/minecraft/net/minecraft/world/DifficultyInstance.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/EnumDifficulty.edit.java b/patches/minecraft/net/minecraft/world/EnumDifficulty.edit.java index c83d1ce..a99094e 100644 --- a/patches/minecraft/net/minecraft/world/EnumDifficulty.edit.java +++ b/patches/minecraft/net/minecraft/world/EnumDifficulty.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/Explosion.edit.java b/patches/minecraft/net/minecraft/world/Explosion.edit.java index 74417ec..7b68c40 100644 --- a/patches/minecraft/net/minecraft/world/Explosion.edit.java +++ b/patches/minecraft/net/minecraft/world/Explosion.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/GameRules.edit.java b/patches/minecraft/net/minecraft/world/GameRules.edit.java index 138dc99..7762cb9 100644 --- a/patches/minecraft/net/minecraft/world/GameRules.edit.java +++ b/patches/minecraft/net/minecraft/world/GameRules.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/IBlockAccess.edit.java b/patches/minecraft/net/minecraft/world/IBlockAccess.edit.java index 97688dc..0dc2ed2 100644 --- a/patches/minecraft/net/minecraft/world/IBlockAccess.edit.java +++ b/patches/minecraft/net/minecraft/world/IBlockAccess.edit.java @@ -1,10 +1,15 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 6 @ 6 : 7 +> INSERT 13 : 15 @ 13 + ++ int getBiomeColorForCoords(BlockPos var1, int index); ++ + > EOF diff --git a/patches/minecraft/net/minecraft/world/IInteractionObject.edit.java b/patches/minecraft/net/minecraft/world/IInteractionObject.edit.java index be6673d..bcd4bdc 100644 --- a/patches/minecraft/net/minecraft/world/IInteractionObject.edit.java +++ b/patches/minecraft/net/minecraft/world/IInteractionObject.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/ILockableContainer.edit.java b/patches/minecraft/net/minecraft/world/ILockableContainer.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/world/ILockableContainer.edit.java +++ b/patches/minecraft/net/minecraft/world/ILockableContainer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/SpawnerAnimals.edit.java b/patches/minecraft/net/minecraft/world/SpawnerAnimals.edit.java index dc96e59..618081e 100644 --- a/patches/minecraft/net/minecraft/world/SpawnerAnimals.edit.java +++ b/patches/minecraft/net/minecraft/world/SpawnerAnimals.edit.java @@ -1,37 +1,49 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> INSERT 3 : 4 @ 3 +> CHANGE 2 : 5 @ 2 : 3 -+ +~ import com.carrotsearch.hppc.LongHashSet; +~ import com.carrotsearch.hppc.LongSet; +~ import com.carrotsearch.hppc.cursors.LongCursor; -> CHANGE 1 : 4 @ 1 : 2 +> CHANGE 1 : 4 @ 1 : 3 ~ ~ import net.lax1dude.eaglercraft.v1_8.EagRuntime; ~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; -> INSERT 2 : 3 @ 2 +> INSERT 1 : 2 @ 1 + import net.minecraft.entity.EntityList; > DELETE 9 @ 9 : 12 -> CHANGE 15 : 18 @ 15 : 16 +> CHANGE 5 : 6 @ 5 : 6 + +~ private final LongSet eligibleChunksForSpawning = new LongHashSet(); + +> CHANGE 9 : 12 @ 9 : 10 ~ List lst = spawnHostileMobs.playerEntities; ~ for (int m = 0, n = lst.size(); m < n; ++m) { ~ EntityPlayer entityplayer = lst.get(m); -> CHANGE 9 : 12 @ 9 : 10 +> CHANGE 3 : 4 @ 3 : 4 +~ byte b0 = (byte) spawnHostileMobs.getMinecraftServer().getConfigurationManager().getViewDistance(); + +> CHANGE 4 : 9 @ 4 : 6 + +~ int cx = l + j; +~ int cz = i1 + k; +~ long chunkcoordintpair = ChunkCoordIntPair.chunkXZ2Int(cx, cz); ~ if (!this.eligibleChunksForSpawning.contains(chunkcoordintpair) -~ && spawnHostileMobs.theChunkProviderServer.chunkExists(chunkcoordintpair.chunkXPos, -~ chunkcoordintpair.chunkZPos)) { +~ && spawnHostileMobs.theChunkProviderServer.chunkExists(cx, cz)) { > CHANGE 13 : 16 @ 13 : 14 @@ -39,7 +51,15 @@ ~ for (int m = 0; m < types.length; ++m) { ~ EnumCreatureType enumcreaturetype = types[m]; -> CHANGE 51 : 53 @ 51 : 54 +> CHANGE 6 : 11 @ 6 : 9 + +~ label374: for (LongCursor chunkcoordintpair1 : this.eligibleChunksForSpawning) { +~ long chunkcoordintpair1l = chunkcoordintpair1.value; +~ int chunkXPos = (int) (chunkcoordintpair1l & 4294967295L); +~ int chunkZPos = (int) (chunkcoordintpair1l >>> 32); +~ BlockPos blockpos = getRandomChunkPosition(spawnHostileMobs, chunkXPos, chunkZPos); + +> CHANGE 42 : 44 @ 42 : 45 ~ entityliving = (EntityLiving) EntityList.createEntityByClassUnsafe( ~ biomegenbase$spawnlistentry.entityClass, spawnHostileMobs); diff --git a/patches/minecraft/net/minecraft/world/Teleporter.edit.java b/patches/minecraft/net/minecraft/world/Teleporter.edit.java index 8c660fa..62dbb6a 100644 --- a/patches/minecraft/net/minecraft/world/Teleporter.edit.java +++ b/patches/minecraft/net/minecraft/world/Teleporter.edit.java @@ -1,26 +1,56 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> CHANGE 5 : 6 @ 5 : 6 +> CHANGE 2 : 7 @ 2 : 6 +~ import com.carrotsearch.hppc.LongArrayList; +~ import com.carrotsearch.hppc.LongObjectHashMap; +~ import com.carrotsearch.hppc.LongObjectMap; +~ ~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; -> DELETE 9 @ 9 : 11 +> DELETE 7 @ 7 : 8 + +> DELETE 1 @ 1 : 3 + +> CHANGE 3 : 6 @ 3 : 6 + +~ private final EaglercraftRandom random; +~ private final LongObjectMap destinationCoordinateCache = new LongObjectHashMap<>(); +~ private final LongArrayList destinationCoordinateKeys = new LongArrayList(); > CHANGE 3 : 4 @ 3 : 4 -~ private final EaglercraftRandom random; - -> CHANGE 5 : 6 @ 5 : 6 - ~ this.random = new EaglercraftRandom(worldIn.getSeed(), !worldIn.getWorldInfo().isOldEaglercraftRandom()); -> CHANGE 152 : 153 @ 152 : 153 +> CHANGE 41 : 43 @ 41 : 44 + +~ if (this.destinationCoordinateCache.containsKey(k)) { +~ Teleporter.PortalPosition teleporter$portalposition = this.destinationCoordinateCache.get(k); + +> CHANGE 33 : 34 @ 33 : 34 + +~ this.destinationCoordinateCache.put(k, + +> CHANGE 74 : 75 @ 74 : 75 ~ BlockPos blockpos$mutableblockpos = new BlockPos(); +> DELETE 165 @ 165 : 166 + +> CHANGE 2 : 5 @ 2 : 6 + +~ for (int j = 0; j < destinationCoordinateKeys.size(); ++j) { +~ long olong = destinationCoordinateKeys.get(j); +~ Teleporter.PortalPosition teleporter$portalposition = this.destinationCoordinateCache.get(olong); + +> CHANGE 1 : 3 @ 1 : 3 + +~ destinationCoordinateKeys.removeAt(j--); +~ this.destinationCoordinateCache.remove(olong); + > EOF diff --git a/patches/minecraft/net/minecraft/world/World.edit.java b/patches/minecraft/net/minecraft/world/World.edit.java index a3a9aa9..2fc01b8 100644 --- a/patches/minecraft/net/minecraft/world/World.edit.java +++ b/patches/minecraft/net/minecraft/world/World.edit.java @@ -1,21 +1,27 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> INSERT 5 : 6 @ 5 +> INSERT 2 : 6 @ 2 + ++ import com.carrotsearch.hppc.IntObjectHashMap; ++ import com.carrotsearch.hppc.IntObjectMap; ++ import com.carrotsearch.hppc.LongHashSet; ++ import com.carrotsearch.hppc.LongSet; + +> INSERT 3 : 4 @ 3 + -> CHANGE 5 : 7 @ 5 : 6 +> DELETE 5 @ 5 : 6 + +> CHANGE 1 : 6 @ 1 : 2 ~ ~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; - -> CHANGE 1 : 4 @ 1 : 2 - ~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; ~ import net.lax1dude.eaglercraft.v1_8.HString; ~ @@ -26,9 +32,22 @@ > DELETE 16 @ 16 : 17 -> DELETE 15 @ 15 : 29 +> DELETE 9 @ 9 : 10 -> CHANGE 24 : 25 @ 24 : 25 +> CHANGE 5 : 6 @ 5 : 19 + +~ import net.minecraft.world.biome.BiomeColorHelper; + +> CHANGE 15 : 17 @ 15 : 17 + +~ public final Set loadedTileEntityList = Sets.newIdentityHashSet(); +~ public final Set tickableTileEntities = Sets.newIdentityHashSet(); + +> CHANGE 4 : 5 @ 4 : 5 + +~ protected final IntObjectMap entitiesById = new IntObjectHashMap<>(); + +> CHANGE 2 : 3 @ 2 : 3 ~ protected int updateLCG = (new EaglercraftRandom()).nextInt(); @@ -38,9 +57,11 @@ > DELETE 8 @ 8 : 9 -> DELETE 2 @ 2 : 3 +> CHANGE 2 : 3 @ 2 : 4 -> INSERT 7 : 8 @ 7 +~ protected LongSet activeChunkSet = new LongHashSet(); + +> INSERT 6 : 7 @ 6 + public final boolean isRemote; @@ -61,7 +82,42 @@ ~ return CrashReportCategory ~ .getCoordinateInfo(new net.minecraft.util.BlockPos(pos.getX(), pos.getY(), pos.getZ())); -> DELETE 123 @ 123 : 124 +> INSERT 9 : 13 @ 9 + ++ public int getBiomeColorForCoords(BlockPos var1, int index) { ++ return BiomeColorHelper.getBiomeColorForCoordsOld(this, var1, index); ++ } ++ + +> CHANGE 34 : 35 @ 34 : 35 + +~ return !this.isValid(pos) ? false : this.isChunkLoaded(pos.x >> 4, pos.z >> 4, true); + +> CHANGE 3 : 4 @ 3 : 4 + +~ return !this.isValid(pos) ? false : this.isChunkLoaded(pos.x >> 4, pos.z >> 4, allowEmpty); + +> CHANGE 53 : 54 @ 53 : 54 + +~ return this.chunkProvider.provideChunk(pos.x >> 4, pos.z >> 4); + +> INSERT 2 : 8 @ 2 + ++ public Chunk getChunkFromBlockCoordsIfLoaded(BlockPos pos) { ++ int x = pos.x >> 4; ++ int z = pos.z >> 4; ++ return this.chunkProvider.chunkExists(x, z) ? this.chunkProvider.provideChunk(x, z) : null; ++ } ++ + +> INSERT 4 : 8 @ 4 + ++ public Chunk getChunkFromChunkCoordsIfLoaded(int chunkX, int chunkZ) { ++ return this.chunkProvider.chunkExists(chunkX, chunkZ) ? this.chunkProvider.provideChunk(chunkX, chunkZ) : null; ++ } ++ + +> DELETE 15 @ 15 : 16 > DELETE 1 @ 1 : 2 @@ -100,7 +156,31 @@ ~ } ~ } else if (j < lightValue) { -> CHANGE 320 : 321 @ 320 : 321 +> INSERT 19 : 28 @ 19 + ++ public IBlockState getBlockStateIfLoaded(BlockPos pos) { ++ if (!this.isValid(pos)) { ++ return Blocks.air.getDefaultState(); ++ } else { ++ Chunk chunk = this.getChunkFromBlockCoordsIfLoaded(pos); ++ return chunk != null ? chunk.getBlockState(pos) : null; ++ } ++ } ++ + +> CHANGE 23 : 26 @ 23 : 24 + +~ IBlockState iblockstate = this.getBlockStateIfLoaded(blockpos); +~ if (iblockstate == null) +~ return null; + +> CHANGE 98 : 101 @ 98 : 99 + +~ IBlockState iblockstate1 = this.getBlockStateIfLoaded(blockpos); +~ if (iblockstate1 == null) +~ return null; + +> CHANGE 178 : 179 @ 178 : 179 ~ BlockPos blockpos$mutableblockpos = new BlockPos(); @@ -180,7 +260,17 @@ > DELETE 23 @ 23 : 24 -> DELETE 14 @ 14 : 15 +> CHANGE 5 : 8 @ 5 : 6 + +~ int l = this.getRenderDistanceChunks() - 1; +~ if (l < 1) +~ l = 1; + +> CHANGE 3 : 4 @ 3 : 4 + +~ this.activeChunkSet.add(ChunkCoordIntPair.chunkXZ2Int(i1 + j, j1 + k)); + +> DELETE 4 @ 4 : 5 > DELETE 4 @ 4 : 5 @@ -250,7 +340,13 @@ > DELETE 8 @ 8 : 9 -> CHANGE 43 : 46 @ 43 : 45 +> CHANGE 30 : 33 @ 30 : 33 + +~ Chunk chunk = this.getChunkFromChunkCoordsIfLoaded(i1, j1); +~ if (chunk != null) { +~ chunk.getEntitiesWithinAABBForEntity(entityIn, boundingBox, arraylist, predicate); + +> CHANGE 10 : 13 @ 10 : 12 ~ for (int i = 0, l = this.loadedEntityList.size(); i < l; ++i) { ~ Entity entity = this.loadedEntityList.get(i); @@ -262,7 +358,11 @@ ~ Entity entity = this.playerEntities.get(i); ~ if (playerType.isAssignableFrom(entity.getClass()) && filter.apply((T) entity)) { -> CHANGE 68 : 70 @ 68 : 69 +> CHANGE 51 : 52 @ 51 : 52 + +~ return this.entitiesById.get(id); + +> CHANGE 16 : 18 @ 16 : 17 ~ for (int j = 0, l = this.loadedEntityList.size(); j < l; ++j) { ~ Entity entity = this.loadedEntityList.get(j); diff --git a/patches/minecraft/net/minecraft/world/WorldManager.edit.java b/patches/minecraft/net/minecraft/world/WorldManager.edit.java index 2e74d30..79251a6 100644 --- a/patches/minecraft/net/minecraft/world/WorldManager.edit.java +++ b/patches/minecraft/net/minecraft/world/WorldManager.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/WorldProvider.edit.java b/patches/minecraft/net/minecraft/world/WorldProvider.edit.java index 41997c4..68d5720 100644 --- a/patches/minecraft/net/minecraft/world/WorldProvider.edit.java +++ b/patches/minecraft/net/minecraft/world/WorldProvider.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/WorldProviderEnd.edit.java b/patches/minecraft/net/minecraft/world/WorldProviderEnd.edit.java index be6673d..bcd4bdc 100644 --- a/patches/minecraft/net/minecraft/world/WorldProviderEnd.edit.java +++ b/patches/minecraft/net/minecraft/world/WorldProviderEnd.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/WorldProviderHell.edit.java b/patches/minecraft/net/minecraft/world/WorldProviderHell.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/world/WorldProviderHell.edit.java +++ b/patches/minecraft/net/minecraft/world/WorldProviderHell.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/WorldProviderSurface.edit.java b/patches/minecraft/net/minecraft/world/WorldProviderSurface.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/world/WorldProviderSurface.edit.java +++ b/patches/minecraft/net/minecraft/world/WorldProviderSurface.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/WorldServer.edit.java b/patches/minecraft/net/minecraft/world/WorldServer.edit.java index 07ea9b2..dc97484 100644 --- a/patches/minecraft/net/minecraft/world/WorldServer.edit.java +++ b/patches/minecraft/net/minecraft/world/WorldServer.edit.java @@ -1,11 +1,15 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> DELETE 6 @ 6 : 7 +> INSERT 2 : 3 @ 2 + ++ import com.carrotsearch.hppc.cursors.LongCursor; + +> DELETE 4 @ 4 : 5 > CHANGE 4 : 5 @ 4 : 5 @@ -66,9 +70,23 @@ ~ for (int k = 0, l = this.playerEntities.size(); k < l; ++k) { ~ EntityPlayer entityplayer = this.playerEntities.get(k); -> DELETE 48 @ 48 : 49 +> CHANGE 36 : 39 @ 36 : 39 -> DELETE 2 @ 2 : 3 +~ for (LongCursor chunkcoordintpair1 : this.activeChunkSet) { +~ long l = chunkcoordintpair1.value; +~ this.getChunkFromChunkCoords((int) (l & 4294967295L), (int) (l >>> 32)).func_150804_b(false); + +> CHANGE 6 : 13 @ 6 : 11 + +~ for (LongCursor chunkcoordintpair : this.activeChunkSet) { +~ long ll = chunkcoordintpair.value; +~ int chunkXPos = (int) (ll & 4294967295L); +~ int chunkZPos = (int) (ll >>> 32); +~ int k = chunkXPos * 16; +~ int l = chunkZPos * 16; +~ Chunk chunk = this.getChunkFromChunkCoords(chunkXPos, chunkZPos); + +> DELETE 1 @ 1 : 2 > DELETE 1 @ 1 : 2 @@ -104,7 +122,28 @@ > DELETE 15 @ 15 : 16 -> CHANGE 144 : 145 @ 144 : 145 +> CHANGE 76 : 77 @ 76 : 77 + +~ ArrayList arraylist = Lists.newArrayList(); + +> CHANGE 1 : 15 @ 1 : 7 + +~ for (int chunkX = (minX >> 4); chunkX <= ((maxX - 1) >> 4); chunkX++) { +~ for (int chunkZ = (minZ >> 4); chunkZ <= ((maxZ - 1) >> 4); chunkZ++) { +~ Chunk chunk = getChunkFromChunkCoords(chunkX, chunkZ); +~ if (chunk == null) { +~ continue; +~ } +~ +~ for (TileEntity tileentity : chunk.getTileEntityMap().values()) { +~ BlockPos pos = tileentity.getPos(); +~ if ((pos.x >= minX) && (pos.y >= minY) && (pos.z >= minZ) && (pos.x < maxX) && (pos.y < maxY) +~ && (pos.z < maxZ)) { +~ arraylist.add(tileentity); +~ } +~ } + +> CHANGE 60 : 61 @ 60 : 61 ~ EaglercraftRandom random = new EaglercraftRandom(this.getSeed()); @@ -122,7 +161,23 @@ ~ protected void saveLevel() { -> CHANGE 63 : 66 @ 63 : 64 +> CHANGE 16 : 17 @ 16 : 17 + +~ this.entitiesById.put(entity.getEntityId(), entity); + +> CHANGE 4 : 5 @ 4 : 5 + +~ this.entitiesById.put(aentity[i].getEntityId(), aentity[i]); + +> CHANGE 7 : 8 @ 7 : 8 + +~ this.entitiesById.remove(entity.getEntityId()); + +> CHANGE 4 : 5 @ 4 : 5 + +~ this.entitiesById.remove(aentity[i].getEntityId()); + +> CHANGE 28 : 31 @ 28 : 29 ~ List lst = this.playerEntities; ~ for (int i = 0, l = lst.size(); i < l; ++i) { diff --git a/patches/minecraft/net/minecraft/world/WorldServerMulti.edit.java b/patches/minecraft/net/minecraft/world/WorldServerMulti.edit.java index c4b788e..4e68694 100644 --- a/patches/minecraft/net/minecraft/world/WorldServerMulti.edit.java +++ b/patches/minecraft/net/minecraft/world/WorldServerMulti.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/WorldSettings.edit.java b/patches/minecraft/net/minecraft/world/WorldSettings.edit.java index acba398..0f34111 100644 --- a/patches/minecraft/net/minecraft/world/WorldSettings.edit.java +++ b/patches/minecraft/net/minecraft/world/WorldSettings.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeCache.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeCache.edit.java index 6422a38..2c239f4 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeCache.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeCache.edit.java @@ -1,10 +1,31 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> DELETE 6 @ 6 : 8 +> INSERT 2 : 4 @ 2 + ++ import com.carrotsearch.hppc.LongObjectHashMap; ++ import com.carrotsearch.hppc.LongObjectMap; + +> DELETE 3 @ 3 : 6 + +> CHANGE 4 : 5 @ 4 : 5 + +~ private LongObjectMap cacheMap = new LongObjectHashMap<>(); + +> CHANGE 10 : 11 @ 10 : 11 + +~ BiomeCache.Block biomecache$block = this.cacheMap.get(i); + +> CHANGE 2 : 3 @ 2 : 3 + +~ this.cacheMap.put(i, biomecache$block); + +> CHANGE 19 : 20 @ 19 : 20 + +~ BiomeCache.Block biomecache$block = this.cache.get(k); > EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeColorHelper.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeColorHelper.edit.java index d21cbbe..d22e409 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeColorHelper.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeColorHelper.edit.java @@ -1,14 +1,43 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude > DELETE 4 @ 4 : 5 -> CHANGE 24 : 25 @ 24 : 25 +> INSERT 2 : 3 @ 2 + ++ + +> CHANGE 22 : 23 @ 22 : 23 ~ for (BlockPos blockpos$mutableblockpos : BlockPos.getAllInBoxMutable(parBlockPos.add(-1, 0, -1), +> CHANGE 12 : 13 @ 12 : 13 + +~ return parIBlockAccess.getBiomeColorForCoords(parBlockPos, 0); + +> CHANGE 3 : 4 @ 3 : 4 + +~ return parIBlockAccess.getBiomeColorForCoords(parBlockPos, 1); + +> CHANGE 3 : 4 @ 3 : 4 + +~ return parIBlockAccess.getBiomeColorForCoords(parBlockPos, 2); + +> INSERT 2 : 12 @ 2 + ++ public static int getBiomeColorForCoordsOld(IBlockAccess parIBlockAccess, BlockPos parBlockPos, int index) { ++ if (index == 0) { ++ return func_180285_a(parIBlockAccess, parBlockPos, field_180291_a); ++ } else if (index == 1) { ++ return func_180285_a(parIBlockAccess, parBlockPos, field_180289_b); ++ } else { ++ return func_180285_a(parIBlockAccess, parBlockPos, field_180290_c); ++ } ++ } ++ + > EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeDecorator.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeDecorator.edit.java index 36bdb08..95a7bc1 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeDecorator.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeDecorator.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeEndDecorator.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeEndDecorator.edit.java index 7e3f464..453a3eb 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeEndDecorator.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeEndDecorator.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenBase.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenBase.edit.java index 334784a..d6d96c0 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenBase.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenBase.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenBeach.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenBeach.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenBeach.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenBeach.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenDesert.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenDesert.edit.java index f183e49..fca8b9d 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenDesert.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenDesert.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenEnd.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenEnd.edit.java index a7f89c1..300ea78 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenEnd.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenEnd.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenForest.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenForest.edit.java index de9160d..27683c6 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenForest.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenForest.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenHell.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenHell.edit.java index be6673d..bcd4bdc 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenHell.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenHell.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenHills.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenHills.edit.java index 28eecd1..f7d10c3 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenHills.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenHills.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenJungle.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenJungle.edit.java index 96dab79..df1bb9c 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenJungle.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenJungle.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenMesa.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenMesa.edit.java index d1b0862..140485b 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenMesa.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenMesa.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenMushroomIsland.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenMushroomIsland.edit.java index 0d7c65a..4e68ba6 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenMushroomIsland.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenMushroomIsland.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenMutated.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenMutated.edit.java index 8944248..a2a22ec 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenMutated.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenMutated.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenOcean.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenOcean.edit.java index e684c10..0a41c24 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenOcean.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenOcean.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenPlains.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenPlains.edit.java index fb25bdd..e03e6a5 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenPlains.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenPlains.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenRiver.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenRiver.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenRiver.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenRiver.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenSavanna.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenSavanna.edit.java index 51cda40..c888056 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenSavanna.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenSavanna.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenSnow.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenSnow.edit.java index afe7756..af54b08 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenSnow.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenSnow.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenStoneBeach.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenStoneBeach.edit.java index a8dd664..d840d8b 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenStoneBeach.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenStoneBeach.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenSwamp.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenSwamp.edit.java index d11854c..8b2b7b5 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenSwamp.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenSwamp.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenTaiga.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenTaiga.edit.java index 304e65e..0d93bb1 100644 --- a/patches/minecraft/net/minecraft/world/biome/BiomeGenTaiga.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenTaiga.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/biome/WorldChunkManager.edit.java b/patches/minecraft/net/minecraft/world/biome/WorldChunkManager.edit.java index 118f33a..da4c7da 100644 --- a/patches/minecraft/net/minecraft/world/biome/WorldChunkManager.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/WorldChunkManager.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude @@ -11,7 +11,14 @@ > DELETE 6 @ 6 : 8 -> CHANGE 167 : 168 @ 167 : 168 +> INSERT 47 : 51 @ 47 + ++ public BiomeGenBase getBiomeGenerator(int x, int z, BiomeGenBase biomeGenBaseIn) { ++ return this.biomeCache.func_180284_a(x, z, biomeGenBaseIn); ++ } ++ + +> CHANGE 120 : 121 @ 120 : 121 ~ public BlockPos findBiomePosition(int x, int z, int range, List biomes, EaglercraftRandom random) { diff --git a/patches/minecraft/net/minecraft/world/biome/WorldChunkManagerHell.edit.java b/patches/minecraft/net/minecraft/world/biome/WorldChunkManagerHell.edit.java index ed8acce..dad81e8 100644 --- a/patches/minecraft/net/minecraft/world/biome/WorldChunkManagerHell.edit.java +++ b/patches/minecraft/net/minecraft/world/biome/WorldChunkManagerHell.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/border/IBorderListener.edit.java b/patches/minecraft/net/minecraft/world/border/IBorderListener.edit.java index 8b7360a..903da28 100644 --- a/patches/minecraft/net/minecraft/world/border/IBorderListener.edit.java +++ b/patches/minecraft/net/minecraft/world/border/IBorderListener.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/border/WorldBorder.edit.java b/patches/minecraft/net/minecraft/world/border/WorldBorder.edit.java index e1038ca..3a9b258 100644 --- a/patches/minecraft/net/minecraft/world/border/WorldBorder.edit.java +++ b/patches/minecraft/net/minecraft/world/border/WorldBorder.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude @@ -16,7 +16,17 @@ > DELETE 4 @ 4 : 6 -> CHANGE 105 : 108 @ 105 : 107 +> INSERT 34 : 41 @ 34 + ++ public boolean contains(long range) { ++ int chunkXPos = (int) (range & 4294967295L); ++ int chunkZPos = (int) (range >>> 32); ++ return (double) ((chunkXPos << 4) + 15) > this.minX() && (double) (chunkXPos << 4) < this.maxX() ++ && (double) ((chunkZPos << 4) + 15) > this.minZ() && (double) (chunkZPos << 4) < this.maxZ(); ++ } ++ + +> CHANGE 71 : 74 @ 71 : 73 ~ List lst = this.getListeners(); ~ for (int i = 0, l = lst.size(); i < l; ++i) { diff --git a/patches/minecraft/net/minecraft/world/chunk/Chunk.edit.java b/patches/minecraft/net/minecraft/world/chunk/Chunk.edit.java index 1a1d2ab..ecbf353 100644 --- a/patches/minecraft/net/minecraft/world/chunk/Chunk.edit.java +++ b/patches/minecraft/net/minecraft/world/chunk/Chunk.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude @@ -11,17 +11,17 @@ + import java.util.LinkedList; -> CHANGE 2 : 4 @ 2 : 3 +> CHANGE 2 : 5 @ 2 : 5 ~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; -~ - -> CHANGE 1 : 3 @ 1 : 2 - ~ ~ import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerMinecraftServer; -> DELETE 18 @ 18 : 19 +> DELETE 4 @ 4 : 6 + +> DELETE 8 @ 8 : 9 + +> DELETE 3 @ 3 : 4 > DELETE 2 @ 2 : 5 @@ -31,15 +31,28 @@ ~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ~ import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.DeferredStateManager; -> CHANGE 24 : 25 @ 24 : 25 +> CHANGE 24 : 26 @ 24 : 25 ~ private List tileEntityPosQueue; +~ private final ChunkCoordIntPair coordsCache; > CHANGE 8 : 9 @ 8 : 9 ~ this.tileEntityPosQueue = new LinkedList(); -> INSERT 135 : 138 @ 135 +> INSERT 5 : 6 @ 5 + ++ this.coordsCache = new ChunkCoordIntPair(x, z); + +> CHANGE 38 : 39 @ 38 : 39 + +~ return this.getHeightValue(pos.x & 15, pos.z & 15); + +> CHANGE 29 : 30 @ 29 : 30 + +~ Block block = this.getBlock(j, l - 1, k); + +> INSERT 61 : 64 @ 61 + if (!this.worldObj.isRemote) { + ++EaglerMinecraftServer.counterLightUpdate; @@ -78,66 +91,70 @@ + ++EaglerMinecraftServer.counterLightUpdate; + } -> CHANGE 61 : 70 @ 61 : 65 +> CHANGE 9 : 10 @ 9 : 10 -~ try { -~ if (pos.getY() >= 0 && pos.getY() >> 4 < this.storageArrays.length) { -~ ExtendedBlockStorage extendedblockstorage = this.storageArrays[pos.getY() >> 4]; -~ if (extendedblockstorage != null) { -~ int j = pos.getX() & 15; -~ int k = pos.getY() & 15; -~ int i = pos.getZ() & 15; -~ return extendedblockstorage.get(j, k, i); -~ } +~ return this.getBlock(x, y, z).getLightOpacity(); -> CHANGE 2 : 9 @ 2 : 17 +> CHANGE 2 : 3 @ 2 : 4 -~ return Blocks.air.getDefaultState(); -~ } catch (Throwable throwable) { -~ CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Getting block state"); -~ CrashReportCategory crashreportcategory = crashreport.makeCategory("Block being got"); -~ crashreportcategory.addCrashSectionCallable("Location", new Callable() { -~ public String call() throws Exception { -~ return CrashReportCategory.getCoordinateInfo(pos); +~ public Block getBlock(int x, int y, int z) { -> INSERT 1 : 5 @ 1 +> CHANGE 3 : 4 @ 3 : 9 -+ }); -+ throw new ReportedException(crashreport); -+ } -+ } +~ return extendedblockstorage.getBlockByExtId(x, y & 15, z); -> CHANGE 1 : 14 @ 1 : 11 +> CHANGE 3 : 4 @ 3 : 4 -~ /** -~ * only use with a regular "net.minecraft.util.BlockPos"! -~ */ -~ public IBlockState getBlockStateFaster(final BlockPos pos) { -~ try { -~ if (pos.y >= 0 && pos.y >> 4 < this.storageArrays.length) { -~ ExtendedBlockStorage extendedblockstorage = this.storageArrays[pos.getY() >> 4]; -~ if (extendedblockstorage != null) { -~ int j = pos.x & 15; -~ int k = pos.y & 15; -~ int i = pos.z & 15; -~ return extendedblockstorage.get(j, k, i); -~ } +~ return Blocks.air; -> INSERT 1 : 12 @ 1 +> CHANGE 2 : 11 @ 2 : 15 + +~ public Block getBlock(final BlockPos pos) { +~ if (pos.y >= 0 && pos.y >> 4 < this.storageArrays.length) { +~ ExtendedBlockStorage extendedblockstorage = this.storageArrays[pos.y >> 4]; +~ if (extendedblockstorage != null) { +~ int j = pos.x & 15; +~ int k = pos.y & 15; +~ int i = pos.z & 15; +~ return extendedblockstorage.getBlockByExtId(j, k, i); +~ } + +> DELETE 1 @ 1 : 2 + +> CHANGE 1 : 2 @ 1 : 14 + +~ return Blocks.air; + +> CHANGE 3 : 10 @ 3 : 7 + +~ if (pos.y >= 0 && pos.y >> 4 < this.storageArrays.length) { +~ ExtendedBlockStorage extendedblockstorage = this.storageArrays[pos.y >> 4]; +~ if (extendedblockstorage != null) { +~ int j = pos.x & 15; +~ int k = pos.y & 15; +~ int i = pos.z & 15; +~ return extendedblockstorage.get(j, k, i); + +> DELETE 1 @ 1 : 30 + +> INSERT 1 : 3 @ 1 + -+ return Blocks.air.getDefaultState(); -+ } catch (Throwable throwable) { -+ CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Getting block state"); -+ CrashReportCategory crashreportcategory = crashreport.makeCategory("Block being got"); -+ crashreportcategory.addCrashSectionCallable("Location", new Callable() { -+ public String call() throws Exception { -+ return CrashReportCategory.getCoordinateInfo(pos); -+ } -+ }); -+ throw new ReportedException(crashreport); ++ return Blocks.air.getDefaultState(); -> CHANGE 110 : 112 @ 110 : 111 +> CHANGE 16 : 19 @ 16 : 19 + +~ int i = pos.x & 15; +~ int j = pos.y; +~ int k = pos.z & 15; + +> CHANGE 86 : 89 @ 86 : 89 + +~ int i = blockpos.x & 15; +~ int j = blockpos.y; +~ int k = blockpos.z & 15; + +> CHANGE 1 : 3 @ 1 : 2 ~ return extendedblockstorage == null ~ ? (this.canSeeSky(blockpos) ? enumskyblock.defaultLightValue : getNoSkyLightValue()) @@ -146,7 +163,19 @@ ~ ? (this.worldObj.provider.getHasNoSky() ? getNoSkyLightValue() -> CHANGE 35 : 36 @ 35 : 36 +> CHANGE 6 : 9 @ 6 : 9 + +~ int j = blockpos.x & 15; +~ int k = blockpos.y; +~ int l = blockpos.z & 15; + +> CHANGE 19 : 22 @ 19 : 22 + +~ int j = blockpos.x & 15; +~ int k = blockpos.y; +~ int l = blockpos.z & 15; + +> CHANGE 4 : 5 @ 4 : 5 ~ : getNoSkyLightValue(); @@ -162,7 +191,13 @@ + } + -> INSERT 58 : 59 @ 58 +> CHANGE 43 : 46 @ 43 : 46 + +~ int i = blockpos.x & 15; +~ int j = blockpos.y; +~ int k = blockpos.z & 15; + +> INSERT 12 : 13 @ 12 + BlockPos pos2 = new BlockPos(blockpos); @@ -197,7 +232,18 @@ ~ BlockPos blockpos = (BlockPos) this.tileEntityPosQueue.remove(0); -> CHANGE 113 : 114 @ 113 : 114 +> CHANGE 15 : 16 @ 15 : 16 + +~ return coordsCache; + +> INSERT 2 : 6 @ 2 + ++ public long getChunkCoordLong() { ++ return ChunkCoordIntPair.chunkXZ2Int(this.xPosition, this.zPosition); ++ } ++ + +> CHANGE 95 : 96 @ 95 : 96 ~ if (chunkManager != null && k == 255) { diff --git a/patches/minecraft/net/minecraft/world/chunk/EmptyChunk.edit.java b/patches/minecraft/net/minecraft/world/chunk/EmptyChunk.edit.java index 9faa82c..74374ca 100644 --- a/patches/minecraft/net/minecraft/world/chunk/EmptyChunk.edit.java +++ b/patches/minecraft/net/minecraft/world/chunk/EmptyChunk.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/chunk/IChunkProvider.edit.java b/patches/minecraft/net/minecraft/world/chunk/IChunkProvider.edit.java index d18a64d..9c7a842 100644 --- a/patches/minecraft/net/minecraft/world/chunk/IChunkProvider.edit.java +++ b/patches/minecraft/net/minecraft/world/chunk/IChunkProvider.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/chunk/NibbleArray.edit.java b/patches/minecraft/net/minecraft/world/chunk/NibbleArray.edit.java new file mode 100644 index 0000000..dd35c2d --- /dev/null +++ b/patches/minecraft/net/minecraft/world/chunk/NibbleArray.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2025 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 29 : 31 @ 29 : 31 + +~ int shift = (index & 1) << 2; +~ return data[index >> 1] >> shift & 15; + +> CHANGE 3 : 6 @ 3 : 10 + +~ int i = index >> 1; +~ int shift = (index & 1) << 2; +~ data[i] = (byte) (data[i] & ~(15 << shift) | (value & 15) << shift); + +> EOF diff --git a/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.edit.java b/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.edit.java index 535387b..18fcc1c 100644 --- a/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.edit.java +++ b/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/chunk/storage/ChunkLoader.edit.java b/patches/minecraft/net/minecraft/world/chunk/storage/ChunkLoader.edit.java index 678d7f3..046c9be 100644 --- a/patches/minecraft/net/minecraft/world/chunk/storage/ChunkLoader.edit.java +++ b/patches/minecraft/net/minecraft/world/chunk/storage/ChunkLoader.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/chunk/storage/IChunkLoader.edit.java b/patches/minecraft/net/minecraft/world/chunk/storage/IChunkLoader.edit.java index a81c606..4e6419f 100644 --- a/patches/minecraft/net/minecraft/world/chunk/storage/IChunkLoader.edit.java +++ b/patches/minecraft/net/minecraft/world/chunk/storage/IChunkLoader.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/chunk/storage/RegionFile.edit.java b/patches/minecraft/net/minecraft/world/chunk/storage/RegionFile.edit.java index 4edc466..ee76f23 100644 --- a/patches/minecraft/net/minecraft/world/chunk/storage/RegionFile.edit.java +++ b/patches/minecraft/net/minecraft/world/chunk/storage/RegionFile.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/demo/DemoWorldServer.edit.java b/patches/minecraft/net/minecraft/world/demo/DemoWorldServer.edit.java index 407f74b..96d6c25 100644 --- a/patches/minecraft/net/minecraft/world/demo/DemoWorldServer.edit.java +++ b/patches/minecraft/net/minecraft/world/demo/DemoWorldServer.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/ChunkProviderEnd.edit.java b/patches/minecraft/net/minecraft/world/gen/ChunkProviderEnd.edit.java index a516a7c..ed42287 100644 --- a/patches/minecraft/net/minecraft/world/gen/ChunkProviderEnd.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/ChunkProviderEnd.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/ChunkProviderFlat.edit.java b/patches/minecraft/net/minecraft/world/gen/ChunkProviderFlat.edit.java index 1229f6c..05157c7 100644 --- a/patches/minecraft/net/minecraft/world/gen/ChunkProviderFlat.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/ChunkProviderFlat.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/ChunkProviderGenerate.edit.java b/patches/minecraft/net/minecraft/world/gen/ChunkProviderGenerate.edit.java index 92cb66b..3284fdb 100644 --- a/patches/minecraft/net/minecraft/world/gen/ChunkProviderGenerate.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/ChunkProviderGenerate.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/ChunkProviderHell.edit.java b/patches/minecraft/net/minecraft/world/gen/ChunkProviderHell.edit.java index eb8b4d2..add0bf2 100644 --- a/patches/minecraft/net/minecraft/world/gen/ChunkProviderHell.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/ChunkProviderHell.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.edit.java b/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.edit.java index b416766..6b23f6d 100644 --- a/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.edit.java @@ -1,15 +1,28 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> INSERT 11 : 12 @ 11 +> INSERT 2 : 6 @ 2 + ++ import com.carrotsearch.hppc.LongHashSet; ++ import com.carrotsearch.hppc.LongObjectHashMap; ++ import com.carrotsearch.hppc.LongObjectMap; ++ import com.carrotsearch.hppc.LongSet; + +> DELETE 3 @ 3 : 4 + +> DELETE 1 @ 1 : 3 + +> INSERT 2 : 3 @ 2 + import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerMinecraftServer; -> DELETE 6 @ 6 : 7 +> DELETE 3 @ 3 : 4 + +> DELETE 2 @ 2 : 3 > CHANGE 7 : 10 @ 7 : 9 @@ -17,11 +30,37 @@ ~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; -> CHANGE 9 : 10 @ 9 : 10 +> CHANGE 3 : 4 @ 3 : 4 +~ private LongSet droppedChunksSet = new LongHashSet(); + +> CHANGE 4 : 6 @ 4 : 6 + +~ private LongObjectMap id2ChunkMap = new LongObjectHashMap<>(); ~ private List loadedChunks = Lists.newLinkedList(); -> INSERT 48 : 49 @ 48 +> CHANGE 4 : 5 @ 4 : 5 + +~ this.dummyChunk = new EmptyChunk(parWorldServer, Integer.MIN_VALUE, Integer.MIN_VALUE); + +> CHANGE 6 : 7 @ 6 : 7 + +~ return this.id2ChunkMap.containsKey(ChunkCoordIntPair.chunkXZ2Int(i, j)); + +> CHANGE 9 : 10 @ 9 : 10 + +~ this.droppedChunksSet.add(ChunkCoordIntPair.chunkXZ2Int(parInt1, parInt2)); + +> CHANGE 2 : 3 @ 2 : 3 + +~ this.droppedChunksSet.add(ChunkCoordIntPair.chunkXZ2Int(parInt1, parInt2)); + +> CHANGE 13 : 15 @ 13 : 15 + +~ this.droppedChunksSet.removeAll(k); +~ Chunk chunk = this.id2ChunkMap.get(k); + +> INSERT 8 : 9 @ 8 + ++EaglerMinecraftServer.counterChunkGenerate; @@ -34,7 +73,15 @@ + } else { + ++EaglerMinecraftServer.counterChunkRead; -> CHANGE 32 : 34 @ 32 : 33 +> CHANGE 2 : 3 @ 2 : 3 + +~ this.id2ChunkMap.put(k, chunk); + +> CHANGE 9 : 10 @ 9 : 10 + +~ Chunk chunk = this.id2ChunkMap.get(ChunkCoordIntPair.chunkXZ2Int(i, j)); + +> CHANGE 19 : 21 @ 19 : 20 ~ logger.error("Couldn\'t load chunk"); ~ logger.error(exception); @@ -59,4 +106,25 @@ ~ for (int j = 0, l = arraylist.size(); j < l; ++j) { +> CHANGE 29 : 31 @ 29 : 31 + +~ long olong = this.droppedChunksSet.iterator().next().value; +~ Chunk chunk = this.id2ChunkMap.get(olong); + +> CHANGE 4 : 5 @ 4 : 5 + +~ this.id2ChunkMap.remove(olong); + +> CHANGE 3 : 4 @ 3 : 4 + +~ this.droppedChunksSet.removeAll(olong); + +> CHANGE 16 : 17 @ 16 : 17 + +~ return "ServerChunkCache: " + this.id2ChunkMap.size() + " Drop: " + this.droppedChunksSet.size(); + +> CHANGE 12 : 13 @ 12 : 13 + +~ return this.id2ChunkMap.size(); + > EOF diff --git a/patches/minecraft/net/minecraft/world/gen/ChunkProviderSettings.edit.java b/patches/minecraft/net/minecraft/world/gen/ChunkProviderSettings.edit.java index f7151b4..6de79da 100644 --- a/patches/minecraft/net/minecraft/world/gen/ChunkProviderSettings.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/ChunkProviderSettings.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/FlatGeneratorInfo.edit.java b/patches/minecraft/net/minecraft/world/gen/FlatGeneratorInfo.edit.java index 2c4f328..f98b4dc 100644 --- a/patches/minecraft/net/minecraft/world/gen/FlatGeneratorInfo.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/FlatGeneratorInfo.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/GeneratorBushFeature.edit.java b/patches/minecraft/net/minecraft/world/gen/GeneratorBushFeature.edit.java index 6be8807..d434dde 100644 --- a/patches/minecraft/net/minecraft/world/gen/GeneratorBushFeature.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/GeneratorBushFeature.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/MapGenBase.edit.java b/patches/minecraft/net/minecraft/world/gen/MapGenBase.edit.java index e80100d..aa93abd 100644 --- a/patches/minecraft/net/minecraft/world/gen/MapGenBase.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/MapGenBase.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/MapGenCaves.edit.java b/patches/minecraft/net/minecraft/world/gen/MapGenCaves.edit.java index 5ef387b..57f6781 100644 --- a/patches/minecraft/net/minecraft/world/gen/MapGenCaves.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/MapGenCaves.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/MapGenCavesHell.edit.java b/patches/minecraft/net/minecraft/world/gen/MapGenCavesHell.edit.java index cb88eab..d18e23b 100644 --- a/patches/minecraft/net/minecraft/world/gen/MapGenCavesHell.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/MapGenCavesHell.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/MapGenRavine.edit.java b/patches/minecraft/net/minecraft/world/gen/MapGenRavine.edit.java index d481373..a9195fc 100644 --- a/patches/minecraft/net/minecraft/world/gen/MapGenRavine.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/MapGenRavine.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorImproved.edit.java b/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorImproved.edit.java index e417916..ffe046a 100644 --- a/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorImproved.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorImproved.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorOctaves.edit.java b/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorOctaves.edit.java index 05d96ca..0395105 100644 --- a/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorOctaves.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorOctaves.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorPerlin.edit.java b/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorPerlin.edit.java index 3f3aaae..63815c3 100644 --- a/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorPerlin.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorPerlin.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorSimplex.edit.java b/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorSimplex.edit.java index 34ae302..2ec92e6 100644 --- a/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorSimplex.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorSimplex.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenAbstractTree.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenAbstractTree.edit.java index 89c0a2a..43f608e 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenAbstractTree.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenAbstractTree.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenBigMushroom.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenBigMushroom.edit.java index 96a1272..970a6e4 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenBigMushroom.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenBigMushroom.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenBigTree.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenBigTree.edit.java index dfe47f4..c5f0445 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenBigTree.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenBigTree.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenBlockBlob.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenBlockBlob.edit.java index 0b2dc25..38890ef 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenBlockBlob.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenBlockBlob.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenCactus.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenCactus.edit.java index 6ed184f..ce3bd99 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenCactus.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenCactus.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenCanopyTree.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenCanopyTree.edit.java index 10004dd..71a8bd2 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenCanopyTree.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenCanopyTree.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenClay.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenClay.edit.java index bcf0a12..cc577c6 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenClay.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenClay.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenDeadBush.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenDeadBush.edit.java index dc816e1..6c0cf5b 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenDeadBush.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenDeadBush.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenDesertWells.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenDesertWells.edit.java index 5875215..02b2d5a 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenDesertWells.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenDesertWells.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenDoublePlant.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenDoublePlant.edit.java index f8454de..167d44c 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenDoublePlant.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenDoublePlant.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenDungeons.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenDungeons.edit.java index 15c5240..fb38115 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenDungeons.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenDungeons.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenFire.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenFire.edit.java index 6ed184f..ce3bd99 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenFire.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenFire.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenFlowers.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenFlowers.edit.java index 784a811..c543540 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenFlowers.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenFlowers.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenForest.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenForest.edit.java index a4e498f..486295a 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenForest.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenForest.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenGlowStone1.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenGlowStone1.edit.java index d66c32a..ba11f52 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenGlowStone1.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenGlowStone1.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenGlowStone2.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenGlowStone2.edit.java index d66c32a..ba11f52 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenGlowStone2.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenGlowStone2.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenHellLava.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenHellLava.edit.java index 4783ecc..0d133cd 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenHellLava.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenHellLava.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenHugeTrees.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenHugeTrees.edit.java index 0fb8853..cd2ce9f 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenHugeTrees.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenHugeTrees.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenIcePath.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenIcePath.edit.java index 6c38fb5..bf4fe58 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenIcePath.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenIcePath.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenIceSpike.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenIceSpike.edit.java index d1f9e3e..3aaca4f 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenIceSpike.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenIceSpike.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenLakes.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenLakes.edit.java index b7cc8a1..31b6996 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenLakes.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenLakes.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenLiquids.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenLiquids.edit.java index 4727abe..06377cb 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenLiquids.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenLiquids.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenMegaJungle.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenMegaJungle.edit.java index f833b0a..89b0944 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenMegaJungle.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenMegaJungle.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenMegaPineTree.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenMegaPineTree.edit.java index 0bc8a06..d1abd0a 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenMegaPineTree.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenMegaPineTree.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenMelon.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenMelon.edit.java index 6ed184f..ce3bd99 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenMelon.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenMelon.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenMinable.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenMinable.edit.java index 666e47d..0c0d3a5 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenMinable.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenMinable.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenPumpkin.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenPumpkin.edit.java index dc816e1..6c0cf5b 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenPumpkin.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenPumpkin.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenReed.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenReed.edit.java index 35e9533..f78f2fc 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenReed.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenReed.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenSand.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenSand.edit.java index 4783ecc..0d133cd 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenSand.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenSand.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenSavannaTree.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenSavannaTree.edit.java index 4c78ae4..20c871f 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenSavannaTree.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenSavannaTree.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenShrub.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenShrub.edit.java index 7271ad3..dea395f 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenShrub.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenShrub.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenSpikes.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenSpikes.edit.java index aac3d69..edc61ed 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenSpikes.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenSpikes.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenSwamp.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenSwamp.edit.java index 8e4c2bf..ae3034a 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenSwamp.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenSwamp.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenTaiga1.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenTaiga1.edit.java index 7307595..6c0786c 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenTaiga1.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenTaiga1.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenTaiga2.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenTaiga2.edit.java index 7307595..6c0786c 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenTaiga2.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenTaiga2.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenTallGrass.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenTallGrass.edit.java index b7cc8a1..31b6996 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenTallGrass.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenTallGrass.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenTrees.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenTrees.edit.java index c89a63d..448f4f6 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenTrees.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenTrees.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenVines.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenVines.edit.java index 298f764..4292745 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenVines.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenVines.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenWaterlily.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenWaterlily.edit.java index 6ed184f..ce3bd99 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenWaterlily.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenWaterlily.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenerator.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenerator.edit.java index 2e66a5d..ca82617 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGenerator.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGenerator.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/feature/WorldGeneratorBonusChest.edit.java b/patches/minecraft/net/minecraft/world/gen/feature/WorldGeneratorBonusChest.edit.java index fb068b3..1a6eb4d 100644 --- a/patches/minecraft/net/minecraft/world/gen/feature/WorldGeneratorBonusChest.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/feature/WorldGeneratorBonusChest.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayer.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayer.edit.java index 4614df5..5853cdb 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayer.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayer.edit.java @@ -1,14 +1,33 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> DELETE 9 @ 9 : 28 +> INSERT 3 : 5 @ 3 -> CHANGE 46 : 47 @ 46 : 47 ++ ++ import net.lax1dude.eaglercraft.v1_8.sp.server.GenLayerEaglerRivers; + +> DELETE 6 @ 6 : 25 + +> CHANGE 2 : 3 @ 2 : 3 + +~ protected long worldGenSeed; + +> CHANGE 43 : 44 @ 43 : 44 ~ GenLayer genlayerhills = new GenLayerHills(1000L, genlayerbiomeedge, genlayer1); +> CHANGE 2 : 5 @ 2 : 4 + +~ GenLayer genlayerriver = new GenLayerRiver(1L, genlayer3); +~ genlayerriver = new GenLayerSmooth(1000L, genlayerriver); +~ genlayerriver = new GenLayerEaglerRivers(69L, genlayerriver); + +> CHANGE 14 : 15 @ 14 : 15 + +~ GenLayerRiverMix genlayerrivermix = new GenLayerRiverMix(100L, genlayersmooth1, genlayerriver); + > EOF diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerAddIsland.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerAddIsland.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerAddIsland.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerAddIsland.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerAddMushroomIsland.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerAddMushroomIsland.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerAddMushroomIsland.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerAddMushroomIsland.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerAddSnow.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerAddSnow.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerAddSnow.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerAddSnow.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerBiome.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerBiome.edit.java index 41da09f..86bf348 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerBiome.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerBiome.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerBiomeEdge.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerBiomeEdge.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerBiomeEdge.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerBiomeEdge.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerDeepOcean.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerDeepOcean.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerDeepOcean.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerDeepOcean.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerEdge.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerEdge.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerEdge.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerEdge.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerFuzzyZoom.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerFuzzyZoom.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerFuzzyZoom.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerFuzzyZoom.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerHills.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerHills.edit.java index 4651b11..d8c247b 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerHills.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerHills.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerIsland.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerIsland.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerIsland.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerIsland.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRareBiome.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRareBiome.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRareBiome.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRareBiome.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRemoveTooMuchOcean.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRemoveTooMuchOcean.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRemoveTooMuchOcean.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRemoveTooMuchOcean.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRiver.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRiver.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRiver.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRiver.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRiverInit.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRiverInit.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRiverInit.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRiverInit.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRiverMix.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRiverMix.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRiverMix.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerRiverMix.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerShore.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerShore.edit.java index 41da09f..86bf348 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerShore.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerShore.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerSmooth.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerSmooth.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerSmooth.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerSmooth.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerVoronoiZoom.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerVoronoiZoom.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerVoronoiZoom.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerVoronoiZoom.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerZoom.edit.java b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerZoom.edit.java index 0c65978..90aedbf 100644 --- a/patches/minecraft/net/minecraft/world/gen/layer/GenLayerZoom.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/layer/GenLayerZoom.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/structure/ComponentScatteredFeaturePieces.edit.java b/patches/minecraft/net/minecraft/world/gen/structure/ComponentScatteredFeaturePieces.edit.java index 1252392..a687dbe 100644 --- a/patches/minecraft/net/minecraft/world/gen/structure/ComponentScatteredFeaturePieces.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/structure/ComponentScatteredFeaturePieces.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/structure/MapGenMineshaft.edit.java b/patches/minecraft/net/minecraft/world/gen/structure/MapGenMineshaft.edit.java index 466eb65..752118e 100644 --- a/patches/minecraft/net/minecraft/world/gen/structure/MapGenMineshaft.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/structure/MapGenMineshaft.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/structure/MapGenNetherBridge.edit.java b/patches/minecraft/net/minecraft/world/gen/structure/MapGenNetherBridge.edit.java index 3b6005e..bcd448e 100644 --- a/patches/minecraft/net/minecraft/world/gen/structure/MapGenNetherBridge.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/structure/MapGenNetherBridge.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/structure/MapGenScatteredFeature.edit.java b/patches/minecraft/net/minecraft/world/gen/structure/MapGenScatteredFeature.edit.java index ee1230e..36202d1 100644 --- a/patches/minecraft/net/minecraft/world/gen/structure/MapGenScatteredFeature.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/structure/MapGenScatteredFeature.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/structure/MapGenStronghold.edit.java b/patches/minecraft/net/minecraft/world/gen/structure/MapGenStronghold.edit.java index c597da1..648d206 100644 --- a/patches/minecraft/net/minecraft/world/gen/structure/MapGenStronghold.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/structure/MapGenStronghold.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/structure/MapGenStructure.edit.java b/patches/minecraft/net/minecraft/world/gen/structure/MapGenStructure.edit.java index f0081ac..3921092 100644 --- a/patches/minecraft/net/minecraft/world/gen/structure/MapGenStructure.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/structure/MapGenStructure.edit.java @@ -1,15 +1,18 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude -> INSERT 3 : 4 @ 3 +> CHANGE 2 : 6 @ 2 : 3 -+ +~ import com.carrotsearch.hppc.LongObjectHashMap; +~ import com.carrotsearch.hppc.LongObjectMap; +~ import com.carrotsearch.hppc.cursors.ObjectCursor; +~ -> CHANGE 3 : 6 @ 3 : 4 +> CHANGE 2 : 5 @ 2 : 4 ~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; ~ import net.lax1dude.eaglercraft.v1_8.HString; @@ -17,7 +20,11 @@ > DELETE 11 @ 11 : 16 -> INSERT 7 : 15 @ 7 +> CHANGE 3 : 4 @ 3 : 4 + +~ protected LongObjectMap structureMap = new LongObjectHashMap<>(); + +> INSERT 3 : 11 @ 3 + public MapGenStructure() { + super(); @@ -28,7 +35,15 @@ + } + -> CHANGE 23 : 24 @ 23 : 24 +> CHANGE 3 : 4 @ 3 : 4 + +~ if (!this.structureMap.containsKey(ChunkCoordIntPair.chunkXZ2Int(i, j))) { + +> CHANGE 5 : 6 @ 5 : 6 + +~ this.structureMap.put(ChunkCoordIntPair.chunkXZ2Int(i, j), structurestart); + +> CHANGE 13 : 14 @ 13 : 14 ~ HString.format("%d,%d", new Object[] { Integer.valueOf(i), Integer.valueOf(j) })); @@ -36,7 +51,27 @@ ~ public boolean generateStructure(World worldIn, EaglercraftRandom randomIn, ChunkCoordIntPair chunkCoord) { -> CHANGE 86 : 87 @ 86 : 87 +> CHANGE 5 : 7 @ 5 : 6 + +~ for (ObjectCursor structurestart_ : this.structureMap.values()) { +~ StructureStart structurestart = structurestart_.value; + +> CHANGE 18 : 20 @ 18 : 19 + +~ label24: for (ObjectCursor structurestart_ : this.structureMap.values()) { +~ StructureStart structurestart = structurestart_.value; + +> CHANGE 24 : 26 @ 24 : 25 + +~ for (ObjectCursor structurestart_ : this.structureMap.values()) { +~ StructureStart structurestart = structurestart_.value; + +> CHANGE 21 : 23 @ 21 : 22 + +~ for (ObjectCursor structurestart_ : this.structureMap.values()) { +~ StructureStart structurestart = structurestart_.value; + +> CHANGE 14 : 15 @ 14 : 15 ~ List list = this.getCoordList(); @@ -45,4 +80,8 @@ ~ for (int m = 0, n = list.size(); m < n; ++m) { ~ BlockPos blockpos3 = list.get(m); +> CHANGE 38 : 39 @ 38 : 40 + +~ this.structureMap.put(ChunkCoordIntPair.chunkXZ2Int(i, j), structurestart); + > EOF diff --git a/patches/minecraft/net/minecraft/world/gen/structure/MapGenStructureIO.edit.java b/patches/minecraft/net/minecraft/world/gen/structure/MapGenStructureIO.edit.java index 5b2936d..0d6b450 100644 --- a/patches/minecraft/net/minecraft/world/gen/structure/MapGenStructureIO.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/structure/MapGenStructureIO.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/structure/MapGenVillage.edit.java b/patches/minecraft/net/minecraft/world/gen/structure/MapGenVillage.edit.java index daff0b1..06a025a 100644 --- a/patches/minecraft/net/minecraft/world/gen/structure/MapGenVillage.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/structure/MapGenVillage.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/structure/StructureComponent.edit.java b/patches/minecraft/net/minecraft/world/gen/structure/StructureComponent.edit.java index 2ef376f..7d35e5e 100644 --- a/patches/minecraft/net/minecraft/world/gen/structure/StructureComponent.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/structure/StructureComponent.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/structure/StructureMineshaftPieces.edit.java b/patches/minecraft/net/minecraft/world/gen/structure/StructureMineshaftPieces.edit.java index d18a8ea..9a3ff15 100644 --- a/patches/minecraft/net/minecraft/world/gen/structure/StructureMineshaftPieces.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/structure/StructureMineshaftPieces.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/structure/StructureMineshaftStart.edit.java b/patches/minecraft/net/minecraft/world/gen/structure/StructureMineshaftStart.edit.java index d31607e..d660f1c 100644 --- a/patches/minecraft/net/minecraft/world/gen/structure/StructureMineshaftStart.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/structure/StructureMineshaftStart.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/structure/StructureNetherBridgePieces.edit.java b/patches/minecraft/net/minecraft/world/gen/structure/StructureNetherBridgePieces.edit.java index 81cad20..ed2ab5d 100644 --- a/patches/minecraft/net/minecraft/world/gen/structure/StructureNetherBridgePieces.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/structure/StructureNetherBridgePieces.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/structure/StructureOceanMonument.edit.java b/patches/minecraft/net/minecraft/world/gen/structure/StructureOceanMonument.edit.java index c004a4b..b700af6 100644 --- a/patches/minecraft/net/minecraft/world/gen/structure/StructureOceanMonument.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/structure/StructureOceanMonument.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/structure/StructureOceanMonumentPieces.edit.java b/patches/minecraft/net/minecraft/world/gen/structure/StructureOceanMonumentPieces.edit.java index 9f51920..fbd5063 100644 --- a/patches/minecraft/net/minecraft/world/gen/structure/StructureOceanMonumentPieces.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/structure/StructureOceanMonumentPieces.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/structure/StructureStart.edit.java b/patches/minecraft/net/minecraft/world/gen/structure/StructureStart.edit.java index 9a58add..0209a10 100644 --- a/patches/minecraft/net/minecraft/world/gen/structure/StructureStart.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/structure/StructureStart.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/structure/StructureStrongholdPieces.edit.java b/patches/minecraft/net/minecraft/world/gen/structure/StructureStrongholdPieces.edit.java index 049cc51..8349fb3 100644 --- a/patches/minecraft/net/minecraft/world/gen/structure/StructureStrongholdPieces.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/structure/StructureStrongholdPieces.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/gen/structure/StructureVillagePieces.edit.java b/patches/minecraft/net/minecraft/world/gen/structure/StructureVillagePieces.edit.java index b23cbb6..9e3a74f 100644 --- a/patches/minecraft/net/minecraft/world/gen/structure/StructureVillagePieces.edit.java +++ b/patches/minecraft/net/minecraft/world/gen/structure/StructureVillagePieces.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/pathfinder/NodeProcessor.edit.java b/patches/minecraft/net/minecraft/world/pathfinder/NodeProcessor.edit.java new file mode 100644 index 0000000..9e13fae --- /dev/null +++ b/patches/minecraft/net/minecraft/world/pathfinder/NodeProcessor.edit.java @@ -0,0 +1,32 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2025 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 5 @ 2 + ++ import com.carrotsearch.hppc.IntObjectHashMap; ++ import com.carrotsearch.hppc.IntObjectMap; ++ + +> DELETE 2 @ 2 : 3 + +> CHANGE 5 : 6 @ 5 : 6 + +~ protected IntObjectMap pointMap = new IntObjectHashMap<>(); + +> CHANGE 6 : 7 @ 6 : 7 + +~ this.pointMap.clear(); + +> CHANGE 10 : 11 @ 10 : 11 + +~ PathPoint pathpoint = this.pointMap.get(i); + +> CHANGE 2 : 3 @ 2 : 3 + +~ this.pointMap.put(i, pathpoint); + +> EOF diff --git a/patches/minecraft/net/minecraft/world/pathfinder/SwimNodeProcessor.edit.java b/patches/minecraft/net/minecraft/world/pathfinder/SwimNodeProcessor.edit.java index f014806..fc62817 100644 --- a/patches/minecraft/net/minecraft/world/pathfinder/SwimNodeProcessor.edit.java +++ b/patches/minecraft/net/minecraft/world/pathfinder/SwimNodeProcessor.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/pathfinder/WalkNodeProcessor.edit.java b/patches/minecraft/net/minecraft/world/pathfinder/WalkNodeProcessor.edit.java index fb31754..99a93b4 100644 --- a/patches/minecraft/net/minecraft/world/pathfinder/WalkNodeProcessor.edit.java +++ b/patches/minecraft/net/minecraft/world/pathfinder/WalkNodeProcessor.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/storage/DerivedWorldInfo.edit.java b/patches/minecraft/net/minecraft/world/storage/DerivedWorldInfo.edit.java index d9711e3..944d4b4 100644 --- a/patches/minecraft/net/minecraft/world/storage/DerivedWorldInfo.edit.java +++ b/patches/minecraft/net/minecraft/world/storage/DerivedWorldInfo.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/storage/ISaveFormat.edit.java b/patches/minecraft/net/minecraft/world/storage/ISaveFormat.edit.java index d3f078b..0520692 100644 --- a/patches/minecraft/net/minecraft/world/storage/ISaveFormat.edit.java +++ b/patches/minecraft/net/minecraft/world/storage/ISaveFormat.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/storage/ISaveHandler.edit.java b/patches/minecraft/net/minecraft/world/storage/ISaveHandler.edit.java index 6a76531..9fd3148 100644 --- a/patches/minecraft/net/minecraft/world/storage/ISaveHandler.edit.java +++ b/patches/minecraft/net/minecraft/world/storage/ISaveHandler.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/storage/MapData.edit.java b/patches/minecraft/net/minecraft/world/storage/MapData.edit.java index 7cdf874..2c5d62b 100644 --- a/patches/minecraft/net/minecraft/world/storage/MapData.edit.java +++ b/patches/minecraft/net/minecraft/world/storage/MapData.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/storage/MapStorage.edit.java b/patches/minecraft/net/minecraft/world/storage/MapStorage.edit.java index cb10e24..fda0d97 100644 --- a/patches/minecraft/net/minecraft/world/storage/MapStorage.edit.java +++ b/patches/minecraft/net/minecraft/world/storage/MapStorage.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/storage/SaveDataMemoryStorage.edit.java b/patches/minecraft/net/minecraft/world/storage/SaveDataMemoryStorage.edit.java index 831f1a2..f89425a 100644 --- a/patches/minecraft/net/minecraft/world/storage/SaveDataMemoryStorage.edit.java +++ b/patches/minecraft/net/minecraft/world/storage/SaveDataMemoryStorage.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/storage/SaveFormatComparator.edit.java b/patches/minecraft/net/minecraft/world/storage/SaveFormatComparator.edit.java index 4381b07..45e0dfa 100644 --- a/patches/minecraft/net/minecraft/world/storage/SaveFormatComparator.edit.java +++ b/patches/minecraft/net/minecraft/world/storage/SaveFormatComparator.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/storage/SaveFormatOld.edit.java b/patches/minecraft/net/minecraft/world/storage/SaveFormatOld.edit.java index 20cf49d..b7251d4 100644 --- a/patches/minecraft/net/minecraft/world/storage/SaveFormatOld.edit.java +++ b/patches/minecraft/net/minecraft/world/storage/SaveFormatOld.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/storage/SaveHandler.edit.java b/patches/minecraft/net/minecraft/world/storage/SaveHandler.edit.java index bc69356..a376c78 100644 --- a/patches/minecraft/net/minecraft/world/storage/SaveHandler.edit.java +++ b/patches/minecraft/net/minecraft/world/storage/SaveHandler.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/storage/SaveHandlerMP.edit.java b/patches/minecraft/net/minecraft/world/storage/SaveHandlerMP.edit.java index 8b1964f..a872dae 100644 --- a/patches/minecraft/net/minecraft/world/storage/SaveHandlerMP.edit.java +++ b/patches/minecraft/net/minecraft/world/storage/SaveHandlerMP.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/net/minecraft/world/storage/WorldInfo.edit.java b/patches/minecraft/net/minecraft/world/storage/WorldInfo.edit.java index c03cd0f..b95128d 100644 --- a/patches/minecraft/net/minecraft/world/storage/WorldInfo.edit.java +++ b/patches/minecraft/net/minecraft/world/storage/WorldInfo.edit.java @@ -1,6 +1,6 @@ # Eagler Context Redacted Diff -# Copyright (c) 2024 lax1dude. All rights reserved. +# Copyright (c) 2025 lax1dude. All rights reserved. # Version: 1.0 # Author: lax1dude diff --git a/patches/minecraft/output_license.txt b/patches/minecraft/output_license.txt index ee11a9c..c207417 100644 --- a/patches/minecraft/output_license.txt +++ b/patches/minecraft/output_license.txt @@ -3,7 +3,7 @@ This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team -EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. +EaglercraftX 1.8 patch files (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 diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java index 676d40d..13c4c62 100644 --- a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java @@ -18,9 +18,12 @@ import java.util.Date; import java.util.List; import java.util.Random; import java.util.function.Consumer; +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; +import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; import javax.imageio.ImageIO; @@ -556,6 +559,16 @@ public class PlatformRuntime { return new DeflaterOutputStream(os); } + public static int deflateFull(byte[] input, int inputOff, int inputLen, byte[] output, int outputOff, + int outputLen) throws IOException { + Deflater df = new Deflater(); + df.setInput(input, inputOff, inputLen); + df.finish(); + int i = df.deflate(output, outputOff, outputLen); + df.end(); + return i; + } + public static OutputStream newGZIPOutputStream(OutputStream os) throws IOException { return new GZIPOutputStream(os); } @@ -564,6 +577,21 @@ public class PlatformRuntime { return new InflaterInputStream(is); } + public static int inflateFull(byte[] input, int inputOff, int inputLen, byte[] output, int outputOff, + int outputLen) throws IOException { + Inflater df = new Inflater(); + int i; + try { + df.setInput(input, inputOff, inputLen); + i = df.inflate(output, outputOff, outputLen); + }catch(DataFormatException ex) { + throw new IOException("Failed to inflate!", ex); + }finally { + df.end(); + } + return i; + } + public static InputStream newGZIPInputStream(InputStream is) throws IOException { return new GZIPInputStream(is); } diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLByteBuffer.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLByteBuffer.java index 2259388..1e3afac 100644 --- a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLByteBuffer.java +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLByteBuffer.java @@ -4,7 +4,7 @@ import net.lax1dude.unsafememcpy.UnsafeMemcpy; import net.lax1dude.unsafememcpy.UnsafeUtils; /** - * 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 @@ -18,7 +18,7 @@ import net.lax1dude.unsafememcpy.UnsafeUtils; * POSSIBILITY OF SUCH DAMAGE. * */ -public class EaglerLWJGLByteBuffer implements ByteBuffer { +public class EaglerLWJGLByteBuffer extends ByteBuffer { final long address; final boolean original; diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLFloatBuffer.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLFloatBuffer.java index d08900a..370948a 100644 --- a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLFloatBuffer.java +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLFloatBuffer.java @@ -4,7 +4,7 @@ import net.lax1dude.unsafememcpy.UnsafeMemcpy; import net.lax1dude.unsafememcpy.UnsafeUtils; /** - * 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 @@ -18,7 +18,7 @@ import net.lax1dude.unsafememcpy.UnsafeUtils; * POSSIBILITY OF SUCH DAMAGE. * */ -public class EaglerLWJGLFloatBuffer implements FloatBuffer { +public class EaglerLWJGLFloatBuffer extends FloatBuffer { final long address; final boolean original; diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLIntBuffer.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLIntBuffer.java index 10df219..8f13bb2 100644 --- a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLIntBuffer.java +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLIntBuffer.java @@ -4,7 +4,7 @@ import net.lax1dude.unsafememcpy.UnsafeMemcpy; import net.lax1dude.unsafememcpy.UnsafeUtils; /** - * 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 @@ -18,7 +18,7 @@ import net.lax1dude.unsafememcpy.UnsafeUtils; * POSSIBILITY OF SUCH DAMAGE. * */ -public class EaglerLWJGLIntBuffer implements IntBuffer { +public class EaglerLWJGLIntBuffer extends IntBuffer { final long address; final boolean original; diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLShortBuffer.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLShortBuffer.java index 01f3152..241f4a0 100644 --- a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLShortBuffer.java +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLShortBuffer.java @@ -4,7 +4,7 @@ import net.lax1dude.unsafememcpy.UnsafeMemcpy; import net.lax1dude.unsafememcpy.UnsafeUtils; /** - * 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 @@ -18,7 +18,7 @@ import net.lax1dude.unsafememcpy.UnsafeUtils; * POSSIBILITY OF SUCH DAMAGE. * */ -public class EaglerLWJGLShortBuffer implements ShortBuffer { +public class EaglerLWJGLShortBuffer extends ShortBuffer { final long address; final boolean original; diff --git a/sources/main/java/com/carrotsearch/hppc/AbstractByteCollection.java b/sources/main/java/com/carrotsearch/hppc/AbstractByteCollection.java new file mode 100644 index 0000000..ed18435 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/AbstractByteCollection.java @@ -0,0 +1,51 @@ +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/sources/main/java/com/carrotsearch/hppc/AbstractCharCollection.java b/sources/main/java/com/carrotsearch/hppc/AbstractCharCollection.java new file mode 100644 index 0000000..5ecdbb5 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/AbstractCharCollection.java @@ -0,0 +1,51 @@ +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/sources/main/java/com/carrotsearch/hppc/AbstractDoubleCollection.java b/sources/main/java/com/carrotsearch/hppc/AbstractDoubleCollection.java new file mode 100644 index 0000000..2bb296f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/AbstractDoubleCollection.java @@ -0,0 +1,51 @@ +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/sources/main/java/com/carrotsearch/hppc/AbstractFloatCollection.java b/sources/main/java/com/carrotsearch/hppc/AbstractFloatCollection.java new file mode 100644 index 0000000..089086f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/AbstractFloatCollection.java @@ -0,0 +1,51 @@ +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/sources/main/java/com/carrotsearch/hppc/AbstractIntCollection.java b/sources/main/java/com/carrotsearch/hppc/AbstractIntCollection.java new file mode 100644 index 0000000..e9ae748 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/AbstractIntCollection.java @@ -0,0 +1,50 @@ +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/sources/main/java/com/carrotsearch/hppc/AbstractIterator.java b/sources/main/java/com/carrotsearch/hppc/AbstractIterator.java new file mode 100644 index 0000000..7895140 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/AbstractIterator.java @@ -0,0 +1,71 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/AbstractLongCollection.java b/sources/main/java/com/carrotsearch/hppc/AbstractLongCollection.java new file mode 100644 index 0000000..914bc36 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/AbstractLongCollection.java @@ -0,0 +1,51 @@ +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/sources/main/java/com/carrotsearch/hppc/AbstractObjectCollection.java b/sources/main/java/com/carrotsearch/hppc/AbstractObjectCollection.java new file mode 100644 index 0000000..1a841f6 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/AbstractObjectCollection.java @@ -0,0 +1,66 @@ +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/sources/main/java/com/carrotsearch/hppc/AbstractShortCollection.java b/sources/main/java/com/carrotsearch/hppc/AbstractShortCollection.java new file mode 100644 index 0000000..e78dbe1 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/AbstractShortCollection.java @@ -0,0 +1,51 @@ +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/sources/main/java/com/carrotsearch/hppc/Accountable.java b/sources/main/java/com/carrotsearch/hppc/Accountable.java new file mode 100644 index 0000000..44aefc9 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/Accountable.java @@ -0,0 +1,31 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/ArraySizingStrategy.java b/sources/main/java/com/carrotsearch/hppc/ArraySizingStrategy.java new file mode 100644 index 0000000..b541c56 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ArraySizingStrategy.java @@ -0,0 +1,27 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/BitMixer.java b/sources/main/java/com/carrotsearch/hppc/BitMixer.java new file mode 100644 index 0000000..136c12f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/BitMixer.java @@ -0,0 +1,120 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/BitSet.java b/sources/main/java/com/carrotsearch/hppc/BitSet.java new file mode 100644 index 0000000..7f8a94b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/BitSet.java @@ -0,0 +1,952 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/BitSetIterator.java b/sources/main/java/com/carrotsearch/hppc/BitSetIterator.java new file mode 100644 index 0000000..35aa3e0 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/BitSetIterator.java @@ -0,0 +1,356 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/BitUtil.java b/sources/main/java/com/carrotsearch/hppc/BitUtil.java new file mode 100644 index 0000000..61a628d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/BitUtil.java @@ -0,0 +1,98 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/BoundedProportionalArraySizingStrategy.java b/sources/main/java/com/carrotsearch/hppc/BoundedProportionalArraySizingStrategy.java new file mode 100644 index 0000000..14c5a9a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/BoundedProportionalArraySizingStrategy.java @@ -0,0 +1,115 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/BufferAllocationException.java b/sources/main/java/com/carrotsearch/hppc/BufferAllocationException.java new file mode 100644 index 0000000..f09ef3c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/BufferAllocationException.java @@ -0,0 +1,41 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/ByteArrayDeque.java b/sources/main/java/com/carrotsearch/hppc/ByteArrayDeque.java new file mode 100644 index 0000000..b9c809c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ByteArrayDeque.java @@ -0,0 +1,776 @@ +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/sources/main/java/com/carrotsearch/hppc/ByteArrayList.java b/sources/main/java/com/carrotsearch/hppc/ByteArrayList.java new file mode 100644 index 0000000..836c6b9 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ByteArrayList.java @@ -0,0 +1,579 @@ +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/sources/main/java/com/carrotsearch/hppc/ByteBufferVisualizer.java b/sources/main/java/com/carrotsearch/hppc/ByteBufferVisualizer.java new file mode 100644 index 0000000..0a39142 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ByteBufferVisualizer.java @@ -0,0 +1,33 @@ +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/sources/main/java/com/carrotsearch/hppc/ByteCollection.java b/sources/main/java/com/carrotsearch/hppc/ByteCollection.java new file mode 100644 index 0000000..2e89418 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ByteCollection.java @@ -0,0 +1,64 @@ +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/sources/main/java/com/carrotsearch/hppc/ByteContainer.java b/sources/main/java/com/carrotsearch/hppc/ByteContainer.java new file mode 100644 index 0000000..19edc86 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ByteContainer.java @@ -0,0 +1,76 @@ +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/sources/main/java/com/carrotsearch/hppc/ByteDeque.java b/sources/main/java/com/carrotsearch/hppc/ByteDeque.java new file mode 100644 index 0000000..3b8dd7f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ByteDeque.java @@ -0,0 +1,77 @@ +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/sources/main/java/com/carrotsearch/hppc/ByteIndexedContainer.java b/sources/main/java/com/carrotsearch/hppc/ByteIndexedContainer.java new file mode 100644 index 0000000..f70fb89 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ByteIndexedContainer.java @@ -0,0 +1,91 @@ +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/sources/main/java/com/carrotsearch/hppc/ByteLookupContainer.java b/sources/main/java/com/carrotsearch/hppc/ByteLookupContainer.java new file mode 100644 index 0000000..ac41bc0 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ByteLookupContainer.java @@ -0,0 +1,12 @@ +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/sources/main/java/com/carrotsearch/hppc/ByteStack.java b/sources/main/java/com/carrotsearch/hppc/ByteStack.java new file mode 100644 index 0000000..0cf3243 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ByteStack.java @@ -0,0 +1,137 @@ +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/sources/main/java/com/carrotsearch/hppc/CharArrayDeque.java b/sources/main/java/com/carrotsearch/hppc/CharArrayDeque.java new file mode 100644 index 0000000..e8aeb6a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharArrayDeque.java @@ -0,0 +1,776 @@ +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/sources/main/java/com/carrotsearch/hppc/CharArrayList.java b/sources/main/java/com/carrotsearch/hppc/CharArrayList.java new file mode 100644 index 0000000..90837d3 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharArrayList.java @@ -0,0 +1,579 @@ +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/sources/main/java/com/carrotsearch/hppc/CharBufferVisualizer.java b/sources/main/java/com/carrotsearch/hppc/CharBufferVisualizer.java new file mode 100644 index 0000000..7bd1743 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharBufferVisualizer.java @@ -0,0 +1,33 @@ +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/sources/main/java/com/carrotsearch/hppc/CharByteAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/CharByteAssociativeContainer.java new file mode 100644 index 0000000..a69b4be --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharByteAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/CharByteHashMap.java b/sources/main/java/com/carrotsearch/hppc/CharByteHashMap.java new file mode 100644 index 0000000..93dd3c8 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharByteHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/CharByteMap.java b/sources/main/java/com/carrotsearch/hppc/CharByteMap.java new file mode 100644 index 0000000..dcb4ef5 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharByteMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/CharCharAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/CharCharAssociativeContainer.java new file mode 100644 index 0000000..b7334bb --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharCharAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/CharCharHashMap.java b/sources/main/java/com/carrotsearch/hppc/CharCharHashMap.java new file mode 100644 index 0000000..509c1a2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharCharHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/CharCharMap.java b/sources/main/java/com/carrotsearch/hppc/CharCharMap.java new file mode 100644 index 0000000..c3690bb --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharCharMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/CharCollection.java b/sources/main/java/com/carrotsearch/hppc/CharCollection.java new file mode 100644 index 0000000..4cc8517 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharCollection.java @@ -0,0 +1,64 @@ +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/sources/main/java/com/carrotsearch/hppc/CharContainer.java b/sources/main/java/com/carrotsearch/hppc/CharContainer.java new file mode 100644 index 0000000..5977583 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharContainer.java @@ -0,0 +1,76 @@ +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/sources/main/java/com/carrotsearch/hppc/CharDeque.java b/sources/main/java/com/carrotsearch/hppc/CharDeque.java new file mode 100644 index 0000000..34ff5c9 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharDeque.java @@ -0,0 +1,77 @@ +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/sources/main/java/com/carrotsearch/hppc/CharDoubleAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/CharDoubleAssociativeContainer.java new file mode 100644 index 0000000..2d898f2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharDoubleAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/CharDoubleHashMap.java b/sources/main/java/com/carrotsearch/hppc/CharDoubleHashMap.java new file mode 100644 index 0000000..0a68bfa --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharDoubleHashMap.java @@ -0,0 +1,1082 @@ +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/sources/main/java/com/carrotsearch/hppc/CharDoubleMap.java b/sources/main/java/com/carrotsearch/hppc/CharDoubleMap.java new file mode 100644 index 0000000..2ba6e41 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharDoubleMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/CharFloatAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/CharFloatAssociativeContainer.java new file mode 100644 index 0000000..10069a3 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharFloatAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/CharFloatHashMap.java b/sources/main/java/com/carrotsearch/hppc/CharFloatHashMap.java new file mode 100644 index 0000000..0f4d6d8 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharFloatHashMap.java @@ -0,0 +1,1081 @@ +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/sources/main/java/com/carrotsearch/hppc/CharFloatMap.java b/sources/main/java/com/carrotsearch/hppc/CharFloatMap.java new file mode 100644 index 0000000..4bf7569 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharFloatMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/CharHashSet.java b/sources/main/java/com/carrotsearch/hppc/CharHashSet.java new file mode 100644 index 0000000..58ad8f2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharHashSet.java @@ -0,0 +1,787 @@ +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/sources/main/java/com/carrotsearch/hppc/CharIndexedContainer.java b/sources/main/java/com/carrotsearch/hppc/CharIndexedContainer.java new file mode 100644 index 0000000..9682452 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharIndexedContainer.java @@ -0,0 +1,91 @@ +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/sources/main/java/com/carrotsearch/hppc/CharIntAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/CharIntAssociativeContainer.java new file mode 100644 index 0000000..08c5d64 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharIntAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/CharIntHashMap.java b/sources/main/java/com/carrotsearch/hppc/CharIntHashMap.java new file mode 100644 index 0000000..aff7e50 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharIntHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/CharIntMap.java b/sources/main/java/com/carrotsearch/hppc/CharIntMap.java new file mode 100644 index 0000000..8c2f24e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharIntMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/CharLongAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/CharLongAssociativeContainer.java new file mode 100644 index 0000000..7999c34 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharLongAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/CharLongHashMap.java b/sources/main/java/com/carrotsearch/hppc/CharLongHashMap.java new file mode 100644 index 0000000..e1bea34 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharLongHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/CharLongMap.java b/sources/main/java/com/carrotsearch/hppc/CharLongMap.java new file mode 100644 index 0000000..fcbda2b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharLongMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/CharLookupContainer.java b/sources/main/java/com/carrotsearch/hppc/CharLookupContainer.java new file mode 100644 index 0000000..3e2fd0a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharLookupContainer.java @@ -0,0 +1,12 @@ +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/sources/main/java/com/carrotsearch/hppc/CharObjectAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/CharObjectAssociativeContainer.java new file mode 100644 index 0000000..806efa6 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharObjectAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/CharObjectHashMap.java b/sources/main/java/com/carrotsearch/hppc/CharObjectHashMap.java new file mode 100644 index 0000000..a9d9a0d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharObjectHashMap.java @@ -0,0 +1,1050 @@ +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/sources/main/java/com/carrotsearch/hppc/CharObjectMap.java b/sources/main/java/com/carrotsearch/hppc/CharObjectMap.java new file mode 100644 index 0000000..6a1004d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharObjectMap.java @@ -0,0 +1,181 @@ +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/sources/main/java/com/carrotsearch/hppc/CharSet.java b/sources/main/java/com/carrotsearch/hppc/CharSet.java new file mode 100644 index 0000000..a9740ea --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharSet.java @@ -0,0 +1,33 @@ +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/sources/main/java/com/carrotsearch/hppc/CharShortAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/CharShortAssociativeContainer.java new file mode 100644 index 0000000..66f6c44 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharShortAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/CharShortHashMap.java b/sources/main/java/com/carrotsearch/hppc/CharShortHashMap.java new file mode 100644 index 0000000..686281c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharShortHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/CharShortMap.java b/sources/main/java/com/carrotsearch/hppc/CharShortMap.java new file mode 100644 index 0000000..e5b0dee --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharShortMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/CharStack.java b/sources/main/java/com/carrotsearch/hppc/CharStack.java new file mode 100644 index 0000000..fb7a594 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/CharStack.java @@ -0,0 +1,137 @@ +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/sources/main/java/com/carrotsearch/hppc/Containers.java b/sources/main/java/com/carrotsearch/hppc/Containers.java new file mode 100644 index 0000000..edd41b7 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/Containers.java @@ -0,0 +1,68 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/DoubleArrayDeque.java b/sources/main/java/com/carrotsearch/hppc/DoubleArrayDeque.java new file mode 100644 index 0000000..7561da2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/DoubleArrayDeque.java @@ -0,0 +1,776 @@ +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/sources/main/java/com/carrotsearch/hppc/DoubleArrayList.java b/sources/main/java/com/carrotsearch/hppc/DoubleArrayList.java new file mode 100644 index 0000000..a0a8bbd --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/DoubleArrayList.java @@ -0,0 +1,586 @@ +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/sources/main/java/com/carrotsearch/hppc/DoubleBufferVisualizer.java b/sources/main/java/com/carrotsearch/hppc/DoubleBufferVisualizer.java new file mode 100644 index 0000000..f708c7a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/DoubleBufferVisualizer.java @@ -0,0 +1,33 @@ +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/sources/main/java/com/carrotsearch/hppc/DoubleCollection.java b/sources/main/java/com/carrotsearch/hppc/DoubleCollection.java new file mode 100644 index 0000000..e1c4f96 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/DoubleCollection.java @@ -0,0 +1,64 @@ +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/sources/main/java/com/carrotsearch/hppc/DoubleContainer.java b/sources/main/java/com/carrotsearch/hppc/DoubleContainer.java new file mode 100644 index 0000000..b5719fd --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/DoubleContainer.java @@ -0,0 +1,76 @@ +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/sources/main/java/com/carrotsearch/hppc/DoubleDeque.java b/sources/main/java/com/carrotsearch/hppc/DoubleDeque.java new file mode 100644 index 0000000..423026e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/DoubleDeque.java @@ -0,0 +1,77 @@ +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/sources/main/java/com/carrotsearch/hppc/DoubleIndexedContainer.java b/sources/main/java/com/carrotsearch/hppc/DoubleIndexedContainer.java new file mode 100644 index 0000000..faf6b27 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/DoubleIndexedContainer.java @@ -0,0 +1,93 @@ +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/sources/main/java/com/carrotsearch/hppc/DoubleLookupContainer.java b/sources/main/java/com/carrotsearch/hppc/DoubleLookupContainer.java new file mode 100644 index 0000000..c27e6b4 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/DoubleLookupContainer.java @@ -0,0 +1,12 @@ +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/sources/main/java/com/carrotsearch/hppc/DoublePgmIndex.java b/sources/main/java/com/carrotsearch/hppc/DoublePgmIndex.java new file mode 100644 index 0000000..ee5ac21 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/DoublePgmIndex.java @@ -0,0 +1,600 @@ +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/sources/main/java/com/carrotsearch/hppc/DoubleStack.java b/sources/main/java/com/carrotsearch/hppc/DoubleStack.java new file mode 100644 index 0000000..bf0a221 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/DoubleStack.java @@ -0,0 +1,137 @@ +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/sources/main/java/com/carrotsearch/hppc/FloatArrayDeque.java b/sources/main/java/com/carrotsearch/hppc/FloatArrayDeque.java new file mode 100644 index 0000000..e3d2908 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/FloatArrayDeque.java @@ -0,0 +1,776 @@ +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/sources/main/java/com/carrotsearch/hppc/FloatArrayList.java b/sources/main/java/com/carrotsearch/hppc/FloatArrayList.java new file mode 100644 index 0000000..0b82dc2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/FloatArrayList.java @@ -0,0 +1,579 @@ +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/sources/main/java/com/carrotsearch/hppc/FloatBufferVisualizer.java b/sources/main/java/com/carrotsearch/hppc/FloatBufferVisualizer.java new file mode 100644 index 0000000..d0c471c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/FloatBufferVisualizer.java @@ -0,0 +1,33 @@ +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/sources/main/java/com/carrotsearch/hppc/FloatCollection.java b/sources/main/java/com/carrotsearch/hppc/FloatCollection.java new file mode 100644 index 0000000..491fab8 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/FloatCollection.java @@ -0,0 +1,64 @@ +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/sources/main/java/com/carrotsearch/hppc/FloatContainer.java b/sources/main/java/com/carrotsearch/hppc/FloatContainer.java new file mode 100644 index 0000000..83b952b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/FloatContainer.java @@ -0,0 +1,76 @@ +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/sources/main/java/com/carrotsearch/hppc/FloatDeque.java b/sources/main/java/com/carrotsearch/hppc/FloatDeque.java new file mode 100644 index 0000000..240f329 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/FloatDeque.java @@ -0,0 +1,77 @@ +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/sources/main/java/com/carrotsearch/hppc/FloatIndexedContainer.java b/sources/main/java/com/carrotsearch/hppc/FloatIndexedContainer.java new file mode 100644 index 0000000..f7beda6 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/FloatIndexedContainer.java @@ -0,0 +1,91 @@ +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/sources/main/java/com/carrotsearch/hppc/FloatLookupContainer.java b/sources/main/java/com/carrotsearch/hppc/FloatLookupContainer.java new file mode 100644 index 0000000..558f763 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/FloatLookupContainer.java @@ -0,0 +1,12 @@ +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/sources/main/java/com/carrotsearch/hppc/FloatPgmIndex.java b/sources/main/java/com/carrotsearch/hppc/FloatPgmIndex.java new file mode 100644 index 0000000..e14cb07 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/FloatPgmIndex.java @@ -0,0 +1,600 @@ +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/sources/main/java/com/carrotsearch/hppc/FloatStack.java b/sources/main/java/com/carrotsearch/hppc/FloatStack.java new file mode 100644 index 0000000..d237717 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/FloatStack.java @@ -0,0 +1,137 @@ +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/sources/main/java/com/carrotsearch/hppc/Generated.java b/sources/main/java/com/carrotsearch/hppc/Generated.java new file mode 100644 index 0000000..dd8c66d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/Generated.java @@ -0,0 +1,37 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/HashContainers.java b/sources/main/java/com/carrotsearch/hppc/HashContainers.java new file mode 100644 index 0000000..7f4be80 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/HashContainers.java @@ -0,0 +1,112 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/IntArrayDeque.java b/sources/main/java/com/carrotsearch/hppc/IntArrayDeque.java new file mode 100644 index 0000000..06983d9 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntArrayDeque.java @@ -0,0 +1,776 @@ +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/sources/main/java/com/carrotsearch/hppc/IntArrayList.java b/sources/main/java/com/carrotsearch/hppc/IntArrayList.java new file mode 100644 index 0000000..6029be0 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntArrayList.java @@ -0,0 +1,586 @@ +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/sources/main/java/com/carrotsearch/hppc/IntBufferVisualizer.java b/sources/main/java/com/carrotsearch/hppc/IntBufferVisualizer.java new file mode 100644 index 0000000..dddbbfc --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntBufferVisualizer.java @@ -0,0 +1,33 @@ +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/sources/main/java/com/carrotsearch/hppc/IntByteAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/IntByteAssociativeContainer.java new file mode 100644 index 0000000..474cc04 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntByteAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/IntByteHashMap.java b/sources/main/java/com/carrotsearch/hppc/IntByteHashMap.java new file mode 100644 index 0000000..2e69d80 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntByteHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/IntByteMap.java b/sources/main/java/com/carrotsearch/hppc/IntByteMap.java new file mode 100644 index 0000000..06ce8aa --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntByteMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/IntCharAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/IntCharAssociativeContainer.java new file mode 100644 index 0000000..adf2f6b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntCharAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/IntCharHashMap.java b/sources/main/java/com/carrotsearch/hppc/IntCharHashMap.java new file mode 100644 index 0000000..f3bc23a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntCharHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/IntCharMap.java b/sources/main/java/com/carrotsearch/hppc/IntCharMap.java new file mode 100644 index 0000000..dd06fe4 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntCharMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/IntCollection.java b/sources/main/java/com/carrotsearch/hppc/IntCollection.java new file mode 100644 index 0000000..16ae39b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntCollection.java @@ -0,0 +1,64 @@ +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/sources/main/java/com/carrotsearch/hppc/IntContainer.java b/sources/main/java/com/carrotsearch/hppc/IntContainer.java new file mode 100644 index 0000000..fd8c1b3 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntContainer.java @@ -0,0 +1,76 @@ +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/sources/main/java/com/carrotsearch/hppc/IntDeque.java b/sources/main/java/com/carrotsearch/hppc/IntDeque.java new file mode 100644 index 0000000..e7981ad --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntDeque.java @@ -0,0 +1,77 @@ +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/sources/main/java/com/carrotsearch/hppc/IntDoubleAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/IntDoubleAssociativeContainer.java new file mode 100644 index 0000000..b447931 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntDoubleAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/IntDoubleHashMap.java b/sources/main/java/com/carrotsearch/hppc/IntDoubleHashMap.java new file mode 100644 index 0000000..d070d0f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntDoubleHashMap.java @@ -0,0 +1,1082 @@ +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/sources/main/java/com/carrotsearch/hppc/IntDoubleMap.java b/sources/main/java/com/carrotsearch/hppc/IntDoubleMap.java new file mode 100644 index 0000000..03691ff --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntDoubleMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/IntFloatAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/IntFloatAssociativeContainer.java new file mode 100644 index 0000000..50a4b96 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntFloatAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/IntFloatHashMap.java b/sources/main/java/com/carrotsearch/hppc/IntFloatHashMap.java new file mode 100644 index 0000000..f7a3a4f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntFloatHashMap.java @@ -0,0 +1,1081 @@ +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/sources/main/java/com/carrotsearch/hppc/IntFloatMap.java b/sources/main/java/com/carrotsearch/hppc/IntFloatMap.java new file mode 100644 index 0000000..4a1e2c9 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntFloatMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/IntHashSet.java b/sources/main/java/com/carrotsearch/hppc/IntHashSet.java new file mode 100644 index 0000000..f6aae01 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntHashSet.java @@ -0,0 +1,787 @@ +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/sources/main/java/com/carrotsearch/hppc/IntIndexedContainer.java b/sources/main/java/com/carrotsearch/hppc/IntIndexedContainer.java new file mode 100644 index 0000000..0eb076e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntIndexedContainer.java @@ -0,0 +1,93 @@ +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/sources/main/java/com/carrotsearch/hppc/IntIntAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/IntIntAssociativeContainer.java new file mode 100644 index 0000000..fdb8772 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntIntAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/IntIntHashMap.java b/sources/main/java/com/carrotsearch/hppc/IntIntHashMap.java new file mode 100644 index 0000000..5380245 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntIntHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/IntIntMap.java b/sources/main/java/com/carrotsearch/hppc/IntIntMap.java new file mode 100644 index 0000000..29097ec --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntIntMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/IntLongAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/IntLongAssociativeContainer.java new file mode 100644 index 0000000..37f3887 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntLongAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/IntLongHashMap.java b/sources/main/java/com/carrotsearch/hppc/IntLongHashMap.java new file mode 100644 index 0000000..8a59514 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntLongHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/IntLongMap.java b/sources/main/java/com/carrotsearch/hppc/IntLongMap.java new file mode 100644 index 0000000..debb690 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntLongMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/IntLookupContainer.java b/sources/main/java/com/carrotsearch/hppc/IntLookupContainer.java new file mode 100644 index 0000000..7eee508 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntLookupContainer.java @@ -0,0 +1,12 @@ +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/sources/main/java/com/carrotsearch/hppc/IntObjectAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/IntObjectAssociativeContainer.java new file mode 100644 index 0000000..7be4922 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntObjectAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/IntObjectHashMap.java b/sources/main/java/com/carrotsearch/hppc/IntObjectHashMap.java new file mode 100644 index 0000000..8fb8f64 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntObjectHashMap.java @@ -0,0 +1,1050 @@ +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/sources/main/java/com/carrotsearch/hppc/IntObjectMap.java b/sources/main/java/com/carrotsearch/hppc/IntObjectMap.java new file mode 100644 index 0000000..86c778c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntObjectMap.java @@ -0,0 +1,181 @@ +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/sources/main/java/com/carrotsearch/hppc/IntPgmIndex.java b/sources/main/java/com/carrotsearch/hppc/IntPgmIndex.java new file mode 100644 index 0000000..da7dd0d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntPgmIndex.java @@ -0,0 +1,600 @@ +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/sources/main/java/com/carrotsearch/hppc/IntSet.java b/sources/main/java/com/carrotsearch/hppc/IntSet.java new file mode 100644 index 0000000..500b376 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntSet.java @@ -0,0 +1,33 @@ +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/sources/main/java/com/carrotsearch/hppc/IntShortAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/IntShortAssociativeContainer.java new file mode 100644 index 0000000..918efde --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntShortAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/IntShortHashMap.java b/sources/main/java/com/carrotsearch/hppc/IntShortHashMap.java new file mode 100644 index 0000000..c36b0fe --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntShortHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/IntShortMap.java b/sources/main/java/com/carrotsearch/hppc/IntShortMap.java new file mode 100644 index 0000000..b0a5813 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntShortMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/IntStack.java b/sources/main/java/com/carrotsearch/hppc/IntStack.java new file mode 100644 index 0000000..cd65ff6 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/IntStack.java @@ -0,0 +1,137 @@ +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/sources/main/java/com/carrotsearch/hppc/LongArrayDeque.java b/sources/main/java/com/carrotsearch/hppc/LongArrayDeque.java new file mode 100644 index 0000000..8065aaf --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongArrayDeque.java @@ -0,0 +1,776 @@ +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/sources/main/java/com/carrotsearch/hppc/LongArrayList.java b/sources/main/java/com/carrotsearch/hppc/LongArrayList.java new file mode 100644 index 0000000..ea99bfe --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongArrayList.java @@ -0,0 +1,586 @@ +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/sources/main/java/com/carrotsearch/hppc/LongBufferVisualizer.java b/sources/main/java/com/carrotsearch/hppc/LongBufferVisualizer.java new file mode 100644 index 0000000..1c9e0af --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongBufferVisualizer.java @@ -0,0 +1,33 @@ +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/sources/main/java/com/carrotsearch/hppc/LongByteAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/LongByteAssociativeContainer.java new file mode 100644 index 0000000..b3e17bf --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongByteAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/LongByteHashMap.java b/sources/main/java/com/carrotsearch/hppc/LongByteHashMap.java new file mode 100644 index 0000000..eeaf502 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongByteHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/LongByteMap.java b/sources/main/java/com/carrotsearch/hppc/LongByteMap.java new file mode 100644 index 0000000..1291678 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongByteMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/LongCharAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/LongCharAssociativeContainer.java new file mode 100644 index 0000000..9188f4e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongCharAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/LongCharHashMap.java b/sources/main/java/com/carrotsearch/hppc/LongCharHashMap.java new file mode 100644 index 0000000..c5262ab --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongCharHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/LongCharMap.java b/sources/main/java/com/carrotsearch/hppc/LongCharMap.java new file mode 100644 index 0000000..fbc9d68 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongCharMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/LongCollection.java b/sources/main/java/com/carrotsearch/hppc/LongCollection.java new file mode 100644 index 0000000..65869b8 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongCollection.java @@ -0,0 +1,64 @@ +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/sources/main/java/com/carrotsearch/hppc/LongContainer.java b/sources/main/java/com/carrotsearch/hppc/LongContainer.java new file mode 100644 index 0000000..cc10b05 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongContainer.java @@ -0,0 +1,76 @@ +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/sources/main/java/com/carrotsearch/hppc/LongDeque.java b/sources/main/java/com/carrotsearch/hppc/LongDeque.java new file mode 100644 index 0000000..a2a202b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongDeque.java @@ -0,0 +1,77 @@ +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/sources/main/java/com/carrotsearch/hppc/LongDoubleAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/LongDoubleAssociativeContainer.java new file mode 100644 index 0000000..f0ec055 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongDoubleAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/LongDoubleHashMap.java b/sources/main/java/com/carrotsearch/hppc/LongDoubleHashMap.java new file mode 100644 index 0000000..2df0b5a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongDoubleHashMap.java @@ -0,0 +1,1082 @@ +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/sources/main/java/com/carrotsearch/hppc/LongDoubleMap.java b/sources/main/java/com/carrotsearch/hppc/LongDoubleMap.java new file mode 100644 index 0000000..7c83d30 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongDoubleMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/LongFloatAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/LongFloatAssociativeContainer.java new file mode 100644 index 0000000..80cc2da --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongFloatAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/LongFloatHashMap.java b/sources/main/java/com/carrotsearch/hppc/LongFloatHashMap.java new file mode 100644 index 0000000..bc0d5f2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongFloatHashMap.java @@ -0,0 +1,1081 @@ +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/sources/main/java/com/carrotsearch/hppc/LongFloatMap.java b/sources/main/java/com/carrotsearch/hppc/LongFloatMap.java new file mode 100644 index 0000000..dc4cf19 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongFloatMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/LongHashSet.java b/sources/main/java/com/carrotsearch/hppc/LongHashSet.java new file mode 100644 index 0000000..8957939 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongHashSet.java @@ -0,0 +1,787 @@ +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/sources/main/java/com/carrotsearch/hppc/LongIndexedContainer.java b/sources/main/java/com/carrotsearch/hppc/LongIndexedContainer.java new file mode 100644 index 0000000..ba0c78a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongIndexedContainer.java @@ -0,0 +1,93 @@ +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/sources/main/java/com/carrotsearch/hppc/LongIntAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/LongIntAssociativeContainer.java new file mode 100644 index 0000000..9746787 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongIntAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/LongIntHashMap.java b/sources/main/java/com/carrotsearch/hppc/LongIntHashMap.java new file mode 100644 index 0000000..de4d34b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongIntHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/LongIntMap.java b/sources/main/java/com/carrotsearch/hppc/LongIntMap.java new file mode 100644 index 0000000..6fb8c86 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongIntMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/LongLongAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/LongLongAssociativeContainer.java new file mode 100644 index 0000000..24be1a2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongLongAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/LongLongHashMap.java b/sources/main/java/com/carrotsearch/hppc/LongLongHashMap.java new file mode 100644 index 0000000..c193780 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongLongHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/LongLongMap.java b/sources/main/java/com/carrotsearch/hppc/LongLongMap.java new file mode 100644 index 0000000..3ea093c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongLongMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/LongLookupContainer.java b/sources/main/java/com/carrotsearch/hppc/LongLookupContainer.java new file mode 100644 index 0000000..70a9994 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongLookupContainer.java @@ -0,0 +1,12 @@ +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/sources/main/java/com/carrotsearch/hppc/LongObjectAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/LongObjectAssociativeContainer.java new file mode 100644 index 0000000..9e805ff --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongObjectAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/LongObjectHashMap.java b/sources/main/java/com/carrotsearch/hppc/LongObjectHashMap.java new file mode 100644 index 0000000..e8647af --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongObjectHashMap.java @@ -0,0 +1,1050 @@ +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/sources/main/java/com/carrotsearch/hppc/LongObjectMap.java b/sources/main/java/com/carrotsearch/hppc/LongObjectMap.java new file mode 100644 index 0000000..712d01a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongObjectMap.java @@ -0,0 +1,181 @@ +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/sources/main/java/com/carrotsearch/hppc/LongPgmIndex.java b/sources/main/java/com/carrotsearch/hppc/LongPgmIndex.java new file mode 100644 index 0000000..dc80b15 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongPgmIndex.java @@ -0,0 +1,600 @@ +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/sources/main/java/com/carrotsearch/hppc/LongSet.java b/sources/main/java/com/carrotsearch/hppc/LongSet.java new file mode 100644 index 0000000..69a3144 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongSet.java @@ -0,0 +1,33 @@ +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/sources/main/java/com/carrotsearch/hppc/LongShortAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/LongShortAssociativeContainer.java new file mode 100644 index 0000000..e9f9f67 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongShortAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/LongShortHashMap.java b/sources/main/java/com/carrotsearch/hppc/LongShortHashMap.java new file mode 100644 index 0000000..cf9a34c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongShortHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/LongShortMap.java b/sources/main/java/com/carrotsearch/hppc/LongShortMap.java new file mode 100644 index 0000000..3ef5052 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongShortMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/LongStack.java b/sources/main/java/com/carrotsearch/hppc/LongStack.java new file mode 100644 index 0000000..6d5f15f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/LongStack.java @@ -0,0 +1,137 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectArrayDeque.java b/sources/main/java/com/carrotsearch/hppc/ObjectArrayDeque.java new file mode 100644 index 0000000..a25261f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectArrayDeque.java @@ -0,0 +1,786 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectArrayList.java b/sources/main/java/com/carrotsearch/hppc/ObjectArrayList.java new file mode 100644 index 0000000..e11da84 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectArrayList.java @@ -0,0 +1,604 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectBufferVisualizer.java b/sources/main/java/com/carrotsearch/hppc/ObjectBufferVisualizer.java new file mode 100644 index 0000000..c7a988f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectBufferVisualizer.java @@ -0,0 +1,33 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectByteAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/ObjectByteAssociativeContainer.java new file mode 100644 index 0000000..a590416 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectByteAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectByteHashMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectByteHashMap.java new file mode 100644 index 0000000..4de468b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectByteHashMap.java @@ -0,0 +1,1089 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectByteIdentityHashMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectByteIdentityHashMap.java new file mode 100644 index 0000000..f9567cd --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectByteIdentityHashMap.java @@ -0,0 +1,71 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectByteMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectByteMap.java new file mode 100644 index 0000000..9b4ac38 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectByteMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectCharAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/ObjectCharAssociativeContainer.java new file mode 100644 index 0000000..fc06ba2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectCharAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectCharHashMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectCharHashMap.java new file mode 100644 index 0000000..b71d892 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectCharHashMap.java @@ -0,0 +1,1089 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectCharIdentityHashMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectCharIdentityHashMap.java new file mode 100644 index 0000000..d373e7f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectCharIdentityHashMap.java @@ -0,0 +1,71 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectCharMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectCharMap.java new file mode 100644 index 0000000..ab636b5 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectCharMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectCollection.java b/sources/main/java/com/carrotsearch/hppc/ObjectCollection.java new file mode 100644 index 0000000..447e46d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectCollection.java @@ -0,0 +1,64 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectContainer.java b/sources/main/java/com/carrotsearch/hppc/ObjectContainer.java new file mode 100644 index 0000000..6e8cd48 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectContainer.java @@ -0,0 +1,85 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectDeque.java b/sources/main/java/com/carrotsearch/hppc/ObjectDeque.java new file mode 100644 index 0000000..2e6f62f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectDeque.java @@ -0,0 +1,77 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectDoubleAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/ObjectDoubleAssociativeContainer.java new file mode 100644 index 0000000..0eb15e2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectDoubleAssociativeContainer.java @@ -0,0 +1,106 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectDoubleHashMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectDoubleHashMap.java new file mode 100644 index 0000000..bf3240c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectDoubleHashMap.java @@ -0,0 +1,1091 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectDoubleIdentityHashMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectDoubleIdentityHashMap.java new file mode 100644 index 0000000..b034adf --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectDoubleIdentityHashMap.java @@ -0,0 +1,71 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectDoubleMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectDoubleMap.java new file mode 100644 index 0000000..595fc97 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectDoubleMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectFloatAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/ObjectFloatAssociativeContainer.java new file mode 100644 index 0000000..a7448b2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectFloatAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectFloatHashMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectFloatHashMap.java new file mode 100644 index 0000000..2dc4c98 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectFloatHashMap.java @@ -0,0 +1,1090 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectFloatIdentityHashMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectFloatIdentityHashMap.java new file mode 100644 index 0000000..cfb63d0 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectFloatIdentityHashMap.java @@ -0,0 +1,71 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectFloatMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectFloatMap.java new file mode 100644 index 0000000..8feee94 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectFloatMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectHashSet.java b/sources/main/java/com/carrotsearch/hppc/ObjectHashSet.java new file mode 100644 index 0000000..1a37b23 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectHashSet.java @@ -0,0 +1,797 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectIdentityHashSet.java b/sources/main/java/com/carrotsearch/hppc/ObjectIdentityHashSet.java new file mode 100644 index 0000000..4efb528 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectIdentityHashSet.java @@ -0,0 +1,66 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/ObjectIndexedContainer.java b/sources/main/java/com/carrotsearch/hppc/ObjectIndexedContainer.java new file mode 100644 index 0000000..37ce1fc --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectIndexedContainer.java @@ -0,0 +1,93 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectIntAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/ObjectIntAssociativeContainer.java new file mode 100644 index 0000000..13f6844 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectIntAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectIntHashMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectIntHashMap.java new file mode 100644 index 0000000..47e4999 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectIntHashMap.java @@ -0,0 +1,1089 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectIntIdentityHashMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectIntIdentityHashMap.java new file mode 100644 index 0000000..b471a1a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectIntIdentityHashMap.java @@ -0,0 +1,71 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectIntMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectIntMap.java new file mode 100644 index 0000000..5f87f0f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectIntMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectLongAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/ObjectLongAssociativeContainer.java new file mode 100644 index 0000000..3d78e37 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectLongAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectLongHashMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectLongHashMap.java new file mode 100644 index 0000000..1688a68 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectLongHashMap.java @@ -0,0 +1,1089 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectLongIdentityHashMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectLongIdentityHashMap.java new file mode 100644 index 0000000..886d674 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectLongIdentityHashMap.java @@ -0,0 +1,71 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectLongMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectLongMap.java new file mode 100644 index 0000000..4cab963 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectLongMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectLookupContainer.java b/sources/main/java/com/carrotsearch/hppc/ObjectLookupContainer.java new file mode 100644 index 0000000..ee0982c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectLookupContainer.java @@ -0,0 +1,12 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectObjectAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/ObjectObjectAssociativeContainer.java new file mode 100644 index 0000000..551388e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectObjectAssociativeContainer.java @@ -0,0 +1,106 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectObjectHashMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectObjectHashMap.java new file mode 100644 index 0000000..855027c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectObjectHashMap.java @@ -0,0 +1,1059 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectObjectIdentityHashMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectObjectIdentityHashMap.java new file mode 100644 index 0000000..c726faa --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectObjectIdentityHashMap.java @@ -0,0 +1,91 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectObjectMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectObjectMap.java new file mode 100644 index 0000000..c1316b0 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectObjectMap.java @@ -0,0 +1,183 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectSet.java b/sources/main/java/com/carrotsearch/hppc/ObjectSet.java new file mode 100644 index 0000000..24355d4 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectSet.java @@ -0,0 +1,33 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectShortAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/ObjectShortAssociativeContainer.java new file mode 100644 index 0000000..a5c46c2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectShortAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectShortHashMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectShortHashMap.java new file mode 100644 index 0000000..9d492a1 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectShortHashMap.java @@ -0,0 +1,1089 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectShortIdentityHashMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectShortIdentityHashMap.java new file mode 100644 index 0000000..c5c04c2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectShortIdentityHashMap.java @@ -0,0 +1,71 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectShortMap.java b/sources/main/java/com/carrotsearch/hppc/ObjectShortMap.java new file mode 100644 index 0000000..df88b9e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectShortMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/ObjectStack.java b/sources/main/java/com/carrotsearch/hppc/ObjectStack.java new file mode 100644 index 0000000..d768be9 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ObjectStack.java @@ -0,0 +1,145 @@ +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/sources/main/java/com/carrotsearch/hppc/PgmIndexUtil.java b/sources/main/java/com/carrotsearch/hppc/PgmIndexUtil.java new file mode 100644 index 0000000..9793ab2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/PgmIndexUtil.java @@ -0,0 +1,122 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/PlaModel.java b/sources/main/java/com/carrotsearch/hppc/PlaModel.java new file mode 100644 index 0000000..4931740 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/PlaModel.java @@ -0,0 +1,414 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/Preallocable.java b/sources/main/java/com/carrotsearch/hppc/Preallocable.java new file mode 100644 index 0000000..1251bcb --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/Preallocable.java @@ -0,0 +1,21 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/RamUsageEstimator.java b/sources/main/java/com/carrotsearch/hppc/RamUsageEstimator.java new file mode 100644 index 0000000..907ea2e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/RamUsageEstimator.java @@ -0,0 +1,162 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/ShortArrayDeque.java b/sources/main/java/com/carrotsearch/hppc/ShortArrayDeque.java new file mode 100644 index 0000000..9c1cf82 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortArrayDeque.java @@ -0,0 +1,776 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortArrayList.java b/sources/main/java/com/carrotsearch/hppc/ShortArrayList.java new file mode 100644 index 0000000..cae8738 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortArrayList.java @@ -0,0 +1,579 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortBufferVisualizer.java b/sources/main/java/com/carrotsearch/hppc/ShortBufferVisualizer.java new file mode 100644 index 0000000..ce87365 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortBufferVisualizer.java @@ -0,0 +1,33 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortByteAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/ShortByteAssociativeContainer.java new file mode 100644 index 0000000..27f4953 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortByteAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortByteHashMap.java b/sources/main/java/com/carrotsearch/hppc/ShortByteHashMap.java new file mode 100644 index 0000000..049da07 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortByteHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortByteMap.java b/sources/main/java/com/carrotsearch/hppc/ShortByteMap.java new file mode 100644 index 0000000..dfe9a67 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortByteMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortCharAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/ShortCharAssociativeContainer.java new file mode 100644 index 0000000..3ab6c85 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortCharAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortCharHashMap.java b/sources/main/java/com/carrotsearch/hppc/ShortCharHashMap.java new file mode 100644 index 0000000..cb29c12 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortCharHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortCharMap.java b/sources/main/java/com/carrotsearch/hppc/ShortCharMap.java new file mode 100644 index 0000000..17bd928 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortCharMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortCollection.java b/sources/main/java/com/carrotsearch/hppc/ShortCollection.java new file mode 100644 index 0000000..372dfca --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortCollection.java @@ -0,0 +1,64 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortContainer.java b/sources/main/java/com/carrotsearch/hppc/ShortContainer.java new file mode 100644 index 0000000..f425a1f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortContainer.java @@ -0,0 +1,76 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortDeque.java b/sources/main/java/com/carrotsearch/hppc/ShortDeque.java new file mode 100644 index 0000000..244b6a6 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortDeque.java @@ -0,0 +1,77 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortDoubleAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/ShortDoubleAssociativeContainer.java new file mode 100644 index 0000000..0155b8a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortDoubleAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortDoubleHashMap.java b/sources/main/java/com/carrotsearch/hppc/ShortDoubleHashMap.java new file mode 100644 index 0000000..4bf5058 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortDoubleHashMap.java @@ -0,0 +1,1082 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortDoubleMap.java b/sources/main/java/com/carrotsearch/hppc/ShortDoubleMap.java new file mode 100644 index 0000000..8d3ac5f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortDoubleMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortFloatAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/ShortFloatAssociativeContainer.java new file mode 100644 index 0000000..f6a2bf7 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortFloatAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortFloatHashMap.java b/sources/main/java/com/carrotsearch/hppc/ShortFloatHashMap.java new file mode 100644 index 0000000..9383eba --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortFloatHashMap.java @@ -0,0 +1,1081 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortFloatMap.java b/sources/main/java/com/carrotsearch/hppc/ShortFloatMap.java new file mode 100644 index 0000000..d09dac1 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortFloatMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortHashSet.java b/sources/main/java/com/carrotsearch/hppc/ShortHashSet.java new file mode 100644 index 0000000..f2f3500 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortHashSet.java @@ -0,0 +1,787 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortIndexedContainer.java b/sources/main/java/com/carrotsearch/hppc/ShortIndexedContainer.java new file mode 100644 index 0000000..fc7a41c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortIndexedContainer.java @@ -0,0 +1,91 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortIntAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/ShortIntAssociativeContainer.java new file mode 100644 index 0000000..07b4a19 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortIntAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortIntHashMap.java b/sources/main/java/com/carrotsearch/hppc/ShortIntHashMap.java new file mode 100644 index 0000000..07bea9e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortIntHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortIntMap.java b/sources/main/java/com/carrotsearch/hppc/ShortIntMap.java new file mode 100644 index 0000000..78f19f4 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortIntMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortLongAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/ShortLongAssociativeContainer.java new file mode 100644 index 0000000..3237515 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortLongAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortLongHashMap.java b/sources/main/java/com/carrotsearch/hppc/ShortLongHashMap.java new file mode 100644 index 0000000..2fe676e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortLongHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortLongMap.java b/sources/main/java/com/carrotsearch/hppc/ShortLongMap.java new file mode 100644 index 0000000..682a924 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortLongMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortLookupContainer.java b/sources/main/java/com/carrotsearch/hppc/ShortLookupContainer.java new file mode 100644 index 0000000..c049ef8 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortLookupContainer.java @@ -0,0 +1,12 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortObjectAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/ShortObjectAssociativeContainer.java new file mode 100644 index 0000000..0c3b670 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortObjectAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortObjectHashMap.java b/sources/main/java/com/carrotsearch/hppc/ShortObjectHashMap.java new file mode 100644 index 0000000..6b8a20b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortObjectHashMap.java @@ -0,0 +1,1050 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortObjectMap.java b/sources/main/java/com/carrotsearch/hppc/ShortObjectMap.java new file mode 100644 index 0000000..732c57e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortObjectMap.java @@ -0,0 +1,181 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortSet.java b/sources/main/java/com/carrotsearch/hppc/ShortSet.java new file mode 100644 index 0000000..f87250b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortSet.java @@ -0,0 +1,33 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortShortAssociativeContainer.java b/sources/main/java/com/carrotsearch/hppc/ShortShortAssociativeContainer.java new file mode 100644 index 0000000..e1adb95 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortShortAssociativeContainer.java @@ -0,0 +1,105 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortShortHashMap.java b/sources/main/java/com/carrotsearch/hppc/ShortShortHashMap.java new file mode 100644 index 0000000..589b23c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortShortHashMap.java @@ -0,0 +1,1080 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortShortMap.java b/sources/main/java/com/carrotsearch/hppc/ShortShortMap.java new file mode 100644 index 0000000..d90c602 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortShortMap.java @@ -0,0 +1,205 @@ +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/sources/main/java/com/carrotsearch/hppc/ShortStack.java b/sources/main/java/com/carrotsearch/hppc/ShortStack.java new file mode 100644 index 0000000..2c972c3 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/ShortStack.java @@ -0,0 +1,137 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationCharByteHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationCharByteHashMap.java new file mode 100644 index 0000000..4e0da79 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationCharByteHashMap.java @@ -0,0 +1,440 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationCharCharHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationCharCharHashMap.java new file mode 100644 index 0000000..e5f5017 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationCharCharHashMap.java @@ -0,0 +1,440 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationCharDoubleHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationCharDoubleHashMap.java new file mode 100644 index 0000000..0d54399 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationCharDoubleHashMap.java @@ -0,0 +1,441 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationCharFloatHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationCharFloatHashMap.java new file mode 100644 index 0000000..90c227e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationCharFloatHashMap.java @@ -0,0 +1,441 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationCharIntHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationCharIntHashMap.java new file mode 100644 index 0000000..dde200f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationCharIntHashMap.java @@ -0,0 +1,440 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationCharLongHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationCharLongHashMap.java new file mode 100644 index 0000000..76ec360 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationCharLongHashMap.java @@ -0,0 +1,440 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationCharObjectHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationCharObjectHashMap.java new file mode 100644 index 0000000..b4eef4b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationCharObjectHashMap.java @@ -0,0 +1,435 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationCharShortHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationCharShortHashMap.java new file mode 100644 index 0000000..8097e0c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationCharShortHashMap.java @@ -0,0 +1,441 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationIntByteHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationIntByteHashMap.java new file mode 100644 index 0000000..ff9ca50 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationIntByteHashMap.java @@ -0,0 +1,440 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationIntCharHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationIntCharHashMap.java new file mode 100644 index 0000000..c919072 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationIntCharHashMap.java @@ -0,0 +1,440 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationIntDoubleHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationIntDoubleHashMap.java new file mode 100644 index 0000000..107abe8 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationIntDoubleHashMap.java @@ -0,0 +1,441 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationIntFloatHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationIntFloatHashMap.java new file mode 100644 index 0000000..ff98b06 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationIntFloatHashMap.java @@ -0,0 +1,440 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationIntIntHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationIntIntHashMap.java new file mode 100644 index 0000000..8cb4c22 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationIntIntHashMap.java @@ -0,0 +1,440 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationIntLongHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationIntLongHashMap.java new file mode 100644 index 0000000..bbbfb30 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationIntLongHashMap.java @@ -0,0 +1,440 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationIntObjectHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationIntObjectHashMap.java new file mode 100644 index 0000000..cd8591e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationIntObjectHashMap.java @@ -0,0 +1,435 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationIntShortHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationIntShortHashMap.java new file mode 100644 index 0000000..243bf98 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationIntShortHashMap.java @@ -0,0 +1,440 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationLongByteHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationLongByteHashMap.java new file mode 100644 index 0000000..46b063f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationLongByteHashMap.java @@ -0,0 +1,440 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationLongCharHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationLongCharHashMap.java new file mode 100644 index 0000000..c4daf3d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationLongCharHashMap.java @@ -0,0 +1,440 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationLongDoubleHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationLongDoubleHashMap.java new file mode 100644 index 0000000..bbd4153 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationLongDoubleHashMap.java @@ -0,0 +1,441 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationLongFloatHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationLongFloatHashMap.java new file mode 100644 index 0000000..2a8e61d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationLongFloatHashMap.java @@ -0,0 +1,441 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationLongIntHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationLongIntHashMap.java new file mode 100644 index 0000000..77620aa --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationLongIntHashMap.java @@ -0,0 +1,440 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationLongLongHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationLongLongHashMap.java new file mode 100644 index 0000000..605189b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationLongLongHashMap.java @@ -0,0 +1,440 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationLongObjectHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationLongObjectHashMap.java new file mode 100644 index 0000000..b7f64fe --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationLongObjectHashMap.java @@ -0,0 +1,435 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationLongShortHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationLongShortHashMap.java new file mode 100644 index 0000000..63de5c2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationLongShortHashMap.java @@ -0,0 +1,441 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectByteHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectByteHashMap.java new file mode 100644 index 0000000..011ace2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectByteHashMap.java @@ -0,0 +1,447 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectCharHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectCharHashMap.java new file mode 100644 index 0000000..37f0f25 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectCharHashMap.java @@ -0,0 +1,447 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectDoubleHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectDoubleHashMap.java new file mode 100644 index 0000000..f593fe6 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectDoubleHashMap.java @@ -0,0 +1,447 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectFloatHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectFloatHashMap.java new file mode 100644 index 0000000..4781dd4 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectFloatHashMap.java @@ -0,0 +1,447 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectIntHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectIntHashMap.java new file mode 100644 index 0000000..84b1aad --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectIntHashMap.java @@ -0,0 +1,447 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectLongHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectLongHashMap.java new file mode 100644 index 0000000..f353773 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectLongHashMap.java @@ -0,0 +1,447 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectObjectHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectObjectHashMap.java new file mode 100644 index 0000000..d1c881b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectObjectHashMap.java @@ -0,0 +1,440 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectShortHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectShortHashMap.java new file mode 100644 index 0000000..69e7bd7 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationObjectShortHashMap.java @@ -0,0 +1,447 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationShortByteHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationShortByteHashMap.java new file mode 100644 index 0000000..478d3de --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationShortByteHashMap.java @@ -0,0 +1,442 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationShortCharHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationShortCharHashMap.java new file mode 100644 index 0000000..3f4ad7d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationShortCharHashMap.java @@ -0,0 +1,442 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationShortDoubleHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationShortDoubleHashMap.java new file mode 100644 index 0000000..a9ae326 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationShortDoubleHashMap.java @@ -0,0 +1,443 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationShortFloatHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationShortFloatHashMap.java new file mode 100644 index 0000000..bad3ad0 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationShortFloatHashMap.java @@ -0,0 +1,442 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationShortIntHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationShortIntHashMap.java new file mode 100644 index 0000000..501b0a0 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationShortIntHashMap.java @@ -0,0 +1,441 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationShortLongHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationShortLongHashMap.java new file mode 100644 index 0000000..5aae2e1 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationShortLongHashMap.java @@ -0,0 +1,442 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationShortObjectHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationShortObjectHashMap.java new file mode 100644 index 0000000..989690a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationShortObjectHashMap.java @@ -0,0 +1,436 @@ +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/sources/main/java/com/carrotsearch/hppc/SortedIterationShortShortHashMap.java b/sources/main/java/com/carrotsearch/hppc/SortedIterationShortShortHashMap.java new file mode 100644 index 0000000..d726623 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/SortedIterationShortShortHashMap.java @@ -0,0 +1,442 @@ +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/sources/main/java/com/carrotsearch/hppc/XorShift128P.java b/sources/main/java/com/carrotsearch/hppc/XorShift128P.java new file mode 100644 index 0000000..e9db0a0 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/XorShift128P.java @@ -0,0 +1,67 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/comparators/ByteByteComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ByteByteComparator.java new file mode 100644 index 0000000..734e028 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ByteByteComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ByteCharComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ByteCharComparator.java new file mode 100644 index 0000000..6682edd --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ByteCharComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ByteComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ByteComparator.java new file mode 100644 index 0000000..0ff9f38 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ByteComparator.java @@ -0,0 +1,11 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ByteDoubleComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ByteDoubleComparator.java new file mode 100644 index 0000000..84b6105 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ByteDoubleComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ByteFloatComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ByteFloatComparator.java new file mode 100644 index 0000000..a54901d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ByteFloatComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ByteIntComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ByteIntComparator.java new file mode 100644 index 0000000..e2fed66 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ByteIntComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ByteLongComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ByteLongComparator.java new file mode 100644 index 0000000..5684a8b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ByteLongComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ByteObjectComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ByteObjectComparator.java new file mode 100644 index 0000000..fc1c19a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ByteObjectComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ByteShortComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ByteShortComparator.java new file mode 100644 index 0000000..154a216 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ByteShortComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/CharByteComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/CharByteComparator.java new file mode 100644 index 0000000..807f0e0 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/CharByteComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/CharCharComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/CharCharComparator.java new file mode 100644 index 0000000..0f79062 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/CharCharComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/CharComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/CharComparator.java new file mode 100644 index 0000000..acedac6 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/CharComparator.java @@ -0,0 +1,11 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/CharDoubleComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/CharDoubleComparator.java new file mode 100644 index 0000000..486620c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/CharDoubleComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/CharFloatComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/CharFloatComparator.java new file mode 100644 index 0000000..47bc595 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/CharFloatComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/CharIntComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/CharIntComparator.java new file mode 100644 index 0000000..c2a225d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/CharIntComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/CharLongComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/CharLongComparator.java new file mode 100644 index 0000000..390998e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/CharLongComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/CharObjectComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/CharObjectComparator.java new file mode 100644 index 0000000..fdab147 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/CharObjectComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/CharShortComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/CharShortComparator.java new file mode 100644 index 0000000..91ba997 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/CharShortComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/DoubleByteComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/DoubleByteComparator.java new file mode 100644 index 0000000..60fae03 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/DoubleByteComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/DoubleCharComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/DoubleCharComparator.java new file mode 100644 index 0000000..ec215c9 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/DoubleCharComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/DoubleComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/DoubleComparator.java new file mode 100644 index 0000000..193b5f9 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/DoubleComparator.java @@ -0,0 +1,7 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/DoubleDoubleComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/DoubleDoubleComparator.java new file mode 100644 index 0000000..20ba2dc --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/DoubleDoubleComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/DoubleFloatComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/DoubleFloatComparator.java new file mode 100644 index 0000000..50aca95 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/DoubleFloatComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/DoubleIntComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/DoubleIntComparator.java new file mode 100644 index 0000000..afd1251 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/DoubleIntComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/DoubleLongComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/DoubleLongComparator.java new file mode 100644 index 0000000..a843ec6 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/DoubleLongComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/DoubleObjectComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/DoubleObjectComparator.java new file mode 100644 index 0000000..9d654aa --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/DoubleObjectComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/DoubleShortComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/DoubleShortComparator.java new file mode 100644 index 0000000..4b46316 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/DoubleShortComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/FloatByteComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/FloatByteComparator.java new file mode 100644 index 0000000..298f94b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/FloatByteComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/FloatCharComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/FloatCharComparator.java new file mode 100644 index 0000000..a2af271 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/FloatCharComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/FloatComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/FloatComparator.java new file mode 100644 index 0000000..8831a3b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/FloatComparator.java @@ -0,0 +1,7 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/FloatDoubleComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/FloatDoubleComparator.java new file mode 100644 index 0000000..26cfcb6 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/FloatDoubleComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/FloatFloatComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/FloatFloatComparator.java new file mode 100644 index 0000000..8fc373b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/FloatFloatComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/FloatIntComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/FloatIntComparator.java new file mode 100644 index 0000000..86199e5 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/FloatIntComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/FloatLongComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/FloatLongComparator.java new file mode 100644 index 0000000..7dcc9e4 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/FloatLongComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/FloatObjectComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/FloatObjectComparator.java new file mode 100644 index 0000000..f1e9cd6 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/FloatObjectComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/FloatShortComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/FloatShortComparator.java new file mode 100644 index 0000000..baac7d4 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/FloatShortComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/IntByteComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/IntByteComparator.java new file mode 100644 index 0000000..fe34ad5 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/IntByteComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/IntCharComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/IntCharComparator.java new file mode 100644 index 0000000..b682c52 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/IntCharComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/IntComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/IntComparator.java new file mode 100644 index 0000000..3501d91 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/IntComparator.java @@ -0,0 +1,11 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/IntDoubleComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/IntDoubleComparator.java new file mode 100644 index 0000000..5d6580d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/IntDoubleComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/IntFloatComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/IntFloatComparator.java new file mode 100644 index 0000000..6cb7906 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/IntFloatComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/IntIntComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/IntIntComparator.java new file mode 100644 index 0000000..fe4214f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/IntIntComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/IntLongComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/IntLongComparator.java new file mode 100644 index 0000000..620a697 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/IntLongComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/IntObjectComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/IntObjectComparator.java new file mode 100644 index 0000000..8fbbb04 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/IntObjectComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/IntShortComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/IntShortComparator.java new file mode 100644 index 0000000..5657bdb --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/IntShortComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/LongByteComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/LongByteComparator.java new file mode 100644 index 0000000..326d0f1 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/LongByteComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/LongCharComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/LongCharComparator.java new file mode 100644 index 0000000..7c2267f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/LongCharComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/LongComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/LongComparator.java new file mode 100644 index 0000000..d090607 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/LongComparator.java @@ -0,0 +1,11 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/LongDoubleComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/LongDoubleComparator.java new file mode 100644 index 0000000..25f39e2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/LongDoubleComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/LongFloatComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/LongFloatComparator.java new file mode 100644 index 0000000..dde992f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/LongFloatComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/LongIntComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/LongIntComparator.java new file mode 100644 index 0000000..de63af1 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/LongIntComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/LongLongComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/LongLongComparator.java new file mode 100644 index 0000000..9b0822d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/LongLongComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/LongObjectComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/LongObjectComparator.java new file mode 100644 index 0000000..42fd64b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/LongObjectComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/LongShortComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/LongShortComparator.java new file mode 100644 index 0000000..b6377dd --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/LongShortComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ObjectByteComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ObjectByteComparator.java new file mode 100644 index 0000000..54d86da --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ObjectByteComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ObjectCharComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ObjectCharComparator.java new file mode 100644 index 0000000..0c8f42d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ObjectCharComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ObjectDoubleComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ObjectDoubleComparator.java new file mode 100644 index 0000000..1ec1727 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ObjectDoubleComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ObjectFloatComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ObjectFloatComparator.java new file mode 100644 index 0000000..1c289f2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ObjectFloatComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ObjectIntComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ObjectIntComparator.java new file mode 100644 index 0000000..fe14327 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ObjectIntComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ObjectLongComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ObjectLongComparator.java new file mode 100644 index 0000000..e031f4e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ObjectLongComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ObjectObjectComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ObjectObjectComparator.java new file mode 100644 index 0000000..ed5a2d6 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ObjectObjectComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ObjectShortComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ObjectShortComparator.java new file mode 100644 index 0000000..c545a55 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ObjectShortComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ShortByteComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ShortByteComparator.java new file mode 100644 index 0000000..b372f94 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ShortByteComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ShortCharComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ShortCharComparator.java new file mode 100644 index 0000000..82b9b0a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ShortCharComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ShortComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ShortComparator.java new file mode 100644 index 0000000..b935f60 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ShortComparator.java @@ -0,0 +1,11 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ShortDoubleComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ShortDoubleComparator.java new file mode 100644 index 0000000..37c2a7b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ShortDoubleComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ShortFloatComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ShortFloatComparator.java new file mode 100644 index 0000000..15eec4a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ShortFloatComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ShortIntComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ShortIntComparator.java new file mode 100644 index 0000000..9d5ad1f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ShortIntComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ShortLongComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ShortLongComparator.java new file mode 100644 index 0000000..9e394e1 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ShortLongComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ShortObjectComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ShortObjectComparator.java new file mode 100644 index 0000000..6d1715f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ShortObjectComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/comparators/ShortShortComparator.java b/sources/main/java/com/carrotsearch/hppc/comparators/ShortShortComparator.java new file mode 100644 index 0000000..ccd7862 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/comparators/ShortShortComparator.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ByteCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ByteCursor.java new file mode 100644 index 0000000..e0b695d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ByteCursor.java @@ -0,0 +1,19 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/CharByteCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/CharByteCursor.java new file mode 100644 index 0000000..9e36f8f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/CharByteCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/CharCharCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/CharCharCursor.java new file mode 100644 index 0000000..079a268 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/CharCharCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/CharCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/CharCursor.java new file mode 100644 index 0000000..20a0ec5 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/CharCursor.java @@ -0,0 +1,19 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/CharDoubleCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/CharDoubleCursor.java new file mode 100644 index 0000000..5cdcd58 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/CharDoubleCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/CharFloatCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/CharFloatCursor.java new file mode 100644 index 0000000..109f0ae --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/CharFloatCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/CharIntCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/CharIntCursor.java new file mode 100644 index 0000000..b706f1f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/CharIntCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/CharLongCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/CharLongCursor.java new file mode 100644 index 0000000..1d5259f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/CharLongCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/CharObjectCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/CharObjectCursor.java new file mode 100644 index 0000000..fa98d62 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/CharObjectCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/CharShortCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/CharShortCursor.java new file mode 100644 index 0000000..d6407c8 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/CharShortCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/DoubleCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/DoubleCursor.java new file mode 100644 index 0000000..cc4f42c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/DoubleCursor.java @@ -0,0 +1,19 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/FloatCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/FloatCursor.java new file mode 100644 index 0000000..417f27e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/FloatCursor.java @@ -0,0 +1,19 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/IntByteCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/IntByteCursor.java new file mode 100644 index 0000000..b0dede7 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/IntByteCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/IntCharCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/IntCharCursor.java new file mode 100644 index 0000000..d49fd00 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/IntCharCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/IntCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/IntCursor.java new file mode 100644 index 0000000..8fd3478 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/IntCursor.java @@ -0,0 +1,19 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/IntDoubleCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/IntDoubleCursor.java new file mode 100644 index 0000000..03e5688 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/IntDoubleCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/IntFloatCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/IntFloatCursor.java new file mode 100644 index 0000000..0cbc76e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/IntFloatCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/IntIntCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/IntIntCursor.java new file mode 100644 index 0000000..c02ea79 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/IntIntCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/IntLongCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/IntLongCursor.java new file mode 100644 index 0000000..299ad71 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/IntLongCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/IntObjectCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/IntObjectCursor.java new file mode 100644 index 0000000..673a06d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/IntObjectCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/IntShortCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/IntShortCursor.java new file mode 100644 index 0000000..7f72eba --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/IntShortCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/LongByteCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/LongByteCursor.java new file mode 100644 index 0000000..74cdbd9 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/LongByteCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/LongCharCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/LongCharCursor.java new file mode 100644 index 0000000..800389a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/LongCharCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/LongCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/LongCursor.java new file mode 100644 index 0000000..53da311 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/LongCursor.java @@ -0,0 +1,19 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/LongDoubleCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/LongDoubleCursor.java new file mode 100644 index 0000000..ed4ceef --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/LongDoubleCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/LongFloatCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/LongFloatCursor.java new file mode 100644 index 0000000..0ff3ebb --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/LongFloatCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/LongIntCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/LongIntCursor.java new file mode 100644 index 0000000..202d409 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/LongIntCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/LongLongCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/LongLongCursor.java new file mode 100644 index 0000000..824852f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/LongLongCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/LongObjectCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/LongObjectCursor.java new file mode 100644 index 0000000..26f413c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/LongObjectCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/LongShortCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/LongShortCursor.java new file mode 100644 index 0000000..5d82d4e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/LongShortCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ObjectByteCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ObjectByteCursor.java new file mode 100644 index 0000000..380b48a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ObjectByteCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ObjectCharCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ObjectCharCursor.java new file mode 100644 index 0000000..1e4df4f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ObjectCharCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ObjectCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ObjectCursor.java new file mode 100644 index 0000000..9f3ef5c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ObjectCursor.java @@ -0,0 +1,19 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ObjectDoubleCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ObjectDoubleCursor.java new file mode 100644 index 0000000..edc16ae --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ObjectDoubleCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ObjectFloatCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ObjectFloatCursor.java new file mode 100644 index 0000000..767777e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ObjectFloatCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ObjectIntCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ObjectIntCursor.java new file mode 100644 index 0000000..b83b367 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ObjectIntCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ObjectLongCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ObjectLongCursor.java new file mode 100644 index 0000000..4eba0b0 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ObjectLongCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ObjectObjectCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ObjectObjectCursor.java new file mode 100644 index 0000000..85514da --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ObjectObjectCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ObjectShortCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ObjectShortCursor.java new file mode 100644 index 0000000..30f6fa6 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ObjectShortCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ShortByteCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ShortByteCursor.java new file mode 100644 index 0000000..1a2bd55 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ShortByteCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ShortCharCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ShortCharCursor.java new file mode 100644 index 0000000..120fd56 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ShortCharCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ShortCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ShortCursor.java new file mode 100644 index 0000000..3711797 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ShortCursor.java @@ -0,0 +1,19 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ShortDoubleCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ShortDoubleCursor.java new file mode 100644 index 0000000..f4b2d38 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ShortDoubleCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ShortFloatCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ShortFloatCursor.java new file mode 100644 index 0000000..8a60507 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ShortFloatCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ShortIntCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ShortIntCursor.java new file mode 100644 index 0000000..997a679 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ShortIntCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ShortLongCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ShortLongCursor.java new file mode 100644 index 0000000..576e7f0 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ShortLongCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ShortObjectCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ShortObjectCursor.java new file mode 100644 index 0000000..485514f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ShortObjectCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/cursors/ShortShortCursor.java b/sources/main/java/com/carrotsearch/hppc/cursors/ShortShortCursor.java new file mode 100644 index 0000000..5fa724e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/cursors/ShortShortCursor.java @@ -0,0 +1,23 @@ +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/sources/main/java/com/carrotsearch/hppc/internals/SuppressForbidden.java b/sources/main/java/com/carrotsearch/hppc/internals/SuppressForbidden.java new file mode 100644 index 0000000..c746e0f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/internals/SuppressForbidden.java @@ -0,0 +1,20 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/predicates/ByteBytePredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ByteBytePredicate.java new file mode 100644 index 0000000..d381990 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ByteBytePredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ByteCharPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ByteCharPredicate.java new file mode 100644 index 0000000..7e1877c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ByteCharPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ByteDoublePredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ByteDoublePredicate.java new file mode 100644 index 0000000..598b185 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ByteDoublePredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ByteFloatPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ByteFloatPredicate.java new file mode 100644 index 0000000..0a7dde1 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ByteFloatPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ByteIntPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ByteIntPredicate.java new file mode 100644 index 0000000..8b523f5 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ByteIntPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ByteLongPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ByteLongPredicate.java new file mode 100644 index 0000000..bff16b8 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ByteLongPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ByteObjectPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ByteObjectPredicate.java new file mode 100644 index 0000000..322f132 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ByteObjectPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/BytePredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/BytePredicate.java new file mode 100644 index 0000000..16c023b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/BytePredicate.java @@ -0,0 +1,7 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ByteShortPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ByteShortPredicate.java new file mode 100644 index 0000000..975073f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ByteShortPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/CharBytePredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/CharBytePredicate.java new file mode 100644 index 0000000..1bd258e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/CharBytePredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/CharCharPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/CharCharPredicate.java new file mode 100644 index 0000000..b2142e9 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/CharCharPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/CharDoublePredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/CharDoublePredicate.java new file mode 100644 index 0000000..d7409d7 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/CharDoublePredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/CharFloatPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/CharFloatPredicate.java new file mode 100644 index 0000000..d2e3c89 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/CharFloatPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/CharIntPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/CharIntPredicate.java new file mode 100644 index 0000000..e68d9da --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/CharIntPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/CharLongPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/CharLongPredicate.java new file mode 100644 index 0000000..de3ed2d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/CharLongPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/CharObjectPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/CharObjectPredicate.java new file mode 100644 index 0000000..c85875e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/CharObjectPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/CharPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/CharPredicate.java new file mode 100644 index 0000000..efe6e9d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/CharPredicate.java @@ -0,0 +1,7 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/CharShortPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/CharShortPredicate.java new file mode 100644 index 0000000..54d462c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/CharShortPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/DoubleBytePredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/DoubleBytePredicate.java new file mode 100644 index 0000000..1ab1d41 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/DoubleBytePredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/DoubleCharPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/DoubleCharPredicate.java new file mode 100644 index 0000000..ac7f394 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/DoubleCharPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/DoubleDoublePredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/DoubleDoublePredicate.java new file mode 100644 index 0000000..d3e3948 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/DoubleDoublePredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/DoubleFloatPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/DoubleFloatPredicate.java new file mode 100644 index 0000000..1a0343d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/DoubleFloatPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/DoubleIntPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/DoubleIntPredicate.java new file mode 100644 index 0000000..a1a135b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/DoubleIntPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/DoubleLongPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/DoubleLongPredicate.java new file mode 100644 index 0000000..2014b2a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/DoubleLongPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/DoubleObjectPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/DoubleObjectPredicate.java new file mode 100644 index 0000000..290912f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/DoubleObjectPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/DoublePredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/DoublePredicate.java new file mode 100644 index 0000000..57227e9 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/DoublePredicate.java @@ -0,0 +1,7 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/DoubleShortPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/DoubleShortPredicate.java new file mode 100644 index 0000000..6ee48db --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/DoubleShortPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/FloatBytePredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/FloatBytePredicate.java new file mode 100644 index 0000000..07a763b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/FloatBytePredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/FloatCharPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/FloatCharPredicate.java new file mode 100644 index 0000000..a14461d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/FloatCharPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/FloatDoublePredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/FloatDoublePredicate.java new file mode 100644 index 0000000..8c8dda0 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/FloatDoublePredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/FloatFloatPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/FloatFloatPredicate.java new file mode 100644 index 0000000..fae9df7 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/FloatFloatPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/FloatIntPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/FloatIntPredicate.java new file mode 100644 index 0000000..dd37320 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/FloatIntPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/FloatLongPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/FloatLongPredicate.java new file mode 100644 index 0000000..ed1232a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/FloatLongPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/FloatObjectPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/FloatObjectPredicate.java new file mode 100644 index 0000000..a74dca8 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/FloatObjectPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/FloatPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/FloatPredicate.java new file mode 100644 index 0000000..eb72886 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/FloatPredicate.java @@ -0,0 +1,7 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/FloatShortPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/FloatShortPredicate.java new file mode 100644 index 0000000..1a61beb --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/FloatShortPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/IntBytePredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/IntBytePredicate.java new file mode 100644 index 0000000..3bfa0aa --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/IntBytePredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/IntCharPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/IntCharPredicate.java new file mode 100644 index 0000000..c262f41 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/IntCharPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/IntDoublePredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/IntDoublePredicate.java new file mode 100644 index 0000000..3ec64db --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/IntDoublePredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/IntFloatPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/IntFloatPredicate.java new file mode 100644 index 0000000..449b5f0 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/IntFloatPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/IntIntPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/IntIntPredicate.java new file mode 100644 index 0000000..384d33c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/IntIntPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/IntLongPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/IntLongPredicate.java new file mode 100644 index 0000000..f3d4811 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/IntLongPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/IntObjectPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/IntObjectPredicate.java new file mode 100644 index 0000000..5a8063a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/IntObjectPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/IntPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/IntPredicate.java new file mode 100644 index 0000000..77d1c38 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/IntPredicate.java @@ -0,0 +1,7 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/IntShortPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/IntShortPredicate.java new file mode 100644 index 0000000..443642d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/IntShortPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/LongBytePredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/LongBytePredicate.java new file mode 100644 index 0000000..dce65d8 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/LongBytePredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/LongCharPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/LongCharPredicate.java new file mode 100644 index 0000000..f2974d7 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/LongCharPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/LongDoublePredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/LongDoublePredicate.java new file mode 100644 index 0000000..34947e0 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/LongDoublePredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/LongFloatPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/LongFloatPredicate.java new file mode 100644 index 0000000..c0bf1e5 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/LongFloatPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/LongIntPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/LongIntPredicate.java new file mode 100644 index 0000000..a31d293 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/LongIntPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/LongLongPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/LongLongPredicate.java new file mode 100644 index 0000000..4531519 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/LongLongPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/LongObjectPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/LongObjectPredicate.java new file mode 100644 index 0000000..fef9bbc --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/LongObjectPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/LongPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/LongPredicate.java new file mode 100644 index 0000000..52f0b87 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/LongPredicate.java @@ -0,0 +1,7 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/LongShortPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/LongShortPredicate.java new file mode 100644 index 0000000..5621fd7 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/LongShortPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ObjectBytePredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ObjectBytePredicate.java new file mode 100644 index 0000000..5765790 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ObjectBytePredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ObjectCharPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ObjectCharPredicate.java new file mode 100644 index 0000000..2c108db --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ObjectCharPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ObjectDoublePredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ObjectDoublePredicate.java new file mode 100644 index 0000000..e8d4ff5 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ObjectDoublePredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ObjectFloatPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ObjectFloatPredicate.java new file mode 100644 index 0000000..166d0c5 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ObjectFloatPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ObjectIntPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ObjectIntPredicate.java new file mode 100644 index 0000000..d683332 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ObjectIntPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ObjectLongPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ObjectLongPredicate.java new file mode 100644 index 0000000..bd97d95 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ObjectLongPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ObjectObjectPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ObjectObjectPredicate.java new file mode 100644 index 0000000..6970aa6 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ObjectObjectPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ObjectPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ObjectPredicate.java new file mode 100644 index 0000000..c942665 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ObjectPredicate.java @@ -0,0 +1,7 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ObjectShortPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ObjectShortPredicate.java new file mode 100644 index 0000000..2049fab --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ObjectShortPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ShortBytePredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ShortBytePredicate.java new file mode 100644 index 0000000..86305fa --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ShortBytePredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ShortCharPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ShortCharPredicate.java new file mode 100644 index 0000000..14a726e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ShortCharPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ShortDoublePredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ShortDoublePredicate.java new file mode 100644 index 0000000..1fdd873 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ShortDoublePredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ShortFloatPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ShortFloatPredicate.java new file mode 100644 index 0000000..4a55b96 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ShortFloatPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ShortIntPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ShortIntPredicate.java new file mode 100644 index 0000000..039c59a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ShortIntPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ShortLongPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ShortLongPredicate.java new file mode 100644 index 0000000..7036942 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ShortLongPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ShortObjectPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ShortObjectPredicate.java new file mode 100644 index 0000000..1b4bf8b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ShortObjectPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ShortPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ShortPredicate.java new file mode 100644 index 0000000..40fa91c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ShortPredicate.java @@ -0,0 +1,7 @@ +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/sources/main/java/com/carrotsearch/hppc/predicates/ShortShortPredicate.java b/sources/main/java/com/carrotsearch/hppc/predicates/ShortShortPredicate.java new file mode 100644 index 0000000..51d7b0a --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/predicates/ShortShortPredicate.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ByteByteProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ByteByteProcedure.java new file mode 100644 index 0000000..bd4a18e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ByteByteProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ByteCharProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ByteCharProcedure.java new file mode 100644 index 0000000..6b4c5de --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ByteCharProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ByteDoubleProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ByteDoubleProcedure.java new file mode 100644 index 0000000..3269170 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ByteDoubleProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ByteFloatProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ByteFloatProcedure.java new file mode 100644 index 0000000..011427e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ByteFloatProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ByteIntProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ByteIntProcedure.java new file mode 100644 index 0000000..aca3dc2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ByteIntProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ByteLongProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ByteLongProcedure.java new file mode 100644 index 0000000..01e9239 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ByteLongProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ByteObjectProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ByteObjectProcedure.java new file mode 100644 index 0000000..8d954b0 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ByteObjectProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ByteProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ByteProcedure.java new file mode 100644 index 0000000..9959f0d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ByteProcedure.java @@ -0,0 +1,7 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ByteShortProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ByteShortProcedure.java new file mode 100644 index 0000000..83e34cc --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ByteShortProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/CharByteProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/CharByteProcedure.java new file mode 100644 index 0000000..58fa2d4 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/CharByteProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/CharCharProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/CharCharProcedure.java new file mode 100644 index 0000000..2cd5d29 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/CharCharProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/CharDoubleProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/CharDoubleProcedure.java new file mode 100644 index 0000000..091e5c9 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/CharDoubleProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/CharFloatProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/CharFloatProcedure.java new file mode 100644 index 0000000..73b5453 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/CharFloatProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/CharIntProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/CharIntProcedure.java new file mode 100644 index 0000000..8118508 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/CharIntProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/CharLongProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/CharLongProcedure.java new file mode 100644 index 0000000..9657edd --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/CharLongProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/CharObjectProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/CharObjectProcedure.java new file mode 100644 index 0000000..145e6f7 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/CharObjectProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/CharProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/CharProcedure.java new file mode 100644 index 0000000..2bd7e5d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/CharProcedure.java @@ -0,0 +1,7 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/CharShortProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/CharShortProcedure.java new file mode 100644 index 0000000..50b2584 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/CharShortProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/DoubleByteProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/DoubleByteProcedure.java new file mode 100644 index 0000000..70adadf --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/DoubleByteProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/DoubleCharProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/DoubleCharProcedure.java new file mode 100644 index 0000000..101e0d9 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/DoubleCharProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/DoubleDoubleProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/DoubleDoubleProcedure.java new file mode 100644 index 0000000..9ba2012 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/DoubleDoubleProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/DoubleFloatProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/DoubleFloatProcedure.java new file mode 100644 index 0000000..3dabb0c --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/DoubleFloatProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/DoubleIntProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/DoubleIntProcedure.java new file mode 100644 index 0000000..be62b25 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/DoubleIntProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/DoubleLongProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/DoubleLongProcedure.java new file mode 100644 index 0000000..a648e97 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/DoubleLongProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/DoubleObjectProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/DoubleObjectProcedure.java new file mode 100644 index 0000000..5973dd2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/DoubleObjectProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/DoubleProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/DoubleProcedure.java new file mode 100644 index 0000000..c23a561 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/DoubleProcedure.java @@ -0,0 +1,7 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/DoubleShortProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/DoubleShortProcedure.java new file mode 100644 index 0000000..aea0b9e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/DoubleShortProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/FloatByteProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/FloatByteProcedure.java new file mode 100644 index 0000000..df914d4 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/FloatByteProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/FloatCharProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/FloatCharProcedure.java new file mode 100644 index 0000000..8d54dba --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/FloatCharProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/FloatDoubleProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/FloatDoubleProcedure.java new file mode 100644 index 0000000..e398c47 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/FloatDoubleProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/FloatFloatProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/FloatFloatProcedure.java new file mode 100644 index 0000000..00cfa5b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/FloatFloatProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/FloatIntProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/FloatIntProcedure.java new file mode 100644 index 0000000..f09f375 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/FloatIntProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/FloatLongProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/FloatLongProcedure.java new file mode 100644 index 0000000..ee7e3d8 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/FloatLongProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/FloatObjectProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/FloatObjectProcedure.java new file mode 100644 index 0000000..3e23e19 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/FloatObjectProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/FloatProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/FloatProcedure.java new file mode 100644 index 0000000..1754343 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/FloatProcedure.java @@ -0,0 +1,7 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/FloatShortProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/FloatShortProcedure.java new file mode 100644 index 0000000..9ae0b7f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/FloatShortProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/IntByteProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/IntByteProcedure.java new file mode 100644 index 0000000..2232f85 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/IntByteProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/IntCharProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/IntCharProcedure.java new file mode 100644 index 0000000..f5f1d50 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/IntCharProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/IntDoubleProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/IntDoubleProcedure.java new file mode 100644 index 0000000..1fca14b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/IntDoubleProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/IntFloatProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/IntFloatProcedure.java new file mode 100644 index 0000000..8c91a10 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/IntFloatProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/IntIntProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/IntIntProcedure.java new file mode 100644 index 0000000..b870c6b --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/IntIntProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/IntLongProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/IntLongProcedure.java new file mode 100644 index 0000000..741858f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/IntLongProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/IntObjectProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/IntObjectProcedure.java new file mode 100644 index 0000000..575a1f2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/IntObjectProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/IntProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/IntProcedure.java new file mode 100644 index 0000000..4fab32f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/IntProcedure.java @@ -0,0 +1,7 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/IntShortProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/IntShortProcedure.java new file mode 100644 index 0000000..c3bac03 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/IntShortProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/LongByteProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/LongByteProcedure.java new file mode 100644 index 0000000..670af78 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/LongByteProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/LongCharProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/LongCharProcedure.java new file mode 100644 index 0000000..5ca456d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/LongCharProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/LongDoubleProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/LongDoubleProcedure.java new file mode 100644 index 0000000..2aaa2c3 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/LongDoubleProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/LongFloatProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/LongFloatProcedure.java new file mode 100644 index 0000000..6b88a57 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/LongFloatProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/LongIntProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/LongIntProcedure.java new file mode 100644 index 0000000..297f387 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/LongIntProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/LongLongProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/LongLongProcedure.java new file mode 100644 index 0000000..f9f1fba --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/LongLongProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/LongObjectProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/LongObjectProcedure.java new file mode 100644 index 0000000..2e0a8f8 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/LongObjectProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/LongProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/LongProcedure.java new file mode 100644 index 0000000..9397055 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/LongProcedure.java @@ -0,0 +1,7 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/LongShortProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/LongShortProcedure.java new file mode 100644 index 0000000..2ad0865 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/LongShortProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ObjectByteProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ObjectByteProcedure.java new file mode 100644 index 0000000..85d6913 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ObjectByteProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ObjectCharProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ObjectCharProcedure.java new file mode 100644 index 0000000..3393875 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ObjectCharProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ObjectDoubleProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ObjectDoubleProcedure.java new file mode 100644 index 0000000..4d9345d --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ObjectDoubleProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ObjectFloatProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ObjectFloatProcedure.java new file mode 100644 index 0000000..237c286 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ObjectFloatProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ObjectIntProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ObjectIntProcedure.java new file mode 100644 index 0000000..11bb001 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ObjectIntProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ObjectLongProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ObjectLongProcedure.java new file mode 100644 index 0000000..9aec4cd --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ObjectLongProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ObjectObjectProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ObjectObjectProcedure.java new file mode 100644 index 0000000..382d76f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ObjectObjectProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ObjectProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ObjectProcedure.java new file mode 100644 index 0000000..cb0f00f --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ObjectProcedure.java @@ -0,0 +1,7 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ObjectShortProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ObjectShortProcedure.java new file mode 100644 index 0000000..871cac4 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ObjectShortProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ShortByteProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ShortByteProcedure.java new file mode 100644 index 0000000..a9b86f2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ShortByteProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ShortCharProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ShortCharProcedure.java new file mode 100644 index 0000000..2ebbb81 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ShortCharProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ShortDoubleProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ShortDoubleProcedure.java new file mode 100644 index 0000000..ada12e2 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ShortDoubleProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ShortFloatProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ShortFloatProcedure.java new file mode 100644 index 0000000..a99af69 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ShortFloatProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ShortIntProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ShortIntProcedure.java new file mode 100644 index 0000000..e633c55 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ShortIntProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ShortLongProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ShortLongProcedure.java new file mode 100644 index 0000000..99275f7 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ShortLongProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ShortObjectProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ShortObjectProcedure.java new file mode 100644 index 0000000..5ab91d0 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ShortObjectProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ShortProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ShortProcedure.java new file mode 100644 index 0000000..d3482c7 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ShortProcedure.java @@ -0,0 +1,7 @@ +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/sources/main/java/com/carrotsearch/hppc/procedures/ShortShortProcedure.java b/sources/main/java/com/carrotsearch/hppc/procedures/ShortShortProcedure.java new file mode 100644 index 0000000..05873e5 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/procedures/ShortShortProcedure.java @@ -0,0 +1,9 @@ +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/sources/main/java/com/carrotsearch/hppc/sorting/IndirectSort.java b/sources/main/java/com/carrotsearch/hppc/sorting/IndirectSort.java new file mode 100644 index 0000000..2d4722e --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/sorting/IndirectSort.java @@ -0,0 +1,133 @@ +/* + * 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/sources/main/java/com/carrotsearch/hppc/sorting/QuickSort.java b/sources/main/java/com/carrotsearch/hppc/sorting/QuickSort.java new file mode 100644 index 0000000..b547dc6 --- /dev/null +++ b/sources/main/java/com/carrotsearch/hppc/sorting/QuickSort.java @@ -0,0 +1,181 @@ +/* + * 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/sources/main/java/jdk_internal/bidi/Annotation.java b/sources/main/java/jdk_internal/bidi/Annotation.java new file mode 100644 index 0000000..b0b6ed7 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/Annotation.java @@ -0,0 +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; + +}; diff --git a/sources/main/java/jdk_internal/bidi/AttributedCharacterIterator.java b/sources/main/java/jdk_internal/bidi/AttributedCharacterIterator.java new file mode 100644 index 0000000..00223d3 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/AttributedCharacterIterator.java @@ -0,0 +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(); +}; diff --git a/sources/main/java/jdk_internal/bidi/AttributedString.java b/sources/main/java/jdk_internal/bidi/AttributedString.java new file mode 100644 index 0000000..cdabbc7 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/AttributedString.java @@ -0,0 +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(); + } +} diff --git a/sources/main/java/jdk_internal/bidi/Bidi.java b/sources/main/java/jdk_internal/bidi/Bidi.java new file mode 100644 index 0000000..8c698be --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/Bidi.java @@ -0,0 +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.bidi.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/sources/main/java/jdk_internal/bidi/CharacterIterator.java b/sources/main/java/jdk_internal/bidi/CharacterIterator.java new file mode 100644 index 0000000..6831284 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/CharacterIterator.java @@ -0,0 +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(); + +} diff --git a/sources/main/java/jdk_internal/bidi/Normalizer.java b/sources/main/java/jdk_internal/bidi/Normalizer.java new file mode 100644 index 0000000..8029bd3 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/Normalizer.java @@ -0,0 +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.bidi.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/sources/main/java/jdk_internal/bidi/NumericShaper.java b/sources/main/java/jdk_internal/bidi/NumericShaper.java new file mode 100644 index 0000000..a3f5f80 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/NumericShaper.java @@ -0,0 +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(); + } +} diff --git a/sources/main/java/jdk_internal/bidi/ParseException.java b/sources/main/java/jdk_internal/bidi/ParseException.java new file mode 100644 index 0000000..3d3e9d0 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/ParseException.java @@ -0,0 +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; +} diff --git a/sources/main/java/jdk_internal/bidi/SunNormalizer.java b/sources/main/java/jdk_internal/bidi/SunNormalizer.java new file mode 100644 index 0000000..d1f57bc --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/SunNormalizer.java @@ -0,0 +1,96 @@ +/* + * 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.bidi.icu.lang.UCharacter; +import jdk_internal.bidi.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); + } +} diff --git a/sources/main/java/jdk_internal/bidi/TextAttribute.java b/sources/main/java/jdk_internal/bidi/TextAttribute.java new file mode 100644 index 0000000..003e85b --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/TextAttribute.java @@ -0,0 +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); +} diff --git a/sources/main/java/jdk_internal/bidi/icu/impl/BMPSet.java b/sources/main/java/jdk_internal/bidi/icu/impl/BMPSet.java new file mode 100644 index 0000000..c7a9999 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/impl/BMPSet.java @@ -0,0 +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.bidi.icu.impl; + +import jdk_internal.bidi.icu.text.UnicodeSet.SpanCondition; +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/impl/CharTrie.java b/sources/main/java/jdk_internal/bidi/icu/impl/CharTrie.java new file mode 100644 index 0000000..aab129b --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/impl/CharTrie.java @@ -0,0 +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.bidi.icu.impl; + +import java.io.DataInputStream; +import java.io.InputStream; + +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/impl/CharacterIteratorWrapper.java b/sources/main/java/jdk_internal/bidi/icu/impl/CharacterIteratorWrapper.java new file mode 100644 index 0000000..7d6fced --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/impl/CharacterIteratorWrapper.java @@ -0,0 +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.bidi.icu.impl; + +import jdk_internal.bidi.CharacterIterator; +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/impl/ICUBinary.java b/sources/main/java/jdk_internal/bidi/icu/impl/ICUBinary.java new file mode 100644 index 0000000..dbbe0b3 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/impl/ICUBinary.java @@ -0,0 +1,303 @@ +/* + * 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.bidi.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.bidi.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"; +} diff --git a/sources/main/java/jdk_internal/bidi/icu/impl/Norm2AllModes.java b/sources/main/java/jdk_internal/bidi/icu/impl/Norm2AllModes.java new file mode 100644 index 0000000..aaabf2f --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/impl/Norm2AllModes.java @@ -0,0 +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.bidi.icu.impl; + +import java.io.IOException; + +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/impl/NormalizerImpl.java b/sources/main/java/jdk_internal/bidi/icu/impl/NormalizerImpl.java new file mode 100644 index 0000000..920aa7a --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/impl/NormalizerImpl.java @@ -0,0 +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.bidi.icu.impl; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import jdk_internal.bidi.icu.lang.UCharacter; +import jdk_internal.bidi.icu.text.Normalizer2; +import jdk_internal.bidi.icu.text.UTF16; +import jdk_internal.bidi.icu.util.CodePointTrie; +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/impl/Punycode.java b/sources/main/java/jdk_internal/bidi/icu/impl/Punycode.java new file mode 100644 index 0000000..c6491b0 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/impl/Punycode.java @@ -0,0 +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.bidi.icu.impl; + +import java.text.ParseException; + +import jdk_internal.bidi.icu.lang.UCharacter; +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/impl/ReplaceableUCharacterIterator.java b/sources/main/java/jdk_internal/bidi/icu/impl/ReplaceableUCharacterIterator.java new file mode 100644 index 0000000..3e09072 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/impl/ReplaceableUCharacterIterator.java @@ -0,0 +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.bidi.icu.impl; + +import jdk_internal.bidi.icu.text.Replaceable; +import jdk_internal.bidi.icu.text.ReplaceableString; +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/impl/StringPrepDataReader.java b/sources/main/java/jdk_internal/bidi/icu/impl/StringPrepDataReader.java new file mode 100644 index 0000000..98aafc7 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/impl/StringPrepDataReader.java @@ -0,0 +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.bidi.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/sources/main/java/jdk_internal/bidi/icu/impl/Trie.java b/sources/main/java/jdk_internal/bidi/icu/impl/Trie.java new file mode 100644 index 0000000..2b5038e --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/impl/Trie.java @@ -0,0 +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.bidi.icu.impl; + +import java.io.DataInputStream; +import java.io.InputStream; + +import jdk_internal.bidi.icu.lang.UCharacter; +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/impl/Trie2.java b/sources/main/java/jdk_internal/bidi/icu/impl/Trie2.java new file mode 100644 index 0000000..94c72fc --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/impl/Trie2.java @@ -0,0 +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.bidi.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/sources/main/java/jdk_internal/bidi/icu/impl/Trie2_16.java b/sources/main/java/jdk_internal/bidi/icu/impl/Trie2_16.java new file mode 100644 index 0000000..770754a --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/impl/Trie2_16.java @@ -0,0 +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.bidi.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/sources/main/java/jdk_internal/bidi/icu/impl/UBiDiProps.java b/sources/main/java/jdk_internal/bidi/icu/impl/UBiDiProps.java new file mode 100644 index 0000000..b81ec6b --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/impl/UBiDiProps.java @@ -0,0 +1,270 @@ +/* + * 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.bidi.icu.impl; + +import java.io.IOException; +import java.nio.ByteBuffer; +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/impl/UCharacterProperty.java b/sources/main/java/jdk_internal/bidi/icu/impl/UCharacterProperty.java new file mode 100644 index 0000000..7b77640 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/impl/UCharacterProperty.java @@ -0,0 +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.bidi.icu.impl; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Iterator; + +import jdk_internal.bidi.icu.lang.UCharacter.HangulSyllableType; +import jdk_internal.bidi.icu.lang.UCharacter.NumericType; +import jdk_internal.bidi.icu.text.UTF16; +import jdk_internal.bidi.icu.text.UnicodeSet; +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/impl/UnicodeSetStringSpan.java b/sources/main/java/jdk_internal/bidi/icu/impl/UnicodeSetStringSpan.java new file mode 100644 index 0000000..c89f8b0 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/impl/UnicodeSetStringSpan.java @@ -0,0 +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.bidi.icu.impl; + +import java.util.ArrayList; + +import jdk_internal.bidi.icu.text.UTF16; +import jdk_internal.bidi.icu.text.UnicodeSet; +import jdk_internal.bidi.icu.text.UnicodeSet.SpanCondition; +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/impl/Utility.java b/sources/main/java/jdk_internal/bidi/icu/impl/Utility.java new file mode 100644 index 0000000..74d0852 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/impl/Utility.java @@ -0,0 +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.bidi.icu.impl; + +import java.io.IOException; +import java.util.Locale; + +import jdk_internal.bidi.icu.lang.UCharacter; +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/lang/UCharacter.java b/sources/main/java/jdk_internal/bidi/icu/lang/UCharacter.java new file mode 100644 index 0000000..3c0fce0 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/lang/UCharacter.java @@ -0,0 +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.bidi.icu.lang; + +import jdk_internal.bidi.icu.impl.UBiDiProps; +import jdk_internal.bidi.icu.impl.UCharacterProperty; +import jdk_internal.bidi.icu.text.Normalizer2; +import jdk_internal.bidi.icu.text.UTF16; +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/lang/UCharacterDirection.java b/sources/main/java/jdk_internal/bidi/icu/lang/UCharacterDirection.java new file mode 100644 index 0000000..7a3c803 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/lang/UCharacterDirection.java @@ -0,0 +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.bidi.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/sources/main/java/jdk_internal/bidi/icu/lang/UCharacterEnums.java b/sources/main/java/jdk_internal/bidi/icu/lang/UCharacterEnums.java new file mode 100644 index 0000000..dd09e6f --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/lang/UCharacterEnums.java @@ -0,0 +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.bidi.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/sources/main/java/jdk_internal/bidi/icu/text/BidiBase.java b/sources/main/java/jdk_internal/bidi/icu/text/BidiBase.java new file mode 100644 index 0000000..ce33152 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/text/BidiBase.java @@ -0,0 +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.bidi.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.bidi.icu.impl.UBiDiProps; +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/text/BidiLine.java b/sources/main/java/jdk_internal/bidi/icu/text/BidiLine.java new file mode 100644 index 0000000..19d8e73 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/text/BidiLine.java @@ -0,0 +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.bidi.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/sources/main/java/jdk_internal/bidi/icu/text/BidiRun.java b/sources/main/java/jdk_internal/bidi/icu/text/BidiRun.java new file mode 100644 index 0000000..97b0e81 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/text/BidiRun.java @@ -0,0 +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.bidi.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/sources/main/java/jdk_internal/bidi/icu/text/BidiWriter.java b/sources/main/java/jdk_internal/bidi/icu/text/BidiWriter.java new file mode 100644 index 0000000..e9fa71e --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/text/BidiWriter.java @@ -0,0 +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.bidi.icu.text; + +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/text/FilteredNormalizer2.java b/sources/main/java/jdk_internal/bidi/icu/text/FilteredNormalizer2.java new file mode 100644 index 0000000..3f9046b --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/text/FilteredNormalizer2.java @@ -0,0 +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.bidi.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/sources/main/java/jdk_internal/bidi/icu/text/Normalizer2.java b/sources/main/java/jdk_internal/bidi/icu/text/Normalizer2.java new file mode 100644 index 0000000..f833478 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/text/Normalizer2.java @@ -0,0 +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.bidi.icu.text; + +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/text/NormalizerBase.java b/sources/main/java/jdk_internal/bidi/icu/text/NormalizerBase.java new file mode 100644 index 0000000..5887aec --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/text/NormalizerBase.java @@ -0,0 +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.bidi.icu.text; + +import jdk_internal.bidi.CharacterIterator; +import jdk_internal.bidi.Normalizer; +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/text/Replaceable.java b/sources/main/java/jdk_internal/bidi/icu/text/Replaceable.java new file mode 100644 index 0000000..22c7fff --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/text/Replaceable.java @@ -0,0 +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.bidi.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/sources/main/java/jdk_internal/bidi/icu/text/ReplaceableString.java b/sources/main/java/jdk_internal/bidi/icu/text/ReplaceableString.java new file mode 100644 index 0000000..661ecca --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/text/ReplaceableString.java @@ -0,0 +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.bidi.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/sources/main/java/jdk_internal/bidi/icu/text/StringPrep.java b/sources/main/java/jdk_internal/bidi/icu/text/StringPrep.java new file mode 100644 index 0000000..da41151 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/text/StringPrep.java @@ -0,0 +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.bidi.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.bidi.icu.impl.CharTrie; +import jdk_internal.bidi.icu.impl.StringPrepDataReader; +import jdk_internal.bidi.icu.impl.Trie; +import jdk_internal.bidi.icu.lang.UCharacter; +import jdk_internal.bidi.icu.lang.UCharacterDirection; +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/text/UCharacterIterator.java b/sources/main/java/jdk_internal/bidi/icu/text/UCharacterIterator.java new file mode 100644 index 0000000..656b4df --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/text/UCharacterIterator.java @@ -0,0 +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.bidi.icu.text; + +import jdk_internal.bidi.CharacterIterator; +import jdk_internal.bidi.icu.impl.CharacterIteratorWrapper; +import jdk_internal.bidi.icu.impl.ReplaceableUCharacterIterator; +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/text/UTF16.java b/sources/main/java/jdk_internal/bidi/icu/text/UTF16.java new file mode 100644 index 0000000..5a39fed --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/text/UTF16.java @@ -0,0 +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.bidi.icu.text; + +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/text/UnicodeSet.java b/sources/main/java/jdk_internal/bidi/icu/text/UnicodeSet.java new file mode 100644 index 0000000..5797c6b --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/text/UnicodeSet.java @@ -0,0 +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.bidi.icu.text; + +import java.text.ParsePosition; +import java.util.ArrayList; +import java.util.TreeSet; + +import jdk_internal.bidi.icu.impl.BMPSet; +import jdk_internal.bidi.icu.impl.UCharacterProperty; +import jdk_internal.bidi.icu.impl.UnicodeSetStringSpan; +import jdk_internal.bidi.icu.impl.Utility; +import jdk_internal.bidi.icu.lang.UCharacter; +import jdk_internal.bidi.icu.util.OutputInt; +import jdk_internal.bidi.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/sources/main/java/jdk_internal/bidi/icu/util/CodePointMap.java b/sources/main/java/jdk_internal/bidi/icu/util/CodePointMap.java new file mode 100644 index 0000000..358b631 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/util/CodePointMap.java @@ -0,0 +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.bidi.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/sources/main/java/jdk_internal/bidi/icu/util/CodePointTrie.java b/sources/main/java/jdk_internal/bidi/icu/util/CodePointTrie.java new file mode 100644 index 0000000..e84c2d0 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/util/CodePointTrie.java @@ -0,0 +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.bidi.icu.util; + +import static jdk_internal.bidi.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.bidi.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/sources/main/java/jdk_internal/bidi/icu/util/OutputInt.java b/sources/main/java/jdk_internal/bidi/icu/util/OutputInt.java new file mode 100644 index 0000000..433bf28 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/util/OutputInt.java @@ -0,0 +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.bidi.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/sources/main/java/jdk_internal/bidi/icu/util/VersionInfo.java b/sources/main/java/jdk_internal/bidi/icu/util/VersionInfo.java new file mode 100644 index 0000000..d6abf22 --- /dev/null +++ b/sources/main/java/jdk_internal/bidi/icu/util/VersionInfo.java @@ -0,0 +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.bidi.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/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglerBidiReorder.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglerBidiReorder.java new file mode 100644 index 0000000..a966ff9 --- /dev/null +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglerBidiReorder.java @@ -0,0 +1,87 @@ +package net.lax1dude.eaglercraft.v1_8; + +import jdk_internal.bidi.Bidi; + +/** + * 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. + * + */ +public class EaglerBidiReorder { + + /** + * Taken from minecraft 1.6 + */ + public static String bidiReorder(String par1Str) { + if (par1Str != null && Bidi.requiresBidi(par1Str.toCharArray(), 0, par1Str.length())) { + Bidi bidi = new Bidi(par1Str, -2); + byte[] abyte = new byte[bidi.getRunCount()]; + String[] astring = new String[abyte.length]; + int i; + + for (int j = 0; j < abyte.length; ++j) { + int k = bidi.getRunStart(j); + i = bidi.getRunLimit(j); + int l = bidi.getRunLevel(j); + String s1 = par1Str.substring(k, i); + abyte[j] = (byte) l; + astring[j] = s1; + } + + String[] astring1 = (String[]) astring.clone(); + Bidi.reorderVisually(abyte, 0, astring, 0, abyte.length); + StringBuilder stringbuilder = new StringBuilder(); + i = 0; + + while (i < astring.length) { + byte b0 = abyte[i]; + int i1 = 0; + + while (true) { + if (i1 < astring1.length) { + if (!astring1[i1].equals(astring[i])) { + ++i1; + continue; + } + + b0 = abyte[i1]; + } + + if ((b0 & 1) == 0) { + stringbuilder.append(astring[i]); + } else { + for (i1 = astring[i].length() - 1; i1 >= 0; --i1) { + char c0 = astring[i].charAt(i1); + + if (c0 == 40) { + c0 = 41; + } else if (c0 == 41) { + c0 = 40; + } + + stringbuilder.append(c0); + } + } + + ++i; + break; + } + } + + return stringbuilder.toString(); + } else { + return par1Str; + } + } + +} diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglerZLIB.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglerZLIB.java index 839a481..5df3d1c 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglerZLIB.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglerZLIB.java @@ -26,15 +26,33 @@ public class EaglerZLIB { public static OutputStream newDeflaterOutputStream(OutputStream os) throws IOException { return PlatformRuntime.newDeflaterOutputStream(os); } - + + public static int deflateFull(byte[] input, byte[] output) throws IOException { + return PlatformRuntime.deflateFull(input, 0, input.length, output, 0, output.length); + } + + public static int deflateFull(byte[] input, int inputOff, int inputLen, byte[] output, int outputOff, + int outputLen) throws IOException { + return PlatformRuntime.deflateFull(input, inputOff, inputLen, output, outputOff, outputLen); + } + public static OutputStream newGZIPOutputStream(OutputStream os) throws IOException { return PlatformRuntime.newGZIPOutputStream(os); } - + public static InputStream newInflaterInputStream(InputStream is) throws IOException { return PlatformRuntime.newInflaterInputStream(is); } - + + public static int inflateFull(byte[] input, byte[] output) throws IOException { + return PlatformRuntime.inflateFull(input, 0, input.length, output, 0, output.length); + } + + public static int inflateFull(byte[] input, int inputOff, int inputLen, byte[] output, int outputOff, + int outputLen) throws IOException { + return PlatformRuntime.inflateFull(input, inputOff, inputLen, output, outputOff, outputLen); + } + public static InputStream newGZIPInputStream(InputStream is) throws IOException { return PlatformRuntime.newGZIPInputStream(is); } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java index fe1163c..6d00bc7 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java +++ b/sources/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 = "u46"; + public static final String projectForkVersion = "u47"; 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 = "u46"; + public static final String projectOriginVersion = "u47"; public static final String projectOriginURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8"; // rest in peace // EPK Version Identifier - public static final String EPKVersionIdentifier = "u46"; // Set to null to disable EPK version check + public static final String EPKVersionIdentifier = "u47"; // 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 = 46; + public static final int updateBundlePackageVersionInt = 47; public static final String updateLatestLocalStorageKey = "latestUpdate_" + updateBundlePackageName; diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/ByteBuffer.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/ByteBuffer.java index 4ec39b1..9cbe2b2 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/ByteBuffer.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/ByteBuffer.java @@ -2,7 +2,7 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; /** - * 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 @@ -16,88 +16,100 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; * POSSIBILITY OF SUCH DAMAGE. * */ -public interface ByteBuffer extends Buffer { +public abstract class ByteBuffer implements Buffer { - ByteBuffer duplicate(); + public abstract ByteBuffer duplicate(); - byte get(); + public abstract byte get(); - ByteBuffer put(byte b); + public abstract ByteBuffer put(byte b); - byte get(int index); + public abstract byte get(int index); - ByteBuffer put(int index, byte b); + public abstract ByteBuffer put(int index, byte b); - ByteBuffer get(byte[] dst, int offset, int length); + public abstract ByteBuffer get(byte[] dst, int offset, int length); - ByteBuffer get(byte[] dst); + public abstract ByteBuffer get(byte[] dst); - ByteBuffer put(ByteBuffer src); + public abstract ByteBuffer put(ByteBuffer src); - ByteBuffer put(byte[] src, int offset, int length); + public abstract ByteBuffer put(byte[] src, int offset, int length); - ByteBuffer put(byte[] src); + public abstract ByteBuffer put(byte[] src); - char getChar(); + public abstract char getChar(); - ByteBuffer putChar(char value); + public abstract ByteBuffer putChar(char value); - char getChar(int index); + public abstract char getChar(int index); - ByteBuffer putChar(int index, char value); + public abstract ByteBuffer putChar(int index, char value); - short getShort(); + public abstract short getShort(); - ByteBuffer putShort(short value); + public abstract ByteBuffer putShort(short value); - short getShort(int index); + public abstract short getShort(int index); - ByteBuffer putShort(int index, short value); + public abstract ByteBuffer putShort(int index, short value); - ShortBuffer asShortBuffer(); + public abstract ShortBuffer asShortBuffer(); - int getInt(); + public abstract int getInt(); - ByteBuffer putInt(int value); + public abstract ByteBuffer putInt(int value); - int getInt(int index); + public abstract int getInt(int index); - ByteBuffer putInt(int index, int value); + public abstract ByteBuffer putInt(int index, int value); - IntBuffer asIntBuffer(); + public abstract IntBuffer asIntBuffer(); - long getLong(); + public abstract long getLong(); - ByteBuffer putLong(long value); + public abstract ByteBuffer putLong(long value); - long getLong(int index); + public abstract long getLong(int index); - ByteBuffer putLong(int index, long value); + public abstract ByteBuffer putLong(int index, long value); - float getFloat(); + public abstract float getFloat(); - ByteBuffer putFloat(float value); + public abstract ByteBuffer putFloat(float value); - float getFloat(int index); + public abstract float getFloat(int index); - ByteBuffer putFloat(int index, float value); + public abstract ByteBuffer putFloat(int index, float value); - FloatBuffer asFloatBuffer(); + public abstract FloatBuffer asFloatBuffer(); - ByteBuffer mark(); + public abstract ByteBuffer mark(); - ByteBuffer reset(); + public abstract ByteBuffer reset(); - ByteBuffer clear(); + public abstract ByteBuffer clear(); - ByteBuffer flip(); + public abstract ByteBuffer flip(); - ByteBuffer rewind(); + public abstract ByteBuffer rewind(); - ByteBuffer limit(int newLimit); + public abstract ByteBuffer limit(int newLimit); - ByteBuffer position(int newPosition); + public abstract int limit(); - byte[] array(); + public abstract ByteBuffer position(int newPosition); + + public abstract int position(); + + public abstract int remaining(); + + public abstract boolean hasRemaining(); + + public abstract int capacity(); + + public abstract boolean hasArray(); + + public abstract byte[] array(); } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/FloatBuffer.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/FloatBuffer.java index ecaf25f..a1f1bcc 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/FloatBuffer.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/FloatBuffer.java @@ -1,7 +1,7 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; /** - * 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 @@ -15,49 +15,61 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; * POSSIBILITY OF SUCH DAMAGE. * */ -public interface FloatBuffer extends Buffer { +public abstract class FloatBuffer implements Buffer { - FloatBuffer duplicate(); + public abstract FloatBuffer duplicate(); - float get(); + public abstract float get(); - FloatBuffer put(float b); + public abstract FloatBuffer put(float b); - float get(int index); + public abstract float get(int index); - FloatBuffer put(int index, float b); + public abstract FloatBuffer put(int index, float b); - float getElement(int index); + public abstract float getElement(int index); - void putElement(int index, float value); + public abstract void putElement(int index, float value); - FloatBuffer get(float[] dst, int offset, int length); + public abstract FloatBuffer get(float[] dst, int offset, int length); - FloatBuffer get(float[] dst); + public abstract FloatBuffer get(float[] dst); - FloatBuffer put(FloatBuffer src); + public abstract FloatBuffer put(FloatBuffer src); - FloatBuffer put(float[] src, int offset, int length); + public abstract FloatBuffer put(float[] src, int offset, int length); - FloatBuffer put(float[] src); + public abstract FloatBuffer put(float[] src); - boolean isDirect(); + public abstract boolean isDirect(); - FloatBuffer mark(); + public abstract FloatBuffer mark(); - FloatBuffer reset(); + public abstract FloatBuffer reset(); - FloatBuffer clear(); + public abstract FloatBuffer clear(); - FloatBuffer flip(); + public abstract FloatBuffer flip(); - FloatBuffer rewind(); + public abstract FloatBuffer rewind(); - FloatBuffer limit(int newLimit); + public abstract FloatBuffer limit(int newLimit); - FloatBuffer position(int newPosition); + public abstract int limit(); - float[] array(); + public abstract FloatBuffer position(int newPosition); + + public abstract int position(); + + public abstract int remaining(); + + public abstract boolean hasRemaining(); + + public abstract int capacity(); + + public abstract boolean hasArray(); + + public abstract float[] array(); } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/IntBuffer.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/IntBuffer.java index 0d96de5..c33590b 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/IntBuffer.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/IntBuffer.java @@ -15,49 +15,61 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; * POSSIBILITY OF SUCH DAMAGE. * */ -public interface IntBuffer extends Buffer { +public abstract class IntBuffer implements Buffer { - IntBuffer duplicate(); + public abstract IntBuffer duplicate(); - int get(); + public abstract int get(); - IntBuffer put(int b); + public abstract IntBuffer put(int b); - int get(int index); + public abstract int get(int index); - IntBuffer put(int index, int b); + public abstract IntBuffer put(int index, int b); - int getElement(int index); + public abstract int getElement(int index); - void putElement(int index, int value); + public abstract void putElement(int index, int value); - IntBuffer get(int[] dst, int offset, int length); + public abstract IntBuffer get(int[] dst, int offset, int length); - IntBuffer get(int[] dst); + public abstract IntBuffer get(int[] dst); - IntBuffer put(IntBuffer src); + public abstract IntBuffer put(IntBuffer src); - IntBuffer put(int[] src, int offset, int length); + public abstract IntBuffer put(int[] src, int offset, int length); - IntBuffer put(int[] src); + public abstract IntBuffer put(int[] src); - boolean isDirect(); + public abstract boolean isDirect(); - IntBuffer mark(); + public abstract IntBuffer mark(); - IntBuffer reset(); + public abstract IntBuffer reset(); - IntBuffer clear(); + public abstract IntBuffer clear(); - IntBuffer flip(); + public abstract IntBuffer flip(); - IntBuffer rewind(); + public abstract IntBuffer rewind(); - IntBuffer limit(int newLimit); + public abstract IntBuffer limit(int newLimit); - IntBuffer position(int newPosition); + public abstract int limit(); - int[] array(); + public abstract IntBuffer position(int newPosition); + + public abstract int position(); + + public abstract int remaining(); + + public abstract boolean hasRemaining(); + + public abstract int capacity(); + + public abstract boolean hasArray(); + + public abstract int[] array(); } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/ShortBuffer.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/ShortBuffer.java index 56e54a5..7816c12 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/ShortBuffer.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/ShortBuffer.java @@ -1,7 +1,7 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; /** - * 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 @@ -15,48 +15,60 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; * POSSIBILITY OF SUCH DAMAGE. * */ -public interface ShortBuffer extends Buffer { +public abstract class ShortBuffer implements Buffer { - ShortBuffer duplicate(); + public abstract ShortBuffer duplicate(); - short get(); + public abstract short get(); - ShortBuffer put(short b); + public abstract ShortBuffer put(short b); - short get(int index); + public abstract short get(int index); - ShortBuffer put(int index, short b); + public abstract ShortBuffer put(int index, short b); - short getElement(int index); + public abstract short getElement(int index); - void putElement(int index, short value); + public abstract void putElement(int index, short value); - ShortBuffer get(short[] dst, int offset, int length); + public abstract ShortBuffer get(short[] dst, int offset, int length); - ShortBuffer get(short[] dst); + public abstract ShortBuffer get(short[] dst); - ShortBuffer put(ShortBuffer src); + public abstract ShortBuffer put(ShortBuffer src); - ShortBuffer put(short[] src, int offset, int length); + public abstract ShortBuffer put(short[] src, int offset, int length); - ShortBuffer put(short[] src); + public abstract ShortBuffer put(short[] src); - boolean isDirect(); + public abstract boolean isDirect(); - ShortBuffer mark(); + public abstract ShortBuffer mark(); - ShortBuffer reset(); + public abstract ShortBuffer reset(); - ShortBuffer clear(); + public abstract ShortBuffer clear(); - ShortBuffer flip(); + public abstract ShortBuffer flip(); - ShortBuffer rewind(); + public abstract ShortBuffer rewind(); - ShortBuffer limit(int newLimit); + public abstract ShortBuffer limit(int newLimit); - ShortBuffer position(int newPosition); + public abstract int limit(); - short[] array(); + public abstract ShortBuffer position(int newPosition); + + public abstract int position(); + + public abstract int remaining(); + + public abstract boolean hasRemaining(); + + public abstract int capacity(); + + public abstract boolean hasArray(); + + public abstract short[] array(); } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerTextureAtlasSprite.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerTextureAtlasSprite.java index 125c6b6..9fe6a8c 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerTextureAtlasSprite.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerTextureAtlasSprite.java @@ -1,10 +1,10 @@ package net.lax1dude.eaglercraft.v1_8.minecraft; import java.io.IOException; -import java.util.Iterator; import java.util.List; import java.util.concurrent.Callable; +import com.carrotsearch.hppc.cursors.IntCursor; import com.google.common.collect.Lists; import net.lax1dude.eaglercraft.v1_8.HString; @@ -228,10 +228,8 @@ public class EaglerTextureAtlasSprite { int l = i; this.height = this.width; if (meta.getFrameCount() > 0) { - Iterator iterator = meta.getFrameIndexSet().iterator(); - - while (iterator.hasNext()) { - int i1 = iterator.next().intValue(); + for (IntCursor cur : meta.getFrameIndexSet()) { + int i1 = cur.value; if (i1 >= j1) { throw new RuntimeException("invalid frameindex " + i1); } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglerMeshLoader.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglerMeshLoader.java index cb1c7b6..2d253b0 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglerMeshLoader.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglerMeshLoader.java @@ -49,6 +49,7 @@ public class EaglerMeshLoader implements IResourceManagerReloadListener { if(theMesh == null) { theMesh = new HighPolyMesh(); reloadMesh(meshLoc, theMesh, Minecraft.getMinecraft().getResourceManager()); + meshCache.put(meshLoc, theMesh); } meshLoc.cachedPointerType = ResourceLocation.CACHED_POINTER_EAGLER_MESH; meshLoc.cachedPointer = theMesh; diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java index bcd3878..41866da 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java @@ -7,8 +7,8 @@ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.minecraft.util.MathHelper; -import java.util.HashMap; -import java.util.Map; +import com.carrotsearch.hppc.IntObjectHashMap; +import com.carrotsearch.hppc.IntObjectMap; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.internal.GLObjectMap; @@ -213,15 +213,18 @@ public class EaglercraftGPU { ++GlStateManager.stateNormalSerial; } - private static final Map stringCache = new HashMap<>(); + private static final IntObjectMap stringCache = new IntObjectHashMap<>(); public static final String glGetString(int param) { String str = stringCache.get(param); if(str == null) { str = _wglGetString(param); + if(str == null) { + str = ""; + } stringCache.put(param, str); } - return str; + return str.length() == 0 ? null : str; } public static final void glGetInteger(int param, int[] values) { diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/WorldRenderer.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/WorldRenderer.java index 8c732f3..c5397b4 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/WorldRenderer.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/WorldRenderer.java @@ -3,9 +3,10 @@ package net.lax1dude.eaglercraft.v1_8.opengl; import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; -import java.util.Arrays; import java.util.BitSet; -import java.util.Comparator; +import java.util.function.IntBinaryOperator; + +import com.carrotsearch.hppc.sorting.QuickSort; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; @@ -84,12 +85,37 @@ public class WorldRenderer { } } + private float[] sortArrayCacheA = null; + private int[] sortArrayCacheB = null; + private BitSet sortBitSetCache = null; + private final IntBinaryOperator sortArrayCacheLambda = this::sortFunction_func_181674_a; + private final IntBinaryOperator swapArrayCacheLambda = this::swapFunction_func_181674_a; + + protected int sortFunction_func_181674_a(int integer, int integer1) { + return Float.compare(sortArrayCacheA[sortArrayCacheB[integer1]], sortArrayCacheA[sortArrayCacheB[integer]]); + } + + protected int swapFunction_func_181674_a(int i, int j) { + int swap = sortArrayCacheB[i]; + sortArrayCacheB[i] = sortArrayCacheB[j]; + sortArrayCacheB[j] = swap; + return 0; + } + /** * MOST LIKELY USED TO SORT QUADS BACK TO FRONT */ public void func_181674_a(float parFloat1, float parFloat2, float parFloat3) { int i = this.vertexCount / 4; - final float[] afloat = new float[i]; + if(i == 0) { + return; + } + + float[] afloat = sortArrayCacheA; + if(afloat == null || afloat.length < i) { + afloat = new float[i]; + sortArrayCacheA = afloat; + } for (int j = 0; j < i; ++j) { afloat[j] = func_181665_a(this.floatBuffer, (float) ((double) parFloat1 + this.xOffset), @@ -97,30 +123,38 @@ public class WorldRenderer { this.vertexFormat.attribStride >> 2, j * this.vertexFormat.attribStride); } - Integer[] ainteger = new Integer[i]; - - for (int k = 0; k < ainteger.length; ++k) { - ainteger[k] = Integer.valueOf(k); + int[] ainteger = sortArrayCacheB; + if(ainteger == null || ainteger.length < i) { + ainteger = new int[i]; + sortArrayCacheB = ainteger; + } + + for (int k = 0; k < i; ++k) { + ainteger[k] = k; + } + + QuickSort.sort(0, i, sortArrayCacheLambda, swapArrayCacheLambda); + + BitSet bitset = sortBitSetCache; + if(bitset == null) { + bitset = new BitSet(); + sortBitSetCache = bitset; + }else { + bitset.clear(); } - Arrays.sort(ainteger, new Comparator() { - public int compare(Integer integer, Integer integer1) { - return Float.compare(afloat[integer1.intValue()], afloat[integer.intValue()]); - } - }); - BitSet bitset = new BitSet(); int l = this.vertexFormat.attribStride; int[] aint = new int[l]; - for (int l1 = 0; (l1 = bitset.nextClearBit(l1)) < ainteger.length; ++l1) { - int i1 = ainteger[l1].intValue(); + for (int l1 = 0; (l1 = bitset.nextClearBit(l1)) < i; ++l1) { + int i1 = ainteger[l1]; if (i1 != l1) { this.intBuffer.limit(i1 * l + l); this.intBuffer.position(i1 * l); this.intBuffer.get(aint); int j1 = i1; - for (int k1 = ainteger[i1].intValue(); j1 != l1; k1 = ainteger[k1].intValue()) { + for (int k1 = ainteger[i1]; j1 != l1; k1 = ainteger[k1]) { this.intBuffer.limit(k1 * l + l); this.intBuffer.position(k1 * l); IntBuffer intbuffer = this.intBuffer.duplicate(); diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/BlockVertexIDs.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/BlockVertexIDs.java index 2b5a273..a4ba0d7 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/BlockVertexIDs.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/BlockVertexIDs.java @@ -3,8 +3,8 @@ package net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred; import java.io.BufferedReader; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; +import com.carrotsearch.hppc.ObjectIntHashMap; +import com.carrotsearch.hppc.ObjectIntMap; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; @@ -32,7 +32,7 @@ public class BlockVertexIDs implements IResourceManagerReloadListener { private static final Logger logger = LogManager.getLogger("BlockVertexIDsCSV"); - public static final Map modelToID = new HashMap<>(); + public static final ObjectIntMap modelToID = new ObjectIntHashMap<>(); public static int builtin_water_still_vertex_id = 0; public static int builtin_water_flow_vertex_id = 0; diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DebugFramebufferView.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DebugFramebufferView.java index 1a0913b..7592772 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DebugFramebufferView.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/DebugFramebufferView.java @@ -316,6 +316,14 @@ public class DebugFramebufferView { GlStateManager.bindTexture(pipeline.realisticWaterMaskTexture); DrawUtils.drawStandardQuad2D(); })), + (new DebugFramebufferView("Water: Combined Normals", (pipeline) -> { + if(!pipeline.config.is_rendering_realisticWater) throw new NoDataException(); + PipelineShaderGBufferDebugView dbv = pipeline.useDebugViewShader(1); + EaglerDeferredPipeline.uniformMatrixHelper(dbv.uniforms.u_inverseViewMatrix, DeferredStateManager.inverseViewMatrix); + GlStateManager.setActiveTexture(GL_TEXTURE0); + GlStateManager.bindTexture(pipeline.realisticWaterCombinedNormalsTexture); + DrawUtils.drawStandardQuad2D(); + })), (new DebugFramebufferView("Water: Surface Depth", (pipeline) -> { if(!pipeline.config.is_rendering_realisticWater) throw new NoDataException(); float depthStart = 0.001f; diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java index a78c658..d5ebccf 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java @@ -34,6 +34,7 @@ import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.PipelineShaderP import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.PipelineShaderRealisticWaterControl; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.PipelineShaderRealisticWaterNoise; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.PipelineShaderRealisticWaterNormalMap; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.PipelineShaderRealisticWaterNormalsMix; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.PipelineShaderReprojControl; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.PipelineShaderReprojSSR; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.PipelineShaderSSAOGenerate; @@ -43,6 +44,7 @@ import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.PipelineShaderS import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.PipelineShaderSkyboxRender; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.PipelineShaderSkyboxRenderEnd; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.PipelineShaderTonemap; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.ShaderMissingException; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.texture.MetalsLUT; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.texture.TemperaturesLUT; import net.lax1dude.eaglercraft.v1_8.vector.Matrix3f; @@ -291,6 +293,7 @@ public class EaglerDeferredPipeline { public PipelineShaderRealisticWaterControl shader_realistic_water_control = null; public PipelineShaderRealisticWaterNoise shader_realistic_water_noise = null; public PipelineShaderRealisticWaterNormalMap shader_realistic_water_normals = null; + public PipelineShaderRealisticWaterNormalsMix shader_realistic_water_normals_mix = null; public PipelineShaderHandDepthMask shader_hand_depth_mask = null; public PipelineShaderFXAA shader_post_fxaa = null; public SkyboxRenderer skybox = null; @@ -913,6 +916,12 @@ public class EaglerDeferredPipeline { shader_realistic_water_normals = PipelineShaderRealisticWaterNormalMap.compile(); shader_realistic_water_normals.loadUniforms(); _wglUniform2f(shader_realistic_water_normals.uniforms.u_sampleOffset2f, 0.00390625f, 0.00390625f); + try { + shader_realistic_water_normals_mix = PipelineShaderRealisticWaterNormalsMix.compile(); + shader_realistic_water_normals_mix.loadUniforms(); + }catch(ShaderMissingException exx) { + shader_realistic_water_normals_mix = null; + } if(!config.is_rendering_raytracing) { shader_reproject_ssr = PipelineShaderReprojSSR.compile(); shader_reproject_ssr.loadUniforms(); @@ -1083,7 +1092,7 @@ public class EaglerDeferredPipeline { double distX = worldX - reprojectionOriginCoordinateX; double distY = worldY - reprojectionOriginCoordinateY; double distZ = worldZ - reprojectionOriginCoordinateZ; - if(distX * distX + distY * distY + distZ * distZ > 48.0 * 48.0) { + if(distX * distX + distY * distY + distZ * distZ > 72.0 * 72.0) { reprojectionOriginCoordinateX = worldX; reprojectionOriginCoordinateY = worldY; reprojectionOriginCoordinateZ = worldZ; @@ -1098,7 +1107,7 @@ public class EaglerDeferredPipeline { } distX = worldX - cloudRenderOriginCoordinateX; distZ = worldZ - cloudRenderOriginCoordinateZ; - if(distX * distX + distZ * distZ > 256.0 * 256.0) { + if(distX * distX + distZ * distZ > 384.0 * 384.0) { cloudRenderOriginCoordinateX = worldX; cloudRenderOriginCoordinateZ = worldZ; cloudRenderViewerOffsetX = 0.0f; @@ -1844,39 +1853,44 @@ public class EaglerDeferredPipeline { _wglUniformMatrix4x2fv(shader_reproject_ssr.uniforms.u_lastInverseProjMatrix4x2f, false, matrixCopyBuffer); _wglUniform1f(shader_reproject_ssr.uniforms.u_sampleStep1f, 0.125f); - DrawUtils.drawStandardQuad2D(); // sample 1 + if(shader_reproject_ssr.uniforms.u_sampleDelta1i != null) { + _wglUniform1i(shader_reproject_ssr.uniforms.u_sampleDelta1i, 5); + DrawUtils.drawStandardQuad2D(); + }else { + DrawUtils.drawStandardQuad2D(); // sample 1 - _wglBindFramebuffer(_GL_FRAMEBUFFER, reprojectionSSRFramebuffer[0]); - GlStateManager.setActiveTexture(GL_TEXTURE3); - GlStateManager.bindTexture(reprojectionSSRHitVector[1]); - GlStateManager.setActiveTexture(GL_TEXTURE2); - GlStateManager.bindTexture(reprojectionSSRTexture[1]); + _wglBindFramebuffer(_GL_FRAMEBUFFER, reprojectionSSRFramebuffer[0]); + GlStateManager.setActiveTexture(GL_TEXTURE3); + GlStateManager.bindTexture(reprojectionSSRHitVector[1]); + GlStateManager.setActiveTexture(GL_TEXTURE2); + GlStateManager.bindTexture(reprojectionSSRTexture[1]); - DrawUtils.drawStandardQuad2D(); // sample 2 + DrawUtils.drawStandardQuad2D(); // sample 2 - _wglBindFramebuffer(_GL_FRAMEBUFFER, reprojectionSSRFramebuffer[1]); - GlStateManager.setActiveTexture(GL_TEXTURE3); - GlStateManager.bindTexture(reprojectionSSRHitVector[0]); - GlStateManager.setActiveTexture(GL_TEXTURE2); - GlStateManager.bindTexture(reprojectionSSRTexture[0]); + _wglBindFramebuffer(_GL_FRAMEBUFFER, reprojectionSSRFramebuffer[1]); + GlStateManager.setActiveTexture(GL_TEXTURE3); + GlStateManager.bindTexture(reprojectionSSRHitVector[0]); + GlStateManager.setActiveTexture(GL_TEXTURE2); + GlStateManager.bindTexture(reprojectionSSRTexture[0]); - DrawUtils.drawStandardQuad2D(); // sample 3 + DrawUtils.drawStandardQuad2D(); // sample 3 - _wglBindFramebuffer(_GL_FRAMEBUFFER, reprojectionSSRFramebuffer[0]); - GlStateManager.setActiveTexture(GL_TEXTURE3); - GlStateManager.bindTexture(reprojectionSSRHitVector[1]); - GlStateManager.setActiveTexture(GL_TEXTURE2); - GlStateManager.bindTexture(reprojectionSSRTexture[1]); + _wglBindFramebuffer(_GL_FRAMEBUFFER, reprojectionSSRFramebuffer[0]); + GlStateManager.setActiveTexture(GL_TEXTURE3); + GlStateManager.bindTexture(reprojectionSSRHitVector[1]); + GlStateManager.setActiveTexture(GL_TEXTURE2); + GlStateManager.bindTexture(reprojectionSSRTexture[1]); - DrawUtils.drawStandardQuad2D(); // sample 4 + DrawUtils.drawStandardQuad2D(); // sample 4 - _wglBindFramebuffer(_GL_FRAMEBUFFER, reprojectionSSRFramebuffer[1]); - GlStateManager.setActiveTexture(GL_TEXTURE3); - GlStateManager.bindTexture(reprojectionSSRHitVector[0]); - GlStateManager.setActiveTexture(GL_TEXTURE2); - GlStateManager.bindTexture(reprojectionSSRTexture[0]); + _wglBindFramebuffer(_GL_FRAMEBUFFER, reprojectionSSRFramebuffer[1]); + GlStateManager.setActiveTexture(GL_TEXTURE3); + GlStateManager.bindTexture(reprojectionSSRHitVector[0]); + GlStateManager.setActiveTexture(GL_TEXTURE2); + GlStateManager.bindTexture(reprojectionSSRTexture[0]); - DrawUtils.drawStandardQuad2D(); // sample 5 + DrawUtils.drawStandardQuad2D(); // sample 5 + } DeferredStateManager.checkGLError("combineGBuffersAndIlluminate(): RUN SCREENSPACE REFLECTIONS ALGORITHM"); } @@ -2663,14 +2677,24 @@ public class EaglerDeferredPipeline { _wglBindFramebuffer(_GL_FRAMEBUFFER, realisticWaterCombinedNormalsFramebuffer); GlStateManager.viewport(0, 0, currentWidth, currentHeight); - GlStateManager.bindTexture(gBufferNormalsTexture); - TextureCopyUtil.blitTexture(); - - GlStateManager.bindTexture(realisticWaterMaskTexture); - GlStateManager.enableBlend(); - GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); - TextureCopyUtil.blitTexture(); - GlStateManager.disableBlend(); + if(shader_realistic_water_normals_mix != null) { + GlStateManager.disableBlend(); + GlStateManager.setActiveTexture(GL_TEXTURE1); + GlStateManager.bindTexture(realisticWaterMaskTexture); + GlStateManager.setActiveTexture(GL_TEXTURE0); + GlStateManager.bindTexture(gBufferNormalsTexture); + shader_realistic_water_normals_mix.useProgram(); + DrawUtils.drawStandardQuad2D(); + }else { + GlStateManager.bindTexture(gBufferNormalsTexture); + TextureCopyUtil.blitTexture(); + + GlStateManager.bindTexture(realisticWaterMaskTexture); + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); + TextureCopyUtil.blitTexture(); + GlStateManager.disableBlend(); + } DeferredStateManager.checkGLError("endDrawRealisticWaterMask(): COMBINE NORMALS"); @@ -2773,39 +2797,44 @@ public class EaglerDeferredPipeline { _wglUniformMatrix4x2fv(shader_reproject_ssr.uniforms.u_lastInverseProjMatrix4x2f, false, matrixCopyBuffer); _wglUniform1f(shader_reproject_ssr.uniforms.u_sampleStep1f, 0.5f); - DrawUtils.drawStandardQuad2D(); // sample 1 + if(shader_reproject_ssr.uniforms.u_sampleDelta1i != null) { + _wglUniform1i(shader_reproject_ssr.uniforms.u_sampleDelta1i, 5); + DrawUtils.drawStandardQuad2D(); + }else { + DrawUtils.drawStandardQuad2D(); // sample 1 - _wglBindFramebuffer(_GL_FRAMEBUFFER, realisticWaterSSRFramebuffer[0]); - GlStateManager.setActiveTexture(GL_TEXTURE3); - GlStateManager.bindTexture(realisticWaterControlHitVectorTexture[1]); - GlStateManager.setActiveTexture(GL_TEXTURE2); - GlStateManager.bindTexture(realisticWaterControlReflectionTexture[1]); + _wglBindFramebuffer(_GL_FRAMEBUFFER, realisticWaterSSRFramebuffer[0]); + GlStateManager.setActiveTexture(GL_TEXTURE3); + GlStateManager.bindTexture(realisticWaterControlHitVectorTexture[1]); + GlStateManager.setActiveTexture(GL_TEXTURE2); + GlStateManager.bindTexture(realisticWaterControlReflectionTexture[1]); - DrawUtils.drawStandardQuad2D(); // sample 2 + DrawUtils.drawStandardQuad2D(); // sample 2 - _wglBindFramebuffer(_GL_FRAMEBUFFER, realisticWaterSSRFramebuffer[1]); - GlStateManager.setActiveTexture(GL_TEXTURE3); - GlStateManager.bindTexture(realisticWaterControlHitVectorTexture[0]); - GlStateManager.setActiveTexture(GL_TEXTURE2); - GlStateManager.bindTexture(realisticWaterControlReflectionTexture[0]); + _wglBindFramebuffer(_GL_FRAMEBUFFER, realisticWaterSSRFramebuffer[1]); + GlStateManager.setActiveTexture(GL_TEXTURE3); + GlStateManager.bindTexture(realisticWaterControlHitVectorTexture[0]); + GlStateManager.setActiveTexture(GL_TEXTURE2); + GlStateManager.bindTexture(realisticWaterControlReflectionTexture[0]); - DrawUtils.drawStandardQuad2D(); // sample 3 + DrawUtils.drawStandardQuad2D(); // sample 3 - _wglBindFramebuffer(_GL_FRAMEBUFFER, realisticWaterSSRFramebuffer[0]); - GlStateManager.setActiveTexture(GL_TEXTURE3); - GlStateManager.bindTexture(realisticWaterControlHitVectorTexture[1]); - GlStateManager.setActiveTexture(GL_TEXTURE2); - GlStateManager.bindTexture(realisticWaterControlReflectionTexture[1]); + _wglBindFramebuffer(_GL_FRAMEBUFFER, realisticWaterSSRFramebuffer[0]); + GlStateManager.setActiveTexture(GL_TEXTURE3); + GlStateManager.bindTexture(realisticWaterControlHitVectorTexture[1]); + GlStateManager.setActiveTexture(GL_TEXTURE2); + GlStateManager.bindTexture(realisticWaterControlReflectionTexture[1]); - DrawUtils.drawStandardQuad2D(); // sample 4 + DrawUtils.drawStandardQuad2D(); // sample 4 - _wglBindFramebuffer(_GL_FRAMEBUFFER, realisticWaterSSRFramebuffer[1]); - GlStateManager.setActiveTexture(GL_TEXTURE3); - GlStateManager.bindTexture(realisticWaterControlHitVectorTexture[0]); - GlStateManager.setActiveTexture(GL_TEXTURE2); - GlStateManager.bindTexture(realisticWaterControlReflectionTexture[0]); + _wglBindFramebuffer(_GL_FRAMEBUFFER, realisticWaterSSRFramebuffer[1]); + GlStateManager.setActiveTexture(GL_TEXTURE3); + GlStateManager.bindTexture(realisticWaterControlHitVectorTexture[0]); + GlStateManager.setActiveTexture(GL_TEXTURE2); + GlStateManager.bindTexture(realisticWaterControlReflectionTexture[0]); - DrawUtils.drawStandardQuad2D(); // sample 5 + DrawUtils.drawStandardQuad2D(); // sample 5 + } DeferredStateManager.checkGLError("endDrawRealisticWaterMask(): RUN SCREENSPACE REFLECTIONS ALGORITHM"); @@ -3066,6 +3095,8 @@ public class EaglerDeferredPipeline { GlStateManager.clear(GL_DEPTH_BUFFER_BIT); GlStateManager.enableDepth(); DeferredStateManager.setDefaultMaterialConstants(); + DeferredStateManager.disableFog(); + updateForwardRenderWorldLightingData(); DeferredStateManager.checkGLError("Post: beginDrawHandOverlay()"); } @@ -3838,6 +3869,10 @@ public class EaglerDeferredPipeline { shader_realistic_water_normals.destroy(); shader_realistic_water_normals = null; } + if(shader_realistic_water_normals_mix != null) { + shader_realistic_water_normals_mix.destroy(); + shader_realistic_water_normals_mix = null; + } if(shader_post_fxaa != null) { shader_post_fxaa.destroy(); shader_post_fxaa = null; diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderRealisticWaterNormalsMix.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderRealisticWaterNormalsMix.java new file mode 100644 index 0000000..bb28ee0 --- /dev/null +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderRealisticWaterNormalsMix.java @@ -0,0 +1,57 @@ +package net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program; + +import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL; +import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL; + +/** + * 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. + * + */ +public class PipelineShaderRealisticWaterNormalsMix extends ShaderProgram { + + public static PipelineShaderRealisticWaterNormalsMix compile() throws ShaderException { + IShaderGL normalsMix = ShaderCompiler.compileShader("realistic_water_normals_mix", GL_FRAGMENT_SHADER, + ShaderSource.realistic_water_normals_mix_fsh); + try { + IProgramGL prog = ShaderCompiler.linkProgram("realistic_water_normals_mix", + SharedPipelineShaders.deferred_local, normalsMix); + return new PipelineShaderRealisticWaterNormalsMix(prog); + }finally { + if(normalsMix != null) { + normalsMix.free(); + } + } + } + + private PipelineShaderRealisticWaterNormalsMix(IProgramGL program) { + super(program, new Uniforms()); + } + + public static class Uniforms implements IProgramUniforms { + + private Uniforms() { + } + + @Override + public void loadUniforms(IProgramGL prog) { + _wglUniform1i(_wglGetUniformLocation(prog, "u_gbufferNormalsTexture"), 0); + _wglUniform1i(_wglGetUniformLocation(prog, "u_surfaceNormalsTexture"), 1); + } + + } + +} diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderReprojSSR.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderReprojSSR.java index 69fea20..8b98ffe 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderReprojSSR.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/PipelineShaderReprojSSR.java @@ -48,6 +48,7 @@ public class PipelineShaderReprojSSR extends ShaderProgram compileFlags) throws ShaderCompileException { - return compileShader(name, stage, filename.toString(), ShaderSource.getSourceFor(filename), compileFlags); + String src = ShaderSource.getSourceFor(filename); + if(src == null) { + throw new ShaderMissingException(name, "File not found: " + filename); + } + return compileShader(name, stage, filename.toString(), src, compileFlags); } public static IShaderGL compileShader(String name, int stage, String filename, String source, List compileFlags) throws ShaderCompileException { diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderMissingException.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderMissingException.java new file mode 100644 index 0000000..867acf6 --- /dev/null +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderMissingException.java @@ -0,0 +1,24 @@ +package net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program; + +/** + * 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. + * + */ +public class ShaderMissingException extends ShaderException { + + public ShaderMissingException(String shaderName, String msg) { + super(shaderName, msg); + } + +} diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderSource.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderSource.java index 17b2129..63dd3fb 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderSource.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/program/ShaderSource.java @@ -54,6 +54,7 @@ public class ShaderSource { public static final ResourceLocation realistic_water_render_fsh = new ResourceLocation("eagler:glsl/deferred/realistic_water_render.fsh"); public static final ResourceLocation realistic_water_control_fsh = new ResourceLocation("eagler:glsl/deferred/realistic_water_control.fsh"); public static final ResourceLocation realistic_water_normals_fsh = new ResourceLocation("eagler:glsl/deferred/realistic_water_normals.fsh"); + public static final ResourceLocation realistic_water_normals_mix_fsh = new ResourceLocation("eagler:glsl/deferred/realistic_water_normals_mix.fsh"); public static final ResourceLocation realistic_water_noise_fsh = new ResourceLocation("eagler:glsl/deferred/realistic_water_noise.fsh"); public static final ResourceLocation gbuffer_debug_view_fsh = new ResourceLocation("eagler:glsl/deferred/gbuffer_debug_view.fsh"); public static final ResourceLocation ssao_generate_fsh = new ResourceLocation("eagler:glsl/deferred/ssao_generate.fsh"); diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/EaglerTextureAtlasSpritePBR.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/EaglerTextureAtlasSpritePBR.java index a2074e2..05d31bd 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/EaglerTextureAtlasSpritePBR.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/texture/EaglerTextureAtlasSpritePBR.java @@ -1,10 +1,10 @@ package net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.texture; import java.io.IOException; -import java.util.Iterator; import java.util.List; import java.util.concurrent.Callable; +import com.carrotsearch.hppc.cursors.IntCursor; import com.google.common.collect.Lists; import net.lax1dude.eaglercraft.v1_8.HString; @@ -104,10 +104,8 @@ public class EaglerTextureAtlasSpritePBR extends EaglerTextureAtlasSprite { int l = i; this.height = this.width; if (meta.getFrameCount() > 0) { - Iterator iterator = meta.getFrameIndexSet().iterator(); - - while (iterator.hasNext()) { - int i1 = ((Integer) iterator.next()).intValue(); + for (IntCursor cur : meta.getFrameIndexSet()) { + int i1 = cur.value; if (i1 >= j1) { throw new RuntimeException("invalid frameindex " + i1); } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerSkinCache.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerSkinCache.java index f11cf4b..f98b581 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerSkinCache.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerSkinCache.java @@ -201,7 +201,7 @@ public class ServerSkinCache { } public SkinCacheEntry getSkin(String url, SkinModel skinModelResponse) { - if(url.length() > 0xFFFF) { + if(url.length() > 0x7F00) { return skinModelResponse == SkinModel.ALEX ? defaultSlimCacheEntry : defaultCacheEntry; } EaglercraftUUID generatedUUID = SkinPackets.createEaglerURLSkinUUID(url); diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/socket/ServerQueryImpl.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/socket/ServerQueryImpl.java index 21bc973..773530e 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/socket/ServerQueryImpl.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/socket/ServerQueryImpl.java @@ -10,7 +10,6 @@ import net.lax1dude.eaglercraft.v1_8.internal.EnumServerRateLimit; import net.lax1dude.eaglercraft.v1_8.internal.IServerQuery; import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient; import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame; -import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; import net.lax1dude.eaglercraft.v1_8.internal.QueryResponse; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/GameProtocolMessageController.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/GameProtocolMessageController.java index dd02590..c4cc930 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/GameProtocolMessageController.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/socket/protocol/client/GameProtocolMessageController.java @@ -1,6 +1,7 @@ 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; @@ -149,10 +150,12 @@ public class GameProtocolMessageController { while(sendQueueV4.size() > 0) { sendCount = 0; totalLen = 0; + Iterator itr = sendQueueV4.iterator(); do { - i = sendQueueV4.get(sendCount++).readableBytes(); + i = itr.next().readableBytes(); totalLen += GamePacketOutputBuffer.getVarIntSize(i) + i; - }while(totalLen < 32760 && sendCount < sendQueueV4.size()); + ++sendCount; + }while(totalLen < 32760 && itr.hasNext()); if(totalLen >= 32760) { --sendCount; } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacketManager.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacketManager.java index 6363efa..10b6cdd 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacketManager.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/ipc/IPCPacketManager.java @@ -3,9 +3,11 @@ package net.lax1dude.eaglercraft.v1_8.sp.ipc; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; -import java.util.HashMap; import java.util.function.Supplier; +import com.carrotsearch.hppc.IntObjectHashMap; +import com.carrotsearch.hppc.IntObjectMap; + /** * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. * @@ -23,7 +25,7 @@ import java.util.function.Supplier; */ public class IPCPacketManager { - public static final HashMap> mappings = new HashMap<>(); + public static final IntObjectMap> mappings = new IntObjectHashMap<>(); public final IPCInputStream IPC_INPUT_STREAM = new IPCInputStream(); public final IPCOutputStream IPC_OUTPUT_STREAM = new IPCOutputStream(); diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientNetworkManager.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientNetworkManager.java index bfb742b..2bf828f 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientNetworkManager.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientNetworkManager.java @@ -2,9 +2,7 @@ package net.lax1dude.eaglercraft.v1_8.sp.lan; 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.EaglerZLIB; -import net.lax1dude.eaglercraft.v1_8.IOUtils; import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState; import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; @@ -21,7 +19,6 @@ import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.IChatComponent; import java.io.IOException; -import java.io.InputStream; import java.util.ArrayList; import java.util.List; @@ -308,13 +305,26 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager { } for(int k = 0, l = packets.size(); k < l; ++k) { byte[] data = packets.get(k); + + if(firstPacket) { + // 1.5 kick packet + if(data.length == 31 && data[0] == (byte)0xFF && data[1] == (byte)0x00 && data[2] == (byte)0x0E) { + logger.error("Detected a 1.5 LAN server!"); + this.closeChannel(new ChatComponentTranslation("singleplayer.outdatedLANServerKick")); + firstPacket = false; + return; + } + firstPacket = false; + } + byte[] fullData; boolean compressed = false; + int off = 0; if (data[0] == 0 || data[0] == 2) { if(fragmentedPacket.isEmpty()) { - fullData = new byte[data.length - 1]; - System.arraycopy(data, 1, fullData, 0, fullData.length); + fullData = data; + off = 1; }else { fragmentedPacket.add(data); int len = 0; @@ -341,34 +351,23 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager { } if(compressed) { - if(fullData.length < 4) { + if(fullData.length < 4 + off) { throw new IOException("Recieved invalid " + fullData.length + " byte compressed packet"); } - EaglerInputStream bi = new EaglerInputStream(fullData); - int i = (bi.read() << 24) | (bi.read() << 16) | (bi.read() << 8) | bi.read(); - fullData = new byte[i]; - int r; - try(InputStream inflaterInputStream = EaglerZLIB.newInflaterInputStream(bi)) { - r = IOUtils.readFully(inflaterInputStream, fullData); - } + int i = (((int) fullData[off] & 0xFF) << 24) | (((int) fullData[off + 1] & 0xFF) << 16) + | (((int) fullData[off + 2] & 0xFF) << 8) | ((int) fullData[off + 3] & 0xFF); + byte[] fullData2 = new byte[i]; + int r = EaglerZLIB.inflateFull(fullData, off + 4, fullData.length - off - 4, fullData2, 0, i); + fullData = fullData2; + off = 0; if (i != r) { logger.warn("Decompressed packet expected size {} differs from actual size {}!", i, r); } } - if(firstPacket) { - // 1.5 kick packet - if(fullData.length == 31 && fullData[0] == (byte)0xFF && fullData[1] == (byte)0x00 && fullData[2] == (byte)0x0E) { - logger.error("Detected a 1.5 LAN server!"); - this.closeChannel(new ChatComponentTranslation("singleplayer.outdatedLANServerKick")); - firstPacket = false; - return; - } - firstPacket = false; - } - ByteBuf nettyBuffer = Unpooled.buffer(fullData, fullData.length); nettyBuffer.writerIndex(fullData.length); + nettyBuffer.readerIndex(off); PacketBuffer input = new PacketBuffer(nettyBuffer); int pktId = input.readVarIntFromBuffer(); diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/GenLayerEaglerRivers.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/GenLayerEaglerRivers.java new file mode 100644 index 0000000..097b8bb --- /dev/null +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/GenLayerEaglerRivers.java @@ -0,0 +1,107 @@ +package net.lax1dude.eaglercraft.v1_8.sp.server; + +import net.minecraft.world.biome.BiomeGenBase; +import net.minecraft.world.gen.layer.GenLayer; +import net.minecraft.world.gen.layer.IntCache; + +/** + * 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. + * + */ +public class GenLayerEaglerRivers extends GenLayer { + + private static final int[] pattern = new int[] { + 0b111000011100001110000111, + 0b111000111110011111000111, + 0b011100011100001110001110, + 0b011100000000000000001110, + 0b001110000000000000011100, + 0b001110000000000000011100, + 0b000111000000000000111000, + 0b000111000000000000111000, + 0b000011100000000001110000, + 0b000011100000000001110000, + 0b000001110000000011100000, + 0b000001110000000011100000, + 0b000000111000000111000000, + 0b000000111000000111000000, + 0b000000011100001110000000, + 0b000000011100001110000000, + 0b000000001110011100000000, + 0b000000001110011100000000, + 0b000000000111111000000000, + 0b000000000111111000000000, + 0b000000000011110000000000, + 0b000000000011110000000000, + 0b000000000001100000000000, + 0b000000000001100000000000, + }; + + private static final int patternSize = 24; + + public GenLayerEaglerRivers(long parLong1, GenLayer p) { + super(parLong1); + this.parent = p; + } + + @Override + public int[] getInts(int x, int y, int w, int h) { + int[] aint = this.parent.getInts(x, y, w, h); + int[] aint1 = IntCache.getIntCache(w * h); + + long a = worldGenSeed * 6364136223846793005L + 1442695040888963407L; + long b = ((a & 112104l) == 0) ? (((a & 534l) == 0) ? 1l : 15l) : 746l; + for (int yy = 0; yy < h; ++yy) { + for (int xx = 0; xx < w; ++xx) { + int i = xx + yy * w; + aint1[i] = aint[i]; + long xxx = (long)(x + xx) & 0xFFFFFFFFl; + long yyy = (long)(y + yy) & 0xFFFFFFFFl; + long hash = a + (xxx / patternSize); + hash *= hash * 6364136223846793005L + 1442695040888963407L; + hash += (yyy / patternSize); + hash *= hash * 6364136223846793005L + 1442695040888963407L; + hash += a; + if ((hash & b) == 0l) { + xxx %= (long)patternSize; + yyy %= (long)patternSize; + long tmp; + switch((int)((hash >>> 16l) & 3l)) { + case 1: + tmp = xxx; + xxx = yyy; + yyy = (long)patternSize - tmp - 1l; + break; + case 2: + tmp = xxx; + xxx = (long)patternSize - yyy - 1l; + yyy = tmp; + break; + case 3: + tmp = xxx; + xxx = (long)patternSize - yyy - 1l; + yyy = (long)patternSize - tmp - 1l; + break; + } + if((pattern[(int)yyy] & (1 << (int)xxx)) != 0) { + aint1[i] = BiomeGenBase.river.biomeID; + } + } + } + } + + return aint1; + } + +} diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/IntegratedServerPlayerNetworkManager.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/IntegratedServerPlayerNetworkManager.java index 419c69b..6bc99c7 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/IntegratedServerPlayerNetworkManager.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/IntegratedServerPlayerNetworkManager.java @@ -2,8 +2,8 @@ package net.lax1dude.eaglercraft.v1_8.sp.server.socket; import java.io.DataOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream; @@ -48,10 +48,9 @@ public class IntegratedServerPlayerNetworkManager { public final String playerChannel; private EnumConnectionState packetState = EnumConnectionState.HANDSHAKING; private static PacketBuffer temporaryBuffer; - private static EaglerOutputStream temporaryOutputStream; + private static byte[] compressedPacketTmp; private int debugPacketCounter = 0; - private byte[][] recievedPacketBuffer = new byte[16384][]; - private int recievedPacketBufferCounter = 0; + private final List recievedPacketBuffer = new LinkedList<>(); private final boolean enableSendCompression; private boolean firstPacket = true; @@ -69,11 +68,6 @@ public class IntegratedServerPlayerNetworkManager { } this.playerChannel = playerChannel; this.enableSendCompression = !SingleplayerServerController.PLAYER_CHANNEL.equals(playerChannel); - if(this.enableSendCompression) { - if(temporaryOutputStream == null) { - temporaryOutputStream = new EaglerOutputStream(16386); - } - } } public void connect() { @@ -97,19 +91,14 @@ public class IntegratedServerPlayerNetworkManager { } public void addRecievedPacket(byte[] next) { - if(recievedPacketBufferCounter < recievedPacketBuffer.length - 1) { - recievedPacketBuffer[recievedPacketBufferCounter++] = next; - }else { - logger.error("Dropping packets on recievedPacketBuffer for channel \"{}\"! (overflow)", playerChannel); - } + recievedPacketBuffer.add(next); } public void processReceivedPackets() { if(nethandler == null) return; - - for(int i = 0; i < recievedPacketBufferCounter; ++i) { - byte[] data = recievedPacketBuffer[i]; + while(!recievedPacketBuffer.isEmpty()) { + byte[] data = recievedPacketBuffer.remove(0); byte[] fullData; if(enableSendCompression) { @@ -132,7 +121,6 @@ public class IntegratedServerPlayerNetworkManager { ServerPlatformSingleplayer.sendPacket(new IPCPacketData(playerChannel, kickPacketBAO.toByteArray())); closeChannel(new ChatComponentText("Recieved unsuppoorted connection from an Eaglercraft 1.5.2 client!")); firstPacket = false; - recievedPacketBufferCounter = 0; return; } firstPacket = false; @@ -169,7 +157,6 @@ public class IntegratedServerPlayerNetworkManager { fullData = data; } - recievedPacketBuffer[i] = null; ++debugPacketCounter; try { ByteBuf nettyBuffer = Unpooled.buffer(fullData, fullData.length); @@ -206,7 +193,6 @@ public class IntegratedServerPlayerNetworkManager { logger.error(t); } } - recievedPacketBufferCounter = 0; } public void sendPacket(Packet pkt) { @@ -234,22 +220,24 @@ public class IntegratedServerPlayerNetworkManager { int len = temporaryBuffer.readableBytes(); if(enableSendCompression) { if(len > compressionThreshold) { - temporaryOutputStream.reset(); - byte[] compressedData; + if(compressedPacketTmp == null || compressedPacketTmp.length < len) { + compressedPacketTmp = new byte[len]; + } + int cmpLen; try { - temporaryOutputStream.write(2); - temporaryOutputStream.write((len >>> 24) & 0xFF); - temporaryOutputStream.write((len >>> 16) & 0xFF); - temporaryOutputStream.write((len >>> 8) & 0xFF); - temporaryOutputStream.write(len & 0xFF); - try(OutputStream os = EaglerZLIB.newDeflaterOutputStream(temporaryOutputStream)) { - temporaryBuffer.readBytes(os, len); - } - compressedData = temporaryOutputStream.toByteArray(); + cmpLen = EaglerZLIB.deflateFull(temporaryBuffer.array(), 0, len, compressedPacketTmp, 0, compressedPacketTmp.length); }catch(IOException ex) { logger.error("Failed to compress packet {}!", pkt.getClass().getSimpleName()); + 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) { diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/socket/ClientIntegratedServerNetworkManager.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/socket/ClientIntegratedServerNetworkManager.java index f6ac239..dd8def8 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/socket/ClientIntegratedServerNetworkManager.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/socket/ClientIntegratedServerNetworkManager.java @@ -1,6 +1,8 @@ package net.lax1dude.eaglercraft.v1_8.sp.socket; import java.io.IOException; +import java.util.LinkedList; +import java.util.List; import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState; import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData; @@ -34,8 +36,7 @@ import net.minecraft.util.IChatComponent; public class ClientIntegratedServerNetworkManager extends EaglercraftNetworkManager { private int debugPacketCounter = 0; - private byte[][] recievedPacketBuffer = new byte[16384][]; - private int recievedPacketBufferCounter = 0; + private final List recievedPacketBuffer = new LinkedList<>(); public boolean isPlayerChannelOpen = false; public ClientIntegratedServerNetworkManager(String channel) { @@ -65,20 +66,15 @@ public class ClientIntegratedServerNetworkManager extends EaglercraftNetworkMana } public void addRecievedPacket(byte[] next) { - if(recievedPacketBufferCounter < recievedPacketBuffer.length - 1) { - recievedPacketBuffer[recievedPacketBufferCounter++] = next; - }else { - logger.error("Dropping packets on recievedPacketBuffer for channel \"{}\"! (overflow)", address); - } + recievedPacketBuffer.add(next); } @Override public void processReceivedPackets() throws IOException { if(nethandler == null) return; - for(int i = 0; i < recievedPacketBufferCounter; ++i) { - byte[] next = recievedPacketBuffer[i]; - recievedPacketBuffer[i] = null; + while(!recievedPacketBuffer.isEmpty()) { + byte[] next = recievedPacketBuffer.remove(0); ++debugPacketCounter; try { ByteBuf nettyBuffer = Unpooled.buffer(next, next.length); @@ -115,7 +111,6 @@ public class ClientIntegratedServerNetworkManager extends EaglercraftNetworkMana logger.error(t); } } - recievedPacketBufferCounter = 0; } @Override @@ -170,9 +165,6 @@ public class ClientIntegratedServerNetworkManager extends EaglercraftNetworkMana } public void clearRecieveQueue() { - for(int i = 0; i < recievedPacketBufferCounter; ++i) { - recievedPacketBuffer[i] = null; - } - recievedPacketBufferCounter = 0; + recievedPacketBuffer.clear(); } } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/TouchControls.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/TouchControls.java index aa1b59c..1a6d573 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/TouchControls.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/TouchControls.java @@ -7,6 +7,10 @@ import net.minecraft.client.gui.ScaledResolution; import java.util.*; +import com.carrotsearch.hppc.IntObjectHashMap; +import com.carrotsearch.hppc.IntObjectMap; +import com.carrotsearch.hppc.cursors.ObjectCursor; + /** * Copyright (c) 2024 lax1dude, ayunami2000. All Rights Reserved. * @@ -24,7 +28,7 @@ import java.util.*; */ public class TouchControls { - public static final Map touchControls = new HashMap<>(); + public static final IntObjectMap touchControls = new IntObjectHashMap<>(); protected static Set touchControlPressed = EnumSet.noneOf(EnumTouchControl.class); protected static boolean isSneakToggled = false; @@ -112,7 +116,8 @@ public class TouchControls { if(!touchControls.isEmpty()) { Set newPressed = EnumSet.noneOf(EnumTouchControl.class); TouchOverlayRenderer renderer = Minecraft.getMinecraft().touchOverlayRenderer; - for (TouchControlInput input : touchControls.values()) { + for (ObjectCursor input_ : touchControls.values()) { + TouchControlInput input = input_.value; TouchAction action = input.control.getAction(); if(action != null) { action.call(input.control, input.x, input.y); diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/TouchOverlayRenderer.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/TouchOverlayRenderer.java index 040f28a..5cad334 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/TouchOverlayRenderer.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/touch_gui/TouchOverlayRenderer.java @@ -17,6 +17,7 @@ import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; import java.util.Set; +import com.carrotsearch.hppc.cursors.ObjectCursor; import com.google.common.collect.Sets; /** @@ -103,8 +104,8 @@ public class TouchOverlayRenderer { GlStateManager.clear(GL_COLOR_BUFFER_BIT); } Set controls = Sets.newHashSet(EnumTouchControl._VALUES); - for (TouchControlInput input : TouchControls.touchControls.values()) { - controls.remove(input.control); + for (ObjectCursor input : TouchControls.touchControls.values()) { + controls.remove(input.value.control); } for (EnumTouchControl control : controls) { if(invalidDeep || control.invalid) { @@ -115,7 +116,8 @@ public class TouchOverlayRenderer { control.invalid = false; } } - for (TouchControlInput input : TouchControls.touchControls.values()) { + for (ObjectCursor input_ : TouchControls.touchControls.values()) { + TouchControlInput input = input_.value; EnumTouchControl control = input.control; if(invalidDeep || control.invalid) { if(control.visible) { diff --git a/sources/resources/EPKVersionIdentifier.txt b/sources/resources/EPKVersionIdentifier.txt index 50356da..503c4b5 100644 --- a/sources/resources/EPKVersionIdentifier.txt +++ b/sources/resources/EPKVersionIdentifier.txt @@ -1 +1 @@ -u46 \ No newline at end of file +u47 \ No newline at end of file diff --git a/sources/resources/assets/eagler/CREDITS.txt b/sources/resources/assets/eagler/CREDITS.txt index 7460d54..6e70923 100644 --- a/sources/resources/assets/eagler/CREDITS.txt +++ b/sources/resources/assets/eagler/CREDITS.txt @@ -217,6 +217,28 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + Project Name: High Performance Primitive Collections + Project Author: Carrot Search + Project URL: http://labs.carrotsearch.com/hppc.html + + Used For: Primitive collections library for the client + + * Copyright 2010-2013, Carrot Search s.c., Boznicza 11/56, Poznan, Poland + * + * Licensed 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 + * + * https://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. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + Project Name: Google Guava Project Author: Google Project URL: https://github.com/google/guava @@ -514,6 +536,45 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + Project Name: OpenJDK + Project Author: Oracle, IBM + Project URL: https://openjdk.org/projects/jdk/17/ + + Used For: Debloated version of IBM's ICU4J for reordering Arabic and Hebrew text + + * 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. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + Project Name: Java-WebSocket Project Author: Nathan Rajlich (TooTallNate) Project URL: http://tootallnate.github.io/Java-WebSocket diff --git a/sources/resources/assets/eagler/glsl/deferred/accel_particle_forward.fsh b/sources/resources/assets/eagler/glsl/deferred/accel_particle_forward.fsh index 6af2686..8066ab3 100644 --- a/sources/resources/assets/eagler/glsl/deferred/accel_particle_forward.fsh +++ b/sources/resources/assets/eagler/glsl/deferred/accel_particle_forward.fsh @@ -206,6 +206,7 @@ void main() { vec3 dlightDist3f, dlightDir3f, dlightColor3f; int safeLightCount = u_dynamicLightCount1i > 12 ? 0 : u_dynamicLightCount1i; // hate this + float cm; for(int i = 0; i < safeLightCount; ++i) { dlightDist3f = worldPosition4f.xyz - u_dynamicLightArray[i].u_lightPosition4f.xyz; dlightDir3f = normalize(dlightDist3f); @@ -215,9 +216,11 @@ void main() { continue; } dlightColor3f = u_dynamicLightArray[i].u_lightColor4f.rgb / dot(dlightDist3f, dlightDist3f); - if(dlightColor3f.r + dlightColor3f.g + dlightColor3f.b < 0.025) { + cm = dlightColor3f.r + dlightColor3f.g + dlightColor3f.b; + if(cm < 0.025) { continue; } + dlightColor3f *= ((cm - 0.025) / cm); lightColor3f += eaglercraftLighting(diffuseColor4f.rgb, dlightColor3f, -worldDirection4f.xyz, dlightDir3f, normalVector3f, materialData3f, metalN, metalK) * u_blockSkySunDynamicLightFac4f.w; } diff --git a/sources/resources/assets/eagler/glsl/deferred/deferred_combine.fsh b/sources/resources/assets/eagler/glsl/deferred/deferred_combine.fsh index 3de01b4..a55bea5 100644 --- a/sources/resources/assets/eagler/glsl/deferred/deferred_combine.fsh +++ b/sources/resources/assets/eagler/glsl/deferred/deferred_combine.fsh @@ -68,7 +68,7 @@ void main() { vec4 materialData4f; float depth = textureLod(u_gbufferDepthTexture, v_position2f, 0.0).r; - if(depth < 0.00001) { + if(depth == 0.0) { discard; } diff --git a/sources/resources/assets/eagler/glsl/deferred/deferred_fog.fsh b/sources/resources/assets/eagler/glsl/deferred/deferred_fog.fsh index 8130c07..4c0d4f9 100644 --- a/sources/resources/assets/eagler/glsl/deferred/deferred_fog.fsh +++ b/sources/resources/assets/eagler/glsl/deferred/deferred_fog.fsh @@ -52,7 +52,7 @@ void main() { vec4 fragPos4f = vec4(v_position2f, textureLod(u_fogDepthTexture, v_position2f, 0.0).r, 1.0); #ifdef COMPILE_FOG_ATMOSPHERE - if(fragPos4f.z <= 0.0000001) { + if(fragPos4f.z == 0.0) { discard; } #endif diff --git a/sources/resources/assets/eagler/glsl/deferred/forward_core.fsh b/sources/resources/assets/eagler/glsl/deferred/forward_core.fsh index 9c94d44..65dbf97 100644 --- a/sources/resources/assets/eagler/glsl/deferred/forward_core.fsh +++ b/sources/resources/assets/eagler/glsl/deferred/forward_core.fsh @@ -404,6 +404,7 @@ void main() { vec3 dlightDist3f, dlightDir3f, dlightColor3f; int safeLightCount = u_dynamicLightCount1i > 12 ? 0 : u_dynamicLightCount1i; // hate this + float cm; for(int i = 0; i < safeLightCount; ++i) { dlightDist3f = worldPosition4f.xyz - u_dynamicLightArray[i].u_lightPosition4f.xyz; dlightDir3f = normalize(dlightDist3f); @@ -412,9 +413,11 @@ void main() { continue; } dlightColor3f = u_dynamicLightArray[i].u_lightColor4f.rgb / dot(dlightDist3f, dlightDist3f); - if(dlightColor3f.r + dlightColor3f.g + dlightColor3f.b < 0.025) { + cm = dlightColor3f.r + dlightColor3f.g + dlightColor3f.b; + if(cm < 0.025) { continue; } + dlightColor3f *= ((cm - 0.025) / cm); lightColor3f += eaglercraftLighting(diffuseColor4f.rgb, dlightColor3f, -worldDirection4f.xyz, dlightDir3f, normalVector3f, materialData3f, metalN, metalK) * u_blockSkySunDynamicLightFac4f.w; } @@ -451,7 +454,7 @@ void main() { #ifdef COMPILE_FOG_LIGHT_SHAFTS fogBlend4f.rgb *= pow(textureLod(u_lightShaftsTexture, v_positionClip2f * 0.5 + 0.5, 0.0).r * 0.9 + 0.1, 2.25); - fogBlend4f.a = fogBlend4f.a * 0.9 + 0.1; + fogBlend4f.a = fogBlend4f.a * 0.85 + 0.2; #endif break; } diff --git a/sources/resources/assets/eagler/glsl/deferred/forward_glass_highlights.fsh b/sources/resources/assets/eagler/glsl/deferred/forward_glass_highlights.fsh index 5f0100f..51e12b3 100644 --- a/sources/resources/assets/eagler/glsl/deferred/forward_glass_highlights.fsh +++ b/sources/resources/assets/eagler/glsl/deferred/forward_glass_highlights.fsh @@ -268,6 +268,7 @@ void main() { vec3 dlightDist3f, dlightDir3f, dlightColor3f; int safeLightCount = u_dynamicLightCount1i > 12 ? 0 : u_dynamicLightCount1i; // hate this + float cm; for(int i = 0; i < safeLightCount; ++i) { dlightDist3f = u_dynamicLightArray[i].u_lightPosition4f.xyz - worldPosition4f.xyz; dlightDir3f = normalize(dlightDist3f); @@ -275,9 +276,11 @@ void main() { continue; } dlightColor3f = u_dynamicLightArray[i].u_lightColor4f.rgb / dot(dlightDist3f, dlightDist3f); - if(dlightColor3f.r + dlightColor3f.g + dlightColor3f.b < 0.025) { + cm = dlightColor3f.r + dlightColor3f.g + dlightColor3f.b; + if(cm < 0.025) { continue; } + dlightColor3f *= ((cm - 0.025) / cm); lightColor3f += eaglercraftLighting_Glass(dlightColor3f, -worldDirection4f.xyz, dlightDir3f, normalVector3f) * u_blockSkySunDynamicLightFac4f.w; } diff --git a/sources/resources/assets/eagler/glsl/deferred/hand_depth_mask.fsh b/sources/resources/assets/eagler/glsl/deferred/hand_depth_mask.fsh index 9386082..ad78098 100644 --- a/sources/resources/assets/eagler/glsl/deferred/hand_depth_mask.fsh +++ b/sources/resources/assets/eagler/glsl/deferred/hand_depth_mask.fsh @@ -25,5 +25,5 @@ in vec2 v_position2f; uniform sampler2D u_depthTexture; void main() { - gl_FragDepth = textureLod(u_depthTexture, v_position2f, 0.0).r <= 0.0000001 ? 0.0 : 1.0; + gl_FragDepth = textureLod(u_depthTexture, v_position2f, 0.0).r == 0.0 ? 0.0 : 1.0; } diff --git a/sources/resources/assets/eagler/glsl/deferred/light_shafts_sample.fsh b/sources/resources/assets/eagler/glsl/deferred/light_shafts_sample.fsh index 85a1509..d69ad3b 100644 --- a/sources/resources/assets/eagler/glsl/deferred/light_shafts_sample.fsh +++ b/sources/resources/assets/eagler/glsl/deferred/light_shafts_sample.fsh @@ -84,7 +84,7 @@ float shadow(in vec3 coords) { void main() { output1f = 0.0; float depth = textureLod(u_gbufferDepthTexture, v_position2f, 0.0).r; - if(depth < 0.00001) { + if(depth == 0.0) { return; } diff --git a/sources/resources/assets/eagler/glsl/deferred/lighting_point.fsh b/sources/resources/assets/eagler/glsl/deferred/lighting_point.fsh index a771f2e..0127732 100644 --- a/sources/resources/assets/eagler/glsl/deferred/lighting_point.fsh +++ b/sources/resources/assets/eagler/glsl/deferred/lighting_point.fsh @@ -47,7 +47,7 @@ void main() { vec3 materialData3f; float depth = textureLod(u_gbufferDepthTexture, v_position2f, 0.0).r; - if(depth < 0.00001) { + if(depth == 0.0) { discard; } @@ -59,11 +59,14 @@ void main() { worldSpacePosition = u_inverseViewMatrix4f * worldSpacePosition; vec3 lightDist = (worldSpacePosition.xyz / worldSpacePosition.w) - u_lightPosition3f; vec3 color3f = u_lightColor3f / dot(lightDist, lightDist); + float cm = color3f.r + color3f.g + color3f.b; - if(color3f.r + color3f.g + color3f.b < 0.025) { + if(cm < 0.025) { discard; } + color3f *= ((cm - 0.025) / cm); + vec4 sampleVar4f = textureLod(u_gbufferColorTexture, v_position2f, 0.0); diffuseColor3f.rgb = sampleVar4f.rgb; lightmapCoords2f.x = sampleVar4f.a; diff --git a/sources/resources/assets/eagler/glsl/deferred/lighting_sun.fsh b/sources/resources/assets/eagler/glsl/deferred/lighting_sun.fsh index 0edbba4..de8be41 100644 --- a/sources/resources/assets/eagler/glsl/deferred/lighting_sun.fsh +++ b/sources/resources/assets/eagler/glsl/deferred/lighting_sun.fsh @@ -64,6 +64,14 @@ void main() { #endif #endif + float depth = textureLod(u_gbufferDepthTexture, v_position2f, 0.0).r; + +#ifndef COMPILE_SUN_SHADOW + if(depth == 0.0) { + discard; + } +#endif + vec4 sampleVar4f = textureLod(u_gbufferNormalTexture, v_position2f, 0.0); #ifndef COMPILE_SUN_SHADOW @@ -77,14 +85,6 @@ void main() { normalVector3f.xyz = sampleVar4f.rgb * 2.0 - 1.0; lightmapCoords2f.y = sampleVar4f.a; - float depth = textureLod(u_gbufferDepthTexture, v_position2f, 0.0).r; - -#ifndef COMPILE_SUN_SHADOW - if(depth < 0.00001) { - discard; - } -#endif - sampleVar4f = textureLod(u_gbufferColorTexture, v_position2f, 0.0); diffuseColor3f.rgb = sampleVar4f.rgb; lightmapCoords2f.x = sampleVar4f.a; diff --git a/sources/resources/assets/eagler/glsl/deferred/post_bloom_bright.fsh b/sources/resources/assets/eagler/glsl/deferred/post_bloom_bright.fsh index e232ea8..614c2bb 100644 --- a/sources/resources/assets/eagler/glsl/deferred/post_bloom_bright.fsh +++ b/sources/resources/assets/eagler/glsl/deferred/post_bloom_bright.fsh @@ -39,7 +39,7 @@ void main() { if(inputColor.a > 0.0) { emission = textureLod(u_gbufferMaterialTexture, alignedUV, 0.0).b; }else { - emission = textureLod(u_gbufferDepthTexture, alignedUV, 0.0).r <= 0.0000001 ? 10.0 : 0.0; + emission = textureLod(u_gbufferDepthTexture, alignedUV, 0.0).r == 0.0 ? 10.0 : 0.0; } float f = dot(inputColor.rgb, vec3(0.2126, 0.7152, 0.0722)) * (5.0 + emission * 15.0); if(f > 2.0 + exposure) { diff --git a/sources/resources/assets/eagler/glsl/deferred/post_tonemap.fsh b/sources/resources/assets/eagler/glsl/deferred/post_tonemap.fsh index ccbf6ba..ccd734f 100644 --- a/sources/resources/assets/eagler/glsl/deferred/post_tonemap.fsh +++ b/sources/resources/assets/eagler/glsl/deferred/post_tonemap.fsh @@ -34,7 +34,7 @@ void main() { float lumaHDR = textureLod(u_framebufferLumaAvgInput, vec2(0.5), 0.0).r; vec3 input3f = textureLod(u_lightingHDRFramebufferTexture, v_position2f, 0.0).rgb; - input3f /= (0.07 + clamp(lumaHDR * 6.0, 0.2, 4.0)); + input3f /= (0.07 + clamp(lumaHDR * 5.0, 0.18, 4.0)); input3f *= u_exposure3f; diff --git a/sources/resources/assets/eagler/glsl/deferred/realistic_water_mask.fsh b/sources/resources/assets/eagler/glsl/deferred/realistic_water_mask.fsh index e9d318f..85eb078 100644 --- a/sources/resources/assets/eagler/glsl/deferred/realistic_water_mask.fsh +++ b/sources/resources/assets/eagler/glsl/deferred/realistic_water_mask.fsh @@ -29,9 +29,15 @@ uniform vec2 u_textureCoords02; #endif void main() { -#ifdef COMPILE_NORMAL_ATTRIB - output4f = vec4(v_normal3f * 0.5 + 0.5, 1.0); +#ifdef COMPILE_LIGHTMAP_ATTRIB + float skyLight = v_lightmap2f.y; #else - output4f = vec4(0.0, 1.0, 0.0, 1.0); + float skyLight = u_textureCoords02.y; +#endif + skyLight = 0.004 + skyLight * 0.996; +#ifdef COMPILE_NORMAL_ATTRIB + output4f = vec4(v_normal3f * 0.5 + 0.5, skyLight); +#else + output4f = vec4(0.0, 1.0, 0.0, skyLight); #endif } diff --git a/sources/resources/assets/eagler/glsl/deferred/realistic_water_noise.fsh b/sources/resources/assets/eagler/glsl/deferred/realistic_water_noise.fsh index 0b9a60e..1e8be91 100644 --- a/sources/resources/assets/eagler/glsl/deferred/realistic_water_noise.fsh +++ b/sources/resources/assets/eagler/glsl/deferred/realistic_water_noise.fsh @@ -1,7 +1,7 @@ #line 2 /* - * Copyright (c) 2023 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 @@ -37,5 +37,5 @@ void main() { sampleC = textureLod(u_noiseTexture, fract(sampleC) * 0.46875 + vec2(0.515625, 0.515625), 0.0).rg; vec2 sampleD = v_position2f + vec2(-0.135, 0.092) * u_waveTimer4f.x + sampleC * 0.1; sampleD = textureLod(u_noiseTexture, fract(sampleD) * 0.46875 + vec2(0.015625, 0.515625), 0.0).rg; - realisticWaterDisplacementOutput1f = dot(vec4(sampleA.r, sampleB.r, sampleC.r, sampleD.r), vec4(0.63, 0.40, 0.035, 0.035)) + dot(vec2(sampleC.g, sampleD.g), vec2(-0.075 * sampleA.g, 0.053 * sampleA.r)); + realisticWaterDisplacementOutput1f = exp((dot(vec4(sampleA.r, sampleB.r, sampleC.r, sampleD.r), vec4(0.63, 0.40, 0.035, 0.035)) + dot(vec2(sampleC.g, sampleD.g), vec2(-0.075 * sampleA.g, 0.053 * sampleA.r))) * 2.0); } diff --git a/sources/resources/assets/eagler/glsl/deferred/realistic_water_normals.fsh b/sources/resources/assets/eagler/glsl/deferred/realistic_water_normals.fsh index 65bb351..053fc4a 100644 --- a/sources/resources/assets/eagler/glsl/deferred/realistic_water_normals.fsh +++ b/sources/resources/assets/eagler/glsl/deferred/realistic_water_normals.fsh @@ -1,7 +1,7 @@ #line 2 /* - * Copyright (c) 2023 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 @@ -31,5 +31,5 @@ void main() { float A = textureLod(u_displacementTexture, v_position2f, 0.0).r; float B = textureLod(u_displacementTexture, v_position2f + vec2(u_sampleOffset2f.x, 0.0), 0.0).r; float C = textureLod(u_displacementTexture, v_position2f - vec2(0.0, u_sampleOffset2f.y), 0.0).r; - realisticWaterNormalOutput2f = clamp((vec2(A * A) - vec2(B, C) * vec2(B, C)) * 10.0 + 0.5, vec2(0.0), vec2(1.0)); + realisticWaterNormalOutput2f = clamp((vec2(A * A) - vec2(B, C) * vec2(B, C)) * 0.5 + 0.5, vec2(0.0), vec2(1.0)); } diff --git a/sources/resources/assets/eagler/glsl/deferred/realistic_water_normals_mix.fsh b/sources/resources/assets/eagler/glsl/deferred/realistic_water_normals_mix.fsh new file mode 100644 index 0000000..8939e36 --- /dev/null +++ b/sources/resources/assets/eagler/glsl/deferred/realistic_water_normals_mix.fsh @@ -0,0 +1,35 @@ +#line 2 + +/* + * 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. + * + */ + +precision lowp int; +precision mediump float; +precision mediump sampler2D; + +in vec2 v_position2f; + +layout(location = 0) out vec4 combinedNormalsOutput4f; + +uniform sampler2D u_gbufferNormalsTexture; +uniform sampler2D u_surfaceNormalsTexture; + +void main() { + combinedNormalsOutput4f = textureLod(u_surfaceNormalsTexture, v_position2f, 0.0); + if(combinedNormalsOutput4f.a > 0.0) return; + combinedNormalsOutput4f.rgb = textureLod(u_gbufferNormalsTexture, v_position2f, 0.0).rgb; + combinedNormalsOutput4f.a = 0.0; +} diff --git a/sources/resources/assets/eagler/glsl/deferred/realistic_water_render.fsh b/sources/resources/assets/eagler/glsl/deferred/realistic_water_render.fsh index 15453ff..41f05ad 100644 --- a/sources/resources/assets/eagler/glsl/deferred/realistic_water_render.fsh +++ b/sources/resources/assets/eagler/glsl/deferred/realistic_water_render.fsh @@ -1,7 +1,7 @@ #line 2 /* - * Copyright (c) 2023 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 @@ -230,16 +230,17 @@ void main() { worldPosition4fOff.xyz += u_wavingBlockOffset3f; vec2 rotatedUV2f = worldPosition4fOff.xz + (block1f == 10.0 ? u_waterWindOffset4f.z * texCoords2f : u_waterWindOffset4f.xy); - rotatedUV2f *= (block1f == 10.0 ? 0.75 : 0.25); + rotatedUV2f *= (block1f == 10.0 ? 0.30 : 0.10); mat3 cf = cotangent_frame(normalVector3f, worldDirection4f.xyz, rotatedUV2f); - vec3 surfaceNormalsMap3f = vec3(textureLod(u_normalMap, rotatedUV2f, 0.0).rg, 0.0); - surfaceNormalsMap3f.xy *= 2.0; - surfaceNormalsMap3f.xy -= 1.0; + vec3 surfaceNormalsMap3f = vec3(textureLod(u_normalMap, rotatedUV2f * 0.68, 0.0).rg - 0.5, 0.0); + surfaceNormalsMap3f.xy += textureLod(u_normalMap, rotatedUV2f, 0.0).rg - 0.5; + surfaceNormalsMap3f.xy += textureLod(u_normalMap, rotatedUV2f * 2.33, 0.0).rg - 0.5; + surfaceNormalsMap3f.xy *= (2.0 / 3.0); vec3 surfaceNormalsMapFlat3f = cf * surfaceNormalsMap3f; - surfaceNormalsMapFlat3f *= 0.1; + surfaceNormalsMapFlat3f *= 0.15; - surfaceNormalsMap3f.z = 8.0; + surfaceNormalsMap3f.z = 7.0; surfaceNormalsMap3f = normalize(surfaceNormalsMap3f); normalVector3f = surfaceNormalsMap3f = cf * surfaceNormalsMap3f; @@ -268,6 +269,18 @@ void main() { vec4 envMapSample4f = textureLod(u_reflectionMap, reflectCoordsR.xy, 0.0); vec4 refractionSample = textureLod(u_refractionMap, reflectCoordsL.xy, 0.0); + if(refractionSample.a == 0.0) { + reflectCoordsL = mat4x3( + u_modelViewProjMat4f_[0].xyw, + u_modelViewProjMat4f_[1].xyw, + u_modelViewProjMat4f_[2].xyw, + u_modelViewProjMat4f_[3].xyw) * worldPosition4f; + reflectCoordsL.xy /= reflectCoordsL.z; + reflectCoordsL.xy *= 0.5; + reflectCoordsL.xy += 0.5; + refractionSample = textureLod(u_refractionMap, reflectCoordsL.xy, 0.0); + } + #ifdef COMPILE_COLOR_ATTRIB refractionSample *= v_color4f * v_color4f * u_color4f * u_color4f; #else @@ -374,6 +387,7 @@ void main() { vec3 dlightDist3f, dlightDir3f, dlightColor3f; int safeLightCount = u_dynamicLightCount1i > 12 ? 0 : u_dynamicLightCount1i; // hate this + float cm; for(int i = 0; i < safeLightCount; ++i) { dlightDist3f = u_dynamicLightArray[i].u_lightPosition4f.xyz - worldPosition4f.xyz; dlightDir3f = normalize(dlightDist3f); @@ -381,9 +395,11 @@ void main() { continue; } dlightColor3f = u_dynamicLightArray[i].u_lightColor4f.rgb / dot(dlightDist3f, dlightDist3f); - if(dlightColor3f.r + dlightColor3f.g + dlightColor3f.b < 0.025) { + cm = dlightColor3f.r + dlightColor3f.g + dlightColor3f.b; + if(cm < 0.025) { continue; } + dlightColor3f *= ((cm - 0.025) / cm); lightColor3f += eaglercraftLighting_Water(diffuseColor4f.rgb, dlightColor3f, -worldDirection4f.xyz, dlightDir3f, normalVector3f) * u_blockSkySunDynamicLightFac4f.w; } @@ -419,7 +435,7 @@ void main() { #ifdef COMPILE_FOG_LIGHT_SHAFTS fogBlend4f.rgb *= pow(textureLod(u_lightShaftsTexture, v_positionClip2f * 0.5 + 0.5, 0.0).r * 0.9 + 0.1, 2.25); - fogBlend4f.a = fogBlend4f.a * 0.9 + 0.1; + fogBlend4f.a = fogBlend4f.a * 0.85 + 0.2; #endif break; } diff --git a/sources/resources/assets/eagler/glsl/deferred/reproject_ssr.fsh b/sources/resources/assets/eagler/glsl/deferred/reproject_ssr.fsh index 343826a..832b0bb 100644 --- a/sources/resources/assets/eagler/glsl/deferred/reproject_ssr.fsh +++ b/sources/resources/assets/eagler/glsl/deferred/reproject_ssr.fsh @@ -1,7 +1,7 @@ #line 2 /* - * Copyright (c) 2023 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 @@ -41,72 +41,96 @@ uniform float u_sampleStep1f; uniform vec4 u_pixelAlignment4f; +uniform int u_sampleDelta1i; + #define maxAge 55.0 #define maxSamples 50.0 void main() { vec2 v_position2f2 = (floor(v_position2f * u_pixelAlignment4f.xy) + 0.25) * (2.0 / u_pixelAlignment4f.zw); - reflectionOutput4f = vec4(0.0, 0.0, 0.0, 0.0); - hitVectorOutput4f = vec4(0.0, 0.0, 0.0, 0.0); float fragDepth = textureLod(u_gbufferDepthTexture, v_position2f2, 0.0).r; if(fragDepth < 0.000001) { + reflectionOutput4f = vec4(0.0, 0.0, 0.0, 0.0); + hitVectorOutput4f = vec4(0.0, 0.0, 0.0, 0.0); return; } - vec4 reflectionInput4f = textureLod(u_reprojectionReflectionInput4f, v_position2f, 0.0); - vec4 hitVectorInput4f = textureLod(u_reprojectionHitVectorInput4f, v_position2f, 0.0); - hitVectorInput4f.a += 1.0; - float f = reflectionInput4f.a < 1.0 ? 1.0 : reflectionInput4f.a; - reflectionInput4f.a = hitVectorInput4f.a > maxAge ? f : reflectionInput4f.a; - if(reflectionInput4f.a < 1.0) { - reflectionOutput4f = reflectionInput4f; - hitVectorOutput4f = hitVectorInput4f; - return; + vec4 reflectionBuffer4f = textureLod(u_reprojectionReflectionInput4f, v_position2f, 0.0); + vec4 hitVectorBuffer4f = textureLod(u_reprojectionHitVectorInput4f, v_position2f, 0.0); + vec4 surfaceNormal4f = textureLod(u_gbufferNormalTexture, v_position2f2, 0.0); + surfaceNormal4f.xyz *= 2.0; + surfaceNormal4f.xyz -= 1.0; + + vec4 reflectionInput4f; + vec4 hitVectorInput4f; + float f; + vec4 fragPos4f; + vec4 reflectionNormal4f; + float sampleStepMod; + vec3 sampleOffset3f; + vec3 reflectionSamplePos3f; + float reflectDepthSample; + vec2 sampleFragDepth; + vec4 colorSample; + + for(int itr = 0; itr < u_sampleDelta1i; ++itr) { + reflectionInput4f = reflectionBuffer4f; + hitVectorInput4f = hitVectorBuffer4f; + reflectionBuffer4f = vec4(0.0, 0.0, 0.0, 0.0); + hitVectorBuffer4f = vec4(0.0, 0.0, 0.0, 0.0); + hitVectorInput4f.a += 1.0; + f = reflectionInput4f.a < 1.0 ? 1.0 : reflectionInput4f.a; + reflectionInput4f.a = hitVectorInput4f.a > maxAge ? f : reflectionInput4f.a; + if(reflectionInput4f.a < 1.0) { + reflectionBuffer4f = reflectionInput4f; + hitVectorBuffer4f = hitVectorInput4f; + continue; + } + + fragPos4f = u_inverseProjectionMatrix4f * (vec4(v_position2f2, fragDepth, 1.0) * 2.0 - 1.0); + fragPos4f.xyz /= fragPos4f.w; + fragPos4f.w = 1.0; + reflectionNormal4f.xyz = reflect(normalize(fragPos4f.xyz), surfaceNormal4f.xyz); + reflectionNormal4f.w = 1.0; + sampleStepMod = (reflectionInput4f.a * 0.03 + 0.15 + length(fragPos4f.xyz) * 0.03) * u_sampleStep1f; + sampleOffset3f = reflectionNormal4f.xyz * reflectionInput4f.a * sampleStepMod; + fragPos4f.xyz += sampleOffset3f; + reflectionNormal4f = u_lastProjectionMatrix4f * fragPos4f; + reflectionNormal4f.xyz /= reflectionNormal4f.w; + reflectionNormal4f.w = 1.0; + reflectionSamplePos3f = reflectionNormal4f.xyz; + reflectionSamplePos3f *= 0.5; + reflectionSamplePos3f += 0.5; + reflectionSamplePos3f.xy = (floor(reflectionSamplePos3f.xy * u_pixelAlignment4f.zw) + 0.5) * (0.5 / u_pixelAlignment4f.xy); + + if(clamp(reflectionSamplePos3f.xy, vec2(0.001), vec2(0.999)) != reflectionSamplePos3f.xy) { + continue; + } + + reflectDepthSample = textureLod(u_lastFrameDepthInput, reflectionSamplePos3f.xy, 0.0).r; + sampleFragDepth = u_lastInverseProjMatrix4x2f * vec4(reflectionNormal4f.xy, reflectDepthSample * 2.0 - 1.0, 1.0); + sampleFragDepth.x /= sampleFragDepth.y; + + reflectDepthSample = sampleFragDepth.x - fragPos4f.z; + if(reflectDepthSample < sampleStepMod * 3.0) { + reflectionInput4f.a += 1.0; + reflectionBuffer4f = reflectionInput4f.a >= maxSamples ? vec4(0.0, 0.0, 0.0, 0.0) : reflectionInput4f; + hitVectorBuffer4f = vec4(0.0, 0.0, 0.0, hitVectorInput4f.a); + continue; + } + + if(abs(reflectDepthSample) > sampleStepMod * 6.0) { + continue; + } + + colorSample = textureLod(u_lastFrameColorInput4f, reflectionSamplePos3f.xy, 0.0); + reflectionBuffer4f = vec4(colorSample.rgb, 0.0); + reflectionBuffer4f.g += 0.0005; + hitVectorBuffer4f = vec4(colorSample.a > 0.0 ? sampleOffset3f : vec3(0.0), 0.0); + hitVectorBuffer4f.g += colorSample.a > 0.0 ? 0.0004 : 0.0; } - vec4 fragPos4f = u_inverseProjectionMatrix4f * (vec4(v_position2f2, fragDepth, 1.0) * 2.0 - 1.0); - fragPos4f.xyz /= fragPos4f.w; - fragPos4f.w = 1.0; - vec4 reflectionNormal4f = textureLod(u_gbufferNormalTexture, v_position2f2, 0.0); - reflectionNormal4f.xyz *= 2.0; - reflectionNormal4f.xyz -= 1.0; - reflectionNormal4f.xyz = reflect(normalize(fragPos4f.xyz), reflectionNormal4f.xyz); - reflectionNormal4f.w = 1.0; - float sampleStepMod = (reflectionInput4f.a * 0.03 + 0.15 + length(fragPos4f.xyz) * 0.03) * u_sampleStep1f; - vec3 sampleOffset3f = reflectionNormal4f.xyz * reflectionInput4f.a * sampleStepMod; - fragPos4f.xyz += sampleOffset3f; - reflectionNormal4f = u_lastProjectionMatrix4f * fragPos4f; - reflectionNormal4f.xyz /= reflectionNormal4f.w; - reflectionNormal4f.w = 1.0; - vec3 reflectionSamplePos3f = reflectionNormal4f.xyz; - reflectionSamplePos3f *= 0.5; - reflectionSamplePos3f += 0.5; - reflectionSamplePos3f.xy = (floor(reflectionSamplePos3f.xy * u_pixelAlignment4f.zw) + 0.5) * (0.5 / u_pixelAlignment4f.xy); - - if(clamp(reflectionSamplePos3f.xy, vec2(0.001), vec2(0.999)) != reflectionSamplePos3f.xy) { - return; - } - - float reflectDepthSample = textureLod(u_lastFrameDepthInput, reflectionSamplePos3f.xy, 0.0).r; - vec2 sampleFragDepth = u_lastInverseProjMatrix4x2f * vec4(reflectionNormal4f.xy, reflectDepthSample * 2.0 - 1.0, 1.0); - sampleFragDepth.x /= sampleFragDepth.y; - - reflectDepthSample = sampleFragDepth.x - fragPos4f.z; - if(reflectDepthSample < sampleStepMod * 3.0) { - reflectionInput4f.a += 1.0; - reflectionOutput4f = reflectionInput4f.a >= maxSamples ? vec4(0.0, 0.0, 0.0, 0.0) : reflectionInput4f; - hitVectorOutput4f = vec4(0.0, 0.0, 0.0, hitVectorInput4f.a); - return; - } - - if(abs(reflectDepthSample) > sampleStepMod * 6.0) { - return; - } - - vec4 colorSample = textureLod(u_lastFrameColorInput4f, reflectionSamplePos3f.xy, 0.0); - reflectionOutput4f = vec4(colorSample.rgb, 0.0); - reflectionOutput4f.g += 0.0005; - hitVectorOutput4f = vec4(colorSample.a > 0.0 ? sampleOffset3f : vec3(0.0), 0.0); - hitVectorOutput4f.g += colorSample.a > 0.0 ? 0.0004 : 0.0; + reflectionOutput4f = reflectionBuffer4f; + hitVectorOutput4f = hitVectorBuffer4f; } diff --git a/sources/resources/assets/eagler/glsl/deferred/shader_pack_info.json b/sources/resources/assets/eagler/glsl/deferred/shader_pack_info.json index 9cf966a..03ad299 100644 --- a/sources/resources/assets/eagler/glsl/deferred/shader_pack_info.json +++ b/sources/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.2.2", + "vers": "1.3.0", "author": "lax1dude", "api_vers": 1, "features": [ diff --git a/sources/resources/assets/eagler/glsl/deferred/shadows_sun.fsh b/sources/resources/assets/eagler/glsl/deferred/shadows_sun.fsh index cc466ed..7e1567a 100644 --- a/sources/resources/assets/eagler/glsl/deferred/shadows_sun.fsh +++ b/sources/resources/assets/eagler/glsl/deferred/shadows_sun.fsh @@ -79,7 +79,7 @@ void main() { output1f = 0.0; #endif float depth = textureLod(u_gbufferDepthTexture, v_position2f, 0.0).r; - if(depth < 0.00001) { + if(depth == 0.0) { return; } vec4 normalVector4f = textureLod(u_gbufferNormalTexture, v_position2f, 0.0); diff --git a/sources/resources/assets/eagler/glsl/deferred/ssao_generate.fsh b/sources/resources/assets/eagler/glsl/deferred/ssao_generate.fsh index 7f0e37e..34fa3f6 100644 --- a/sources/resources/assets/eagler/glsl/deferred/ssao_generate.fsh +++ b/sources/resources/assets/eagler/glsl/deferred/ssao_generate.fsh @@ -54,7 +54,7 @@ vec3(0.716,-0.439,0.543),vec3(-0.400,0.733,0.550)); void main() { vec3 originalClipSpacePos = vec3(v_position2f, textureLod(u_gbufferDepthTexture, v_position2f, 0.0).r); - if(originalClipSpacePos.z <= 0.0000001) { + if(originalClipSpacePos.z == 0.0) { output1f = 1.0; return; } diff --git a/sources/resources/assets/eagler/icudt/nfc.nrm b/sources/resources/assets/eagler/icudt/nfc.nrm new file mode 100644 index 0000000..45c2608 Binary files /dev/null and b/sources/resources/assets/eagler/icudt/nfc.nrm differ diff --git a/sources/resources/assets/eagler/icudt/nfkc.nrm b/sources/resources/assets/eagler/icudt/nfkc.nrm new file mode 100644 index 0000000..2c0587b Binary files /dev/null and b/sources/resources/assets/eagler/icudt/nfkc.nrm differ diff --git a/sources/resources/assets/eagler/icudt/ubidi.icu b/sources/resources/assets/eagler/icudt/ubidi.icu new file mode 100644 index 0000000..bec7b09 Binary files /dev/null and b/sources/resources/assets/eagler/icudt/ubidi.icu differ diff --git a/sources/resources/assets/eagler/icudt/uprops.icu b/sources/resources/assets/eagler/icudt/uprops.icu new file mode 100644 index 0000000..ee08ff5 Binary files /dev/null and b/sources/resources/assets/eagler/icudt/uprops.icu differ diff --git a/sources/resources/plugin_download.zip b/sources/resources/plugin_download.zip index 0db389f..ccd84bc 100644 Binary files a/sources/resources/plugin_download.zip and b/sources/resources/plugin_download.zip differ diff --git a/sources/resources/plugin_version.json b/sources/resources/plugin_version.json index d5b0c6f..b758fce 100644 --- a/sources/resources/plugin_version.json +++ b/sources/resources/plugin_version.json @@ -1 +1 @@ -{"pluginName":"EaglercraftXBungee","pluginVersion":"1.3.4","pluginButton":"Download \"EaglerXBungee-1.3.4.jar\"","pluginFilename":"EaglerXBungee.zip"} \ No newline at end of file +{"pluginName":"EaglercraftXBungee","pluginVersion":"1.3.5","pluginButton":"Download \"EaglerXBungee-1.3.5.jar\"","pluginFilename":"EaglerXBungee.zip"} \ No newline at end of file diff --git a/sources/resources/relay_download.zip b/sources/resources/relay_download.zip index 5b5db2c..00b74aa 100644 Binary files a/sources/resources/relay_download.zip and b/sources/resources/relay_download.zip differ diff --git a/sources/setup/workspace_template/wasm_gc_teavm/javascript/epw_meta.txt b/sources/setup/workspace_template/wasm_gc_teavm/javascript/epw_meta.txt index 0b61420..c42fc19 100644 --- a/sources/setup/workspace_template/wasm_gc_teavm/javascript/epw_meta.txt +++ b/sources/setup/workspace_template/wasm_gc_teavm/javascript/epw_meta.txt @@ -1,8 +1,8 @@ -client-version-integer=46 +client-version-integer=47 client-package-name=net.lax1dude.eaglercraft.v1_8.client client-origin-name=EaglercraftX -client-origin-version=u46 +client-origin-version=u47 client-origin-vendor=lax1dude client-fork-name=EaglercraftX -client-fork-version=u46 +client-fork-version=u47 client-fork-vendor=lax1dude diff --git a/sources/setup/workspace_template/wasm_gc_teavm/javascript_dist/bootstrap.js b/sources/setup/workspace_template/wasm_gc_teavm/javascript_dist/bootstrap.js index 1062d55..1c94eac 100644 --- a/sources/setup/workspace_template/wasm_gc_teavm/javascript_dist/bootstrap.js +++ b/sources/setup/workspace_template/wasm_gc_teavm/javascript_dist/bootstrap.js @@ -1,12 +1,13 @@ -(function(){'use strict';function g(a){console.log("LoaderBootstrap: [INFO] "+a)}function n(a){console.error("LoaderBootstrap: [ERROR] "+a)}var q=null; -function r(){const a=[];for(var c=0;64>c;++c)a["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charCodeAt(c)]=c;a[45]=62;a[95]=63;return function(b,d){var e=b.length-d;if(0>16&255,f[h++]=d>>8&255,f[h++]=d&255;2===e?(d=a[b.charCodeAt(k)]<<2|a[b.charCodeAt(k+1)]>>4,f[h++]=d&255):1===e&&(d=a[b.charCodeAt(k)]<<10|a[b.charCodeAt(k+1)]<<4|a[b.charCodeAt(k+2)]>>2,f[h++]=d>>8&255,f[h++]=d&255);return f.buffer}}function u(){return new Promise(function(a){setTimeout(a,20)})}function v(a){return new Promise(function(c){fetch(a,{cache:"force-cache"}).then(function(b){return b.arrayBuffer()}).then(c).catch(function(b){n("Failed to fetch URL! "+b);c(null)})})} -function w(a){return a.startsWith("data:application/octet-stream;base64,")?new Promise(function(c){v(a).then(function(b){if(b)c(b);else{console.log("LoaderBootstrap: [WARN] Failed to decode base64 via fetch, doing it the slow way instead...");try{q||=r();var d=q(a,37);c(d)}catch(e){n("Failed to decode base64! "+e),c(null)}}})}):v(a)} -function x(a,c){const b=document.createElement("h2");b.style.color="#AA0000";b.style.padding="25px";b.style.fontFamily="sans-serif";b.style.marginBlock="0px";b.appendChild(document.createTextNode(c));a.appendChild(b);c=document.createElement("h4");c.style.color="#AA0000";c.style.padding="25px";c.style.fontFamily="sans-serif";c.style.marginBlock="0px";c.appendChild(document.createTextNode("Try again later"));a.style.backgroundColor="white";a.appendChild(c)} -window.main=async function(){if("undefined"===typeof window.eaglercraftXOpts)n("window.eaglercraftXOpts is not defined!"),alert("window.eaglercraftXOpts is not defined!");else{var a=window.eaglercraftXOpts.container;if("string"!==typeof a)n("window.eaglercraftXOpts.container is not a string!"),alert("window.eaglercraftXOpts.container is not a string!");else{var c=window.eaglercraftXOpts.assetsURI;if("string"!==typeof c)if("object"===typeof c&&"object"===typeof c[0]&&"string"===typeof c[0].url)c=c[0].url; -else{n("window.eaglercraftXOpts.assetsURI is not a string!");alert("window.eaglercraftXOpts.assetsURI is not a string!");return}var b=document.getElementById(a);if(b){for(;a=b.lastChild;)b.removeChild(a);a=document.createElement("div");a.style.width="100%";a.style.height="100%";a.style.setProperty("image-rendering","pixelated");a.style.background='center / contain no-repeat url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAAAAAB3tzPbAAAACXBIWXMAAC4jAAAuIwF4pT92AAAG+UlEQVR42u2cy23jOhRATwbTwGwFvAJoF6BFGjColcGkASNuIPA6C68DN+BADZiCVxLSQBYqIGYBAbSdEvwWkvUzZWfymwlwCQwQUZeXPOT9URPkYs/3bj8QAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAH4x9vPvzFpAhAzM98UILmqfjDf1YT0N/cBk+71v+wDSczHmDeJ6TqO+SIfyD7IvC9g33Yc7dP6CQDxB+q62Hc2xnyJD2Sf5vuzL3Hi5MM0WbCN51u/Y/30ryEGmDVHlhwsY9Y7xlq0CuzVc4lh2n7NkGsnQ1nB7IefmrY/araJcbrq6Ryk9YqW4l3J/dHww1jdej+8kte042EW0Nba1hyWdl+9irq/FNXaD6BbQoexuvf+tQC2vX1+AFvP0kxiuyidfWwEbOtQtK0n0r6xbYCKsLcM21+pLZX3u4984Kq2xlnWDimllRudAXEpkGSHfqMzsmxfWnLWNf9aQznW4wMZWOMJxvGs/Ff5X+yPcD0g3dqZesdsI2f7Z2/73W2JSok9Gqu7P1q/I2qtj0qn/ZkTaCPWO2a0VyjrxY7sNUG1LxRlaE90MpDpGVeAxpaGobN2XPWH0aQVE1stfXPAj0+XzUmcob3aTRdVZ2+tRv+gMNBDaTkZ4k6uhtYPaK7iUkUcx9lgij92gZ6aXmxoDeK8D1hPfm18oBvTfPGwXoVG+4VfXcwl8dEOtCJS7De9M0VTqTA2p081O3kJ+uk5cU/RVN8C262Ms9HMlLHSmhNFTcc9u1uQRX4jMhqyNIk1GRk69a6hb0IDZ3pITnbfNqFuJWE9gbYrfmSqen/SiKy27G0VS20VWc+UEn59/YDPkc+0EunrAXQ/JXucYL+3VutyAqvP5wFvtEoyQPsMJMpKc3v7/Su9ALLkhAJDPCObGTDmonfNHAij3sg5866fmTHGnFt/crroh6vEv/Rq6vhEoP7hWWb2ylSQZP5zOVrDqVxSZnm/xL6OFnZwF3/4JoyGjyXu1X3n0rEFyE5Jzc5KEDfT7s2ZYs52s5e1HU88hB17nKTqAroXWPpXiHbN7R3Q8fVDbjzU6vb8hUbX67FWN8Xo4U5SIWjbukr1knY9XrcwS30aOuTatqa0vkA6cI05dyPrzWBbj7ZZrPUT2O7pdpKFtp4rph0E0AxtfN0u9kNVg25d4BPiDF0+R83dPol7/l4m4yQmQzdX+ISewqTnc8ngp94yaCan4vT+Hc228q8/T35+e8+XueSqCaPmEz9ofdbX6eSqE5iN/m4A8Qd9w/1bAEl2fPmafT3Axdv/ytlFeXUwTZyyf+NA3hWDGPrm+HXtHSdQ7nrz7fvv+MPFe/9Q3nAS+iYA3zcKCYAACIAACIAACIAACIAACIAACIAA1C2Komh++r9cogdv90M0+GoZAVHkSiGSaFmOmJdTRdESiKJ5Je4eovnSldoGNJ44gTBNbx+XH7tDYxwOniAPgEdygGWxTm/jBCAHV0u7xa90PV64IW0uOWdCapK7t600vfF2j4Ad5FCE4IopCSWMSg0Q4NgRVNKrwIBJ1ZDGxXO/5+fxhDvFQ87EsHxZMy9Sli/raMbjf9eqMpiciQG3yYOJwW1eQoBoesNBzG3yKdvqNwie1HMwiXFcwo7L7aMBtlSrC7c79RzyUm5w0f66Gk1vcJs8vFYHxUvy/u8leJz4N8t8vX5ccl04Chz5BOLR+mVVWXX5lsU4ncSOFevL7WFsJbYiPfQpcvJwhNsBxKiwcHDPNnoojzp8Jh8PnusiSMcLd1B8R5i+Igq5/BZKU3IEO8cIpoqw6L5NR8kjuOIaFR6GlmKdvmnhuFTsfqNwTBnzBOo+ZFua+jh3jAZtnksMu/b850wIfh1sVwVPhMEzKK9lz/+7Hi3Kx8CjOajVbVCEz3kIT1wyYnsD6s5t8tUaGLFpTfC7q2TH4rjzHMCoGgqTOJiMFi/TY5kduOJWHfzdtzdFrS4PYBwzhi0LAKcAdTcvKhur+VWQ3/TWcq/+LJG5VahUsILHUDGiGCmKy26cOrxlxwZUsMHlvVDW7lMQwghGOGZpmt6zcdFD47EhtQVyWySQRHUgVDzhmkeClyZFlGmiA5BH0WpyB+twPp/cgQpQBH0Lqt6qaTwfs+OW6Kl/RrdET/WqQi5BgWLDqNxmdV/Mo1X1QX5Ms0Pq/jmaP7d2/b6IVq3HW+a9qT7v6/TDNv2+tVA0hzz8klroc07AbXKmN98YQMppARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARCAD2//A2iD9ZsgY5XpAAAAAElFTkSuQmCC") white'; -b.appendChild(a);c.startsWith("data:")?(g('Downloading EPW file ""...'),c=await w(c)):(g('Downloading EPW file "'+c+'"...'),c=await v(c));var d=!1;c?384>c.byteLength&&(n("The EPW file is too short"),d=!0):d=!0;if(d)b.removeChild(a),x(b,"Failed to download EPW file!"),n("Failed to download EPW file!");else{var e=new DataView(c);if(608649541!==e.getUint32(0,!0)||1297301847!==e.getUint32(4,!0))n("The file is not an EPW file"),d=!0;var f=c.byteLength;e.getUint32(8,!0)!==f&&(n("The EPW file is the wrong length"), -d=!0);if(d)b.removeChild(a),x(b,"EPW file is invalid!"),n("EPW file is invalid!");else{var l=new TextDecoder("utf-8"),h=e.getUint32(100,!0),k=e.getUint32(104,!0),m=e.getUint32(108,!0),p=e.getUint32(112,!0);if(0>h||h+k>f||0>m||m+p>f)n("The EPW file contains an invalid offset (component: splash)"),d=!0;if(d)b.removeChild(a),x(b,"EPW file is invalid!"),n("EPW file is invalid!");else{h=new Uint8Array(c,h,k);m=new Uint8Array(c,m,p);l=URL.createObjectURL(new Blob([h],{type:l.decode(m)}));g("Loaded splash img: "+ -l);a.style.background='center / contain no-repeat url("'+l+'"), 0px 0px / 1000000% 1000000% no-repeat url("'+l+'") white';await u();p=e.getUint32(164,!0);h=e.getUint32(168,!0);m=e.getUint32(180,!0);e=e.getUint32(184,!0);if(0>p||p+h>f||0>m||m+e>f)n("The EPW file contains an invalid offset (component: loader)"),d=!0;if(d)b.removeChild(a),x(b,"EPW file is invalid!"),n("EPW file is invalid!");else{a=new Uint8Array(c,p,h);a=URL.createObjectURL(new Blob([a],{type:"text/javascript;charset=utf-8"}));g("Loaded loader.js: "+ -l);d=new Uint8Array(c,m,e);d=URL.createObjectURL(new Blob([d],{type:"application/wasm"}));g("Loaded loader.wasm: "+d);f={};for(const [t,y]of Object.entries(window.eaglercraftXOpts))"container"!==t&&"assetsURI"!==t&&(f[t]=y);window.__eaglercraftXLoaderContextPre={rootElement:b,eaglercraftXOpts:f,theEPWFileBuffer:c,loaderWASMURL:d,splashURL:l};g("Appending loader.js to document...");b=document.createElement("script");b.type="text/javascript";b.src=a;document.head.appendChild(b)}}}}}else b='window.eaglercraftXOpts.container "'+ -a+'" is not a known element id!',n(b),alert(b)}}};}).call(this); +(function(){'use strict';function g(a){console.log("LoaderBootstrap: [INFO] "+a)}function n(a){console.log("LoaderBootstrap: [WARN] "+a)}function q(a){console.error("LoaderBootstrap: [ERROR] "+a)}var r=null; +function u(){const a=[];for(var c=0;64>c;++c)a["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charCodeAt(c)]=c;a[45]=62;a[95]=63;return function(b,d){var e=b.length-d;if(0>16&255,f[h++]=d>>8&255,f[h++]=d&255;2===e?(d=a[b.charCodeAt(k)]<<2|a[b.charCodeAt(k+1)]>>4,f[h++]=d&255):1===e&&(d=a[b.charCodeAt(k)]<<10|a[b.charCodeAt(k+1)]<<4|a[b.charCodeAt(k+2)]>>2,f[h++]=d>>8&255,f[h++]=d&255);return f.buffer}}function v(){return new Promise(function(a){setTimeout(a,20)})} +function w(a){return new Promise(function(c){const b=new Image;b.addEventListener("load",c);b.addEventListener("error",function(){n("Failed to preload image: "+a);c()});b.src=a;setTimeout(c,50)})}function x(a){return new Promise(function(c){fetch(a,{cache:"force-cache"}).then(function(b){return b.arrayBuffer()}).then(c).catch(function(b){q("Failed to fetch URL! "+b);c(null)})})} +function y(a){return a.startsWith("data:application/octet-stream;base64,")?new Promise(function(c){x(a).then(function(b){if(b)c(b);else{n("Failed to decode base64 via fetch, doing it the slow way instead...");try{r||=u();var d=r(a,37);c(d)}catch(e){q("Failed to decode base64! "+e),c(null)}}})}):x(a)} +function z(a,c){const b=document.createElement("h2");b.style.color="#AA0000";b.style.padding="25px";b.style.fontFamily="sans-serif";b.style.marginBlock="0px";b.appendChild(document.createTextNode(c));a.appendChild(b);c=document.createElement("h4");c.style.color="#AA0000";c.style.padding="25px";c.style.fontFamily="sans-serif";c.style.marginBlock="0px";c.appendChild(document.createTextNode("Try again later"));a.style.backgroundColor="white";a.appendChild(c)} +window.main=async function(){if("undefined"===typeof window.eaglercraftXOpts)q("window.eaglercraftXOpts is not defined!"),alert("window.eaglercraftXOpts is not defined!");else{var a=window.eaglercraftXOpts.container;if("string"!==typeof a)q("window.eaglercraftXOpts.container is not a string!"),alert("window.eaglercraftXOpts.container is not a string!");else{var c=window.eaglercraftXOpts.assetsURI;if("string"!==typeof c)if("object"===typeof c&&"object"===typeof c[0]&&"string"===typeof c[0].url)c=c[0].url; +else{q("window.eaglercraftXOpts.assetsURI is not a string!");alert("window.eaglercraftXOpts.assetsURI is not a string!");return}var b=document.getElementById(a);if(b){for(;a=b.lastChild;)b.removeChild(a);a=document.createElement("div");a.style.width="100%";a.style.height="100%";a.style.setProperty("image-rendering","pixelated");a.style.background='center / contain no-repeat url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAAAAAB3tzPbAAAACXBIWXMAAC4jAAAuIwF4pT92AAAG+UlEQVR42u2cy23jOhRATwbTwGwFvAJoF6BFGjColcGkASNuIPA6C68DN+BADZiCVxLSQBYqIGYBAbSdEvwWkvUzZWfymwlwCQwQUZeXPOT9URPkYs/3bj8QAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAH4x9vPvzFpAhAzM98UILmqfjDf1YT0N/cBk+71v+wDSczHmDeJ6TqO+SIfyD7IvC9g33Yc7dP6CQDxB+q62Hc2xnyJD2Sf5vuzL3Hi5MM0WbCN51u/Y/30ryEGmDVHlhwsY9Y7xlq0CuzVc4lh2n7NkGsnQ1nB7IefmrY/araJcbrq6Ryk9YqW4l3J/dHww1jdej+8kte042EW0Nba1hyWdl+9irq/FNXaD6BbQoexuvf+tQC2vX1+AFvP0kxiuyidfWwEbOtQtK0n0r6xbYCKsLcM21+pLZX3u4984Kq2xlnWDimllRudAXEpkGSHfqMzsmxfWnLWNf9aQznW4wMZWOMJxvGs/Ff5X+yPcD0g3dqZesdsI2f7Z2/73W2JSok9Gqu7P1q/I2qtj0qn/ZkTaCPWO2a0VyjrxY7sNUG1LxRlaE90MpDpGVeAxpaGobN2XPWH0aQVE1stfXPAj0+XzUmcob3aTRdVZ2+tRv+gMNBDaTkZ4k6uhtYPaK7iUkUcx9lgij92gZ6aXmxoDeK8D1hPfm18oBvTfPGwXoVG+4VfXcwl8dEOtCJS7De9M0VTqTA2p081O3kJ+uk5cU/RVN8C262Ms9HMlLHSmhNFTcc9u1uQRX4jMhqyNIk1GRk69a6hb0IDZ3pITnbfNqFuJWE9gbYrfmSqen/SiKy27G0VS20VWc+UEn59/YDPkc+0EunrAXQ/JXucYL+3VutyAqvP5wFvtEoyQPsMJMpKc3v7/Su9ALLkhAJDPCObGTDmonfNHAij3sg5866fmTHGnFt/crroh6vEv/Rq6vhEoP7hWWb2ylSQZP5zOVrDqVxSZnm/xL6OFnZwF3/4JoyGjyXu1X3n0rEFyE5Jzc5KEDfT7s2ZYs52s5e1HU88hB17nKTqAroXWPpXiHbN7R3Q8fVDbjzU6vb8hUbX67FWN8Xo4U5SIWjbukr1knY9XrcwS30aOuTatqa0vkA6cI05dyPrzWBbj7ZZrPUT2O7pdpKFtp4rph0E0AxtfN0u9kNVg25d4BPiDF0+R83dPol7/l4m4yQmQzdX+ISewqTnc8ngp94yaCan4vT+Hc228q8/T35+e8+XueSqCaPmEz9ofdbX6eSqE5iN/m4A8Qd9w/1bAEl2fPmafT3Axdv/ytlFeXUwTZyyf+NA3hWDGPrm+HXtHSdQ7nrz7fvv+MPFe/9Q3nAS+iYA3zcKCYAACIAACIAACIAACIAACIAACIAA1C2Komh++r9cogdv90M0+GoZAVHkSiGSaFmOmJdTRdESiKJ5Je4eovnSldoGNJ44gTBNbx+XH7tDYxwOniAPgEdygGWxTm/jBCAHV0u7xa90PV64IW0uOWdCapK7t600vfF2j4Ad5FCE4IopCSWMSg0Q4NgRVNKrwIBJ1ZDGxXO/5+fxhDvFQ87EsHxZMy9Sli/raMbjf9eqMpiciQG3yYOJwW1eQoBoesNBzG3yKdvqNwie1HMwiXFcwo7L7aMBtlSrC7c79RzyUm5w0f66Gk1vcJs8vFYHxUvy/u8leJz4N8t8vX5ccl04Chz5BOLR+mVVWXX5lsU4ncSOFevL7WFsJbYiPfQpcvJwhNsBxKiwcHDPNnoojzp8Jh8PnusiSMcLd1B8R5i+Igq5/BZKU3IEO8cIpoqw6L5NR8kjuOIaFR6GlmKdvmnhuFTsfqNwTBnzBOo+ZFua+jh3jAZtnksMu/b850wIfh1sVwVPhMEzKK9lz/+7Hi3Kx8CjOajVbVCEz3kIT1wyYnsD6s5t8tUaGLFpTfC7q2TH4rjzHMCoGgqTOJiMFi/TY5kduOJWHfzdtzdFrS4PYBwzhi0LAKcAdTcvKhur+VWQ3/TWcq/+LJG5VahUsILHUDGiGCmKy26cOrxlxwZUsMHlvVDW7lMQwghGOGZpmt6zcdFD47EhtQVyWySQRHUgVDzhmkeClyZFlGmiA5BH0WpyB+twPp/cgQpQBH0Lqt6qaTwfs+OW6Kl/RrdET/WqQi5BgWLDqNxmdV/Mo1X1QX5Ms0Pq/jmaP7d2/b6IVq3HW+a9qT7v6/TDNv2+tVA0hzz8klroc07AbXKmN98YQMppARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARCAD2//A2iD9ZsgY5XpAAAAAElFTkSuQmCC") white'; +b.appendChild(a);await v();c.startsWith("data:")?(g('Downloading EPW file ""...'),c=await y(c)):(g('Downloading EPW file "'+c+'"...'),c=await x(c));var d=!1;c?384>c.byteLength&&(q("The EPW file is too short"),d=!0):d=!0;if(d)b.removeChild(a),z(b,"Failed to download EPW file!"),q("Failed to download EPW file!");else{var e=new DataView(c);if(608649541!==e.getUint32(0,!0)||1297301847!==e.getUint32(4,!0))q("The file is not an EPW file"),d=!0;var f=c.byteLength;e.getUint32(8, +!0)!==f&&(q("The EPW file is the wrong length"),d=!0);if(d)b.removeChild(a),z(b,"EPW file is invalid!"),q("EPW file is invalid!");else{var l=new TextDecoder("utf-8"),h=e.getUint32(100,!0),k=e.getUint32(104,!0),m=e.getUint32(108,!0),p=e.getUint32(112,!0);if(0>h||h+k>f||0>m||m+p>f)q("The EPW file contains an invalid offset (component: splash)"),d=!0;if(d)b.removeChild(a),z(b,"EPW file is invalid!"),q("EPW file is invalid!");else{h=new Uint8Array(c,h,k);m=new Uint8Array(c,m,p);l=URL.createObjectURL(new Blob([h], +{type:l.decode(m)}));await w(l);g("Loaded splash img: "+l);a.style.background='center / contain no-repeat url("'+l+'"), 0px 0px / 1000000% 1000000% no-repeat url("'+l+'") white';await v();p=e.getUint32(164,!0);h=e.getUint32(168,!0);m=e.getUint32(180,!0);e=e.getUint32(184,!0);if(0>p||p+h>f||0>m||m+e>f)q("The EPW file contains an invalid offset (component: loader)"),d=!0;if(d)b.removeChild(a),z(b,"EPW file is invalid!"),q("EPW file is invalid!");else{a=new Uint8Array(c,p,h);a=URL.createObjectURL(new Blob([a], +{type:"text/javascript;charset=utf-8"}));g("Loaded loader.js: "+l);d=new Uint8Array(c,m,e);d=URL.createObjectURL(new Blob([d],{type:"application/wasm"}));g("Loaded loader.wasm: "+d);f={};for(const [t,A]of Object.entries(window.eaglercraftXOpts))"container"!==t&&"assetsURI"!==t&&(f[t]=A);window.__eaglercraftXLoaderContextPre={rootElement:b,eaglercraftXOpts:f,theEPWFileBuffer:c,loaderWASMURL:d,splashURL:l};g("Appending loader.js to document...");b=document.createElement("script");b.type="text/javascript"; +b.src=a;document.head.appendChild(b)}}}}}else b='window.eaglercraftXOpts.container "'+a+'" is not a known element id!',q(b),alert(b)}}};}).call(this); diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java index cf33cc9..9d4e1cb 100644 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java +++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java @@ -42,9 +42,11 @@ import org.teavm.platform.PlatformRunnable; import com.google.common.collect.Collections2; import com.google.common.collect.Iterators; import com.google.common.collect.Sets; +import com.jcraft.jzlib.Deflater; import com.jcraft.jzlib.DeflaterOutputStream; import com.jcraft.jzlib.GZIPInputStream; import com.jcraft.jzlib.GZIPOutputStream; +import com.jcraft.jzlib.Inflater; import com.jcraft.jzlib.InflaterInputStream; import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; @@ -539,11 +541,21 @@ public class PlatformRuntime { } public static String getGLVersion() { - return PlatformOpenGL._wglGetString(RealOpenGLEnums.GL_VERSION); + String ret = PlatformOpenGL._wglGetString(RealOpenGLEnums.GL_VERSION); + if(ret != null) { + return ret; + }else { + return "null"; + } } public static String getGLRenderer() { - return PlatformOpenGL._wglGetString(RealOpenGLEnums.GL_RENDERER); + String ret = PlatformOpenGL._wglGetString(RealOpenGLEnums.GL_RENDERER); + if(ret != null) { + return ret; + }else { + return "null"; + } } public static ByteBuffer allocateByteBuffer(int length) { @@ -1070,6 +1082,23 @@ public class PlatformRuntime { return new DeflaterOutputStream(os); } + @SuppressWarnings("deprecation") + public static int deflateFull(byte[] input, int inputOff, int inputLen, byte[] output, int outputOff, + int outputLen) throws IOException { + Deflater df = new Deflater(); + df.setInput(input, inputOff, inputLen, false); + df.setOutput(output, outputOff, outputLen); + df.init(5); + int c; + do { + c = df.deflate(4); + if(c != 0 && c != 1) { + throw new IOException("Deflater failed! Code " + c); + } + }while(c != 1); + return (int)df.getTotalOut(); + } + public static OutputStream newGZIPOutputStream(OutputStream os) throws IOException { return new GZIPOutputStream(os); } @@ -1078,6 +1107,22 @@ public class PlatformRuntime { return new InflaterInputStream(is); } + @SuppressWarnings("deprecation") + public static int inflateFull(byte[] input, int inputOff, int inputLen, byte[] output, int outputOff, + int outputLen) throws IOException { + Inflater df = new Inflater(); + df.setInput(input, inputOff, inputLen, false); + df.setOutput(output, outputOff, outputLen); + int c; + do { + c = df.inflate(0); + if(c != 0 && c != 1) { + throw new IOException("Inflater failed! Code " + c); + } + }while(c != 1); + return (int)df.getTotalOut(); + } + public static InputStream newGZIPInputStream(InputStream is) throws IOException { return new GZIPInputStream(is); } diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayByteBuffer.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayByteBuffer.java index d06fa67..53e7482 100644 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayByteBuffer.java +++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayByteBuffer.java @@ -9,7 +9,7 @@ import org.teavm.jso.typedarrays.Int8Array; import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; /** - * Copyright (c) 2022-2023 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 @@ -23,7 +23,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; * POSSIBILITY OF SUCH DAMAGE. * */ -public class EaglerArrayByteBuffer implements ByteBuffer { +public class EaglerArrayByteBuffer extends ByteBuffer { final DataView dataView; final Int8Array typedArray; diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayFloatBuffer.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayFloatBuffer.java index 99b683f..a699a5a 100644 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayFloatBuffer.java +++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayFloatBuffer.java @@ -5,7 +5,7 @@ import org.teavm.jso.typedarrays.Float32Array; import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; /** - * Copyright (c) 2022-2023 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 @@ -19,7 +19,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; * POSSIBILITY OF SUCH DAMAGE. * */ -public class EaglerArrayFloatBuffer implements FloatBuffer { +public class EaglerArrayFloatBuffer extends FloatBuffer { final Float32Array typedArray; diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayIntBuffer.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayIntBuffer.java index 33c1fc4..a922c57 100644 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayIntBuffer.java +++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayIntBuffer.java @@ -5,7 +5,7 @@ import org.teavm.jso.typedarrays.Int32Array; import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; /** - * Copyright (c) 2022-2023 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 @@ -19,7 +19,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; * POSSIBILITY OF SUCH DAMAGE. * */ -public class EaglerArrayIntBuffer implements IntBuffer { +public class EaglerArrayIntBuffer extends IntBuffer { final Int32Array typedArray; diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayShortBuffer.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayShortBuffer.java index b457a38..e6995bb 100644 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayShortBuffer.java +++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayShortBuffer.java @@ -5,7 +5,7 @@ import org.teavm.jso.typedarrays.Int16Array; import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; /** - * Copyright (c) 2022-2023 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 @@ -19,7 +19,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; * POSSIBILITY OF SUCH DAMAGE. * */ -public class EaglerArrayShortBuffer implements ShortBuffer { +public class EaglerArrayShortBuffer extends ShortBuffer { final Int16Array typedArray; diff --git a/sources/wasm-gc-teavm-bootstrap/js/main.js b/sources/wasm-gc-teavm-bootstrap/js/main.js index 3770af0..a8a9aa7 100644 --- a/sources/wasm-gc-teavm-bootstrap/js/main.js +++ b/sources/wasm-gc-teavm-bootstrap/js/main.js @@ -133,6 +133,24 @@ function asyncSleep(ms) { }); } +/** + * @param {string} url + * @param {number} ms + * @return {!Promise} + */ +function preloadImage(url, ms) { + return new Promise(function(resolve) { + const imgObj = new Image(); + imgObj.addEventListener("load", resolve); + imgObj.addEventListener("error", function() { + logWarn("Failed to preload image: " + url); + resolve(); + }); + imgObj.src = url; + setTimeout(resolve, ms); + }); +} + /** * @param {string} url * @return {!Promise} @@ -248,6 +266,9 @@ window.main = async function() { splashElement.style.background = "center / contain no-repeat url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAAAAAB3tzPbAAAACXBIWXMAAC4jAAAuIwF4pT92AAAG+UlEQVR42u2cy23jOhRATwbTwGwFvAJoF6BFGjColcGkASNuIPA6C68DN+BADZiCVxLSQBYqIGYBAbSdEvwWkvUzZWfymwlwCQwQUZeXPOT9URPkYs/3bj8QAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAH4x9vPvzFpAhAzM98UILmqfjDf1YT0N/cBk+71v+wDSczHmDeJ6TqO+SIfyD7IvC9g33Yc7dP6CQDxB+q62Hc2xnyJD2Sf5vuzL3Hi5MM0WbCN51u/Y/30ryEGmDVHlhwsY9Y7xlq0CuzVc4lh2n7NkGsnQ1nB7IefmrY/araJcbrq6Ryk9YqW4l3J/dHww1jdej+8kte042EW0Nba1hyWdl+9irq/FNXaD6BbQoexuvf+tQC2vX1+AFvP0kxiuyidfWwEbOtQtK0n0r6xbYCKsLcM21+pLZX3u4984Kq2xlnWDimllRudAXEpkGSHfqMzsmxfWnLWNf9aQznW4wMZWOMJxvGs/Ff5X+yPcD0g3dqZesdsI2f7Z2/73W2JSok9Gqu7P1q/I2qtj0qn/ZkTaCPWO2a0VyjrxY7sNUG1LxRlaE90MpDpGVeAxpaGobN2XPWH0aQVE1stfXPAj0+XzUmcob3aTRdVZ2+tRv+gMNBDaTkZ4k6uhtYPaK7iUkUcx9lgij92gZ6aXmxoDeK8D1hPfm18oBvTfPGwXoVG+4VfXcwl8dEOtCJS7De9M0VTqTA2p081O3kJ+uk5cU/RVN8C262Ms9HMlLHSmhNFTcc9u1uQRX4jMhqyNIk1GRk69a6hb0IDZ3pITnbfNqFuJWE9gbYrfmSqen/SiKy27G0VS20VWc+UEn59/YDPkc+0EunrAXQ/JXucYL+3VutyAqvP5wFvtEoyQPsMJMpKc3v7/Su9ALLkhAJDPCObGTDmonfNHAij3sg5866fmTHGnFt/crroh6vEv/Rq6vhEoP7hWWb2ylSQZP5zOVrDqVxSZnm/xL6OFnZwF3/4JoyGjyXu1X3n0rEFyE5Jzc5KEDfT7s2ZYs52s5e1HU88hB17nKTqAroXWPpXiHbN7R3Q8fVDbjzU6vb8hUbX67FWN8Xo4U5SIWjbukr1knY9XrcwS30aOuTatqa0vkA6cI05dyPrzWBbj7ZZrPUT2O7pdpKFtp4rph0E0AxtfN0u9kNVg25d4BPiDF0+R83dPol7/l4m4yQmQzdX+ISewqTnc8ngp94yaCan4vT+Hc228q8/T35+e8+XueSqCaPmEz9ofdbX6eSqE5iN/m4A8Qd9w/1bAEl2fPmafT3Axdv/ytlFeXUwTZyyf+NA3hWDGPrm+HXtHSdQ7nrz7fvv+MPFe/9Q3nAS+iYA3zcKCYAACIAACIAACIAACIAACIAACIAA1C2Komh++r9cogdv90M0+GoZAVHkSiGSaFmOmJdTRdESiKJ5Je4eovnSldoGNJ44gTBNbx+XH7tDYxwOniAPgEdygGWxTm/jBCAHV0u7xa90PV64IW0uOWdCapK7t600vfF2j4Ad5FCE4IopCSWMSg0Q4NgRVNKrwIBJ1ZDGxXO/5+fxhDvFQ87EsHxZMy9Sli/raMbjf9eqMpiciQG3yYOJwW1eQoBoesNBzG3yKdvqNwie1HMwiXFcwo7L7aMBtlSrC7c79RzyUm5w0f66Gk1vcJs8vFYHxUvy/u8leJz4N8t8vX5ccl04Chz5BOLR+mVVWXX5lsU4ncSOFevL7WFsJbYiPfQpcvJwhNsBxKiwcHDPNnoojzp8Jh8PnusiSMcLd1B8R5i+Igq5/BZKU3IEO8cIpoqw6L5NR8kjuOIaFR6GlmKdvmnhuFTsfqNwTBnzBOo+ZFua+jh3jAZtnksMu/b850wIfh1sVwVPhMEzKK9lz/+7Hi3Kx8CjOajVbVCEz3kIT1wyYnsD6s5t8tUaGLFpTfC7q2TH4rjzHMCoGgqTOJiMFi/TY5kduOJWHfzdtzdFrS4PYBwzhi0LAKcAdTcvKhur+VWQ3/TWcq/+LJG5VahUsILHUDGiGCmKy26cOrxlxwZUsMHlvVDW7lMQwghGOGZpmt6zcdFD47EhtQVyWySQRHUgVDzhmkeClyZFlGmiA5BH0WpyB+twPp/cgQpQBH0Lqt6qaTwfs+OW6Kl/RrdET/WqQi5BgWLDqNxmdV/Mo1X1QX5Ms0Pq/jmaP7d2/b6IVq3HW+a9qT7v6/TDNv2+tVA0hzz8klroc07AbXKmN98YQMppARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARCAD2//A2iD9ZsgY5XpAAAAAElFTkSuQmCC\") white"; rootElement.appendChild(splashElement); + // allow the screen to update + await asyncSleep(20); + /** @type {ArrayBuffer} */ var theEPWFileBuffer; if(assetsURI.startsWith("data:")) { @@ -319,6 +340,9 @@ window.main = async function() { const splashBinSlice = new Uint8Array(theEPWFileBuffer, splashDataOffset, splashDataLength); const splashMIMESlice = new Uint8Array(theEPWFileBuffer, splashMIMEOffset, splashMIMELength); const splashURL = URL.createObjectURL(new Blob([ splashBinSlice ], { "type": textDecoder.decode(splashMIMESlice) })); + + await preloadImage(splashURL, 50); + logInfo("Loaded splash img: " + splashURL); splashElement.style.background = "center / contain no-repeat url(\"" + splashURL + "\"), 0px 0px / 1000000% 1000000% no-repeat url(\"" + splashURL + "\") white"; diff --git a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformNetworking.java b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformNetworking.java index 1916232..47ea664 100644 --- a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformNetworking.java +++ b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformNetworking.java @@ -1,21 +1,7 @@ package net.lax1dude.eaglercraft.v1_8.internal; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - import org.teavm.interop.Import; -import org.teavm.jso.JSObject; -import org.teavm.jso.JSProperty; import org.teavm.jso.core.JSString; -import org.teavm.jso.typedarrays.ArrayBuffer; -import org.teavm.jso.typedarrays.Uint8Array; - -import net.lax1dude.eaglercraft.v1_8.EagUtils; -import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator; -import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter; import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter; import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WASMGCWebSocketClient; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; diff --git a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java index 324c085..fbe7fd5 100644 --- a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java +++ b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java @@ -3,8 +3,6 @@ package net.lax1dude.eaglercraft.v1_8.internal; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.HashMap; -import java.util.Map; import java.util.function.Consumer; import org.teavm.interop.Import; @@ -19,9 +17,13 @@ import org.teavm.jso.dom.html.HTMLElement; import org.teavm.jso.typedarrays.ArrayBuffer; import org.teavm.jso.typedarrays.Uint8Array; +import com.carrotsearch.hppc.IntObjectHashMap; +import com.carrotsearch.hppc.IntObjectMap; +import com.jcraft.jzlib.Deflater; import com.jcraft.jzlib.DeflaterOutputStream; import com.jcraft.jzlib.GZIPInputStream; import com.jcraft.jzlib.GZIPOutputStream; +import com.jcraft.jzlib.Inflater; import com.jcraft.jzlib.InflaterInputStream; import net.lax1dude.eaglercraft.v1_8.Filesystem; @@ -163,11 +165,21 @@ public class PlatformRuntime { } public static String getGLVersion() { - return PlatformOpenGL._wglGetString(RealOpenGLEnums.GL_VERSION); + String ret = PlatformOpenGL._wglGetString(RealOpenGLEnums.GL_VERSION); + if(ret != null) { + return ret; + }else { + return "null"; + } } public static String getGLRenderer() { - return PlatformOpenGL._wglGetString(RealOpenGLEnums.GL_RENDERER); + String ret = PlatformOpenGL._wglGetString(RealOpenGLEnums.GL_RENDERER); + if(ret != null) { + return ret; + }else { + return "null"; + } } public static ByteBuffer allocateByteBuffer(int length) { @@ -311,7 +323,7 @@ public class PlatformRuntime { @Import(module = "platformRuntime", name = "getNextEvent") private static native JSEagRuntimeEvent getNextEvent(); - private static final Map> waitingAsyncDownloads = new HashMap<>(); + private static final IntObjectMap> waitingAsyncDownloads = new IntObjectHashMap<>(); private static int asyncDownloadID = 0; private static void queueAsyncDownload(String uri, boolean forceCache, Consumer cb) { @@ -479,6 +491,23 @@ public class PlatformRuntime { return new DeflaterOutputStream(os); } + @SuppressWarnings("deprecation") + public static int deflateFull(byte[] input, int inputOff, int inputLen, byte[] output, int outputOff, + int outputLen) throws IOException { + Deflater df = new Deflater(); + df.setInput(input, inputOff, inputLen, false); + df.setOutput(output, outputOff, outputLen); + df.init(5); + int c; + do { + c = df.deflate(4); + if(c != 0 && c != 1) { + throw new IOException("Deflater failed! Code " + c); + } + }while(c != 1); + return (int)df.getTotalOut(); + } + public static OutputStream newGZIPOutputStream(OutputStream os) throws IOException { return new GZIPOutputStream(os); } @@ -487,6 +516,22 @@ public class PlatformRuntime { return new InflaterInputStream(is); } + @SuppressWarnings("deprecation") + public static int inflateFull(byte[] input, int inputOff, int inputLen, byte[] output, int outputOff, + int outputLen) throws IOException { + Inflater df = new Inflater(); + df.setInput(input, inputOff, inputLen, false); + df.setOutput(output, outputOff, outputLen); + int c; + do { + c = df.inflate(0); + if(c != 0 && c != 1) { + throw new IOException("Inflater failed! Code " + c); + } + }while(c != 1); + return (int)df.getTotalOut(); + } + public static InputStream newGZIPInputStream(InputStream is) throws IOException { return new GZIPInputStream(is); } diff --git a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java index 50c9660..79439e0 100644 --- a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java +++ b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java @@ -20,6 +20,9 @@ import org.teavm.jso.webaudio.MediaStreamAudioDestinationNode; import org.teavm.jso.webaudio.MediaStreamAudioSourceNode; import org.teavm.jso.webaudio.PannerNode; +import com.carrotsearch.hppc.IntObjectHashMap; +import com.carrotsearch.hppc.IntObjectMap; + import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; @@ -133,7 +136,7 @@ public class PlatformVoiceClient { } static final Map peerList = new HashMap<>(); - static final Map peerListI = new HashMap<>(); + static final IntObjectMap peerListI = new IntObjectHashMap<>(); private static class VoicePeer { diff --git a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/DirectMallocByteBuffer.java b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/DirectMallocByteBuffer.java index 28b09b7..cc1e005 100644 --- a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/DirectMallocByteBuffer.java +++ b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/DirectMallocByteBuffer.java @@ -4,7 +4,7 @@ import org.teavm.interop.Address; import org.teavm.interop.DirectMalloc; /** - * 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 @@ -18,7 +18,7 @@ import org.teavm.interop.DirectMalloc; * POSSIBILITY OF SUCH DAMAGE. * */ -public class DirectMallocByteBuffer implements ByteBuffer { +public class DirectMallocByteBuffer extends ByteBuffer { final Address address; final boolean original; diff --git a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/DirectMallocFloatBuffer.java b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/DirectMallocFloatBuffer.java index a27cc15..057859a 100644 --- a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/DirectMallocFloatBuffer.java +++ b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/DirectMallocFloatBuffer.java @@ -4,7 +4,7 @@ import org.teavm.interop.Address; import org.teavm.interop.DirectMalloc; /** - * 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 @@ -18,7 +18,7 @@ import org.teavm.interop.DirectMalloc; * POSSIBILITY OF SUCH DAMAGE. * */ -public class DirectMallocFloatBuffer implements FloatBuffer { +public class DirectMallocFloatBuffer extends FloatBuffer { final Address address; final boolean original; diff --git a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/DirectMallocIntBuffer.java b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/DirectMallocIntBuffer.java index 2550773..f75390d 100644 --- a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/DirectMallocIntBuffer.java +++ b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/DirectMallocIntBuffer.java @@ -4,7 +4,7 @@ import org.teavm.interop.Address; import org.teavm.interop.DirectMalloc; /** - * 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 @@ -18,7 +18,7 @@ import org.teavm.interop.DirectMalloc; * POSSIBILITY OF SUCH DAMAGE. * */ -public class DirectMallocIntBuffer implements IntBuffer { +public class DirectMallocIntBuffer extends IntBuffer { final Address address; final boolean original; diff --git a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/DirectMallocShortBuffer.java b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/DirectMallocShortBuffer.java index cb79503..db1458e 100644 --- a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/DirectMallocShortBuffer.java +++ b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/DirectMallocShortBuffer.java @@ -4,7 +4,7 @@ import org.teavm.interop.Address; import org.teavm.interop.DirectMalloc; /** - * 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 @@ -18,7 +18,7 @@ import org.teavm.interop.DirectMalloc; * POSSIBILITY OF SUCH DAMAGE. * */ -public class DirectMallocShortBuffer implements ShortBuffer { +public class DirectMallocShortBuffer extends ShortBuffer { final Address address; final boolean original; diff --git a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/WASMGCBufferAllocator.java b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/WASMGCBufferAllocator.java index 9e11fa8..4d329cd 100644 --- a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/WASMGCBufferAllocator.java +++ b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/WASMGCBufferAllocator.java @@ -202,7 +202,7 @@ public class WASMGCBufferAllocator { } public static Uint8Array getUnsignedByteBufferView(ShortBuffer buffer) { - DirectMallocIntBuffer buf = (DirectMallocIntBuffer)buffer; + DirectMallocShortBuffer buf = (DirectMallocShortBuffer)buffer; return getUnsignedByteBufferView0(buf.address.add(buf.position()), buf.remaining() << 1); } diff --git a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/wasm_gc_teavm/BetterJSStringConverter.java b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/wasm_gc_teavm/BetterJSStringConverter.java index b953903..06bff82 100644 --- a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/wasm_gc_teavm/BetterJSStringConverter.java +++ b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/wasm_gc_teavm/BetterJSStringConverter.java @@ -1,13 +1,11 @@ package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm; import org.teavm.interop.Address; -import org.teavm.interop.DirectMalloc; import org.teavm.interop.Import; import org.teavm.interop.Unmanaged; import org.teavm.jso.core.JSArray; import org.teavm.jso.core.JSString; -import net.lax1dude.eaglercraft.v1_8.EagUtils; import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator; /** diff --git a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/wasm_gc_teavm/TeaVMUtils.java b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/wasm_gc_teavm/TeaVMUtils.java index 665c634..0cf958b 100644 --- a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/wasm_gc_teavm/TeaVMUtils.java +++ b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/wasm_gc_teavm/TeaVMUtils.java @@ -2,13 +2,6 @@ package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm; import org.teavm.jso.JSBody; import org.teavm.jso.JSObject; -import org.teavm.jso.typedarrays.ArrayBuffer; -import org.teavm.jso.typedarrays.ArrayBufferView; -import org.teavm.jso.typedarrays.Float32Array; -import org.teavm.jso.typedarrays.Int16Array; -import org.teavm.jso.typedarrays.Int32Array; -import org.teavm.jso.typedarrays.Int8Array; -import org.teavm.jso.typedarrays.Uint8Array; /** * Copyright (c) 2024 lax1dude. All Rights Reserved. diff --git a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/wasm_gc_teavm/WASMGCWebSocketClient.java b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/wasm_gc_teavm/WASMGCWebSocketClient.java index f8cc069..71c83e7 100644 --- a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/wasm_gc_teavm/WASMGCWebSocketClient.java +++ b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/wasm_gc_teavm/WASMGCWebSocketClient.java @@ -1,6 +1,5 @@ package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; diff --git a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java index a2d5ac0..561da07 100644 --- a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java +++ b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java @@ -23,7 +23,6 @@ import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter; import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2; import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter; import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WASMGCClientConfigAdapter; -import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerIntegratedServerWorker; import net.lax1dude.eaglercraft.v1_8.sp.server.IWASMCrashCallback; import net.lax1dude.eaglercraft.v1_8.sp.server.internal.wasm_gc_teavm.JS_IPCPacketData; diff --git a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/wasm_gc_teavm/JS_IPCPacketData.java b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/wasm_gc_teavm/JS_IPCPacketData.java index fde06d9..5d6748c 100644 --- a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/wasm_gc_teavm/JS_IPCPacketData.java +++ b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/wasm_gc_teavm/JS_IPCPacketData.java @@ -2,10 +2,12 @@ package net.lax1dude.eaglercraft.v1_8.sp.server.internal.wasm_gc_teavm; import org.teavm.jso.JSObject; import org.teavm.jso.JSProperty; +import org.teavm.jso.core.JSString; import org.teavm.jso.typedarrays.Uint8Array; import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData; import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter; +import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter; /** * Copyright (c) 2024 lax1dude. All Rights Reserved. @@ -25,13 +27,14 @@ import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter; public interface JS_IPCPacketData extends JSObject { @JSProperty - String getCh(); + JSString getCh(); @JSProperty Uint8Array getData(); default IPCPacketData internalize() { - return new IPCPacketData(getCh(), WASMGCDirectArrayConverter.externU8ArrayToByteArray(getData())); + return new IPCPacketData(BetterJSStringConverter.stringFromJS(getCh()), + WASMGCDirectArrayConverter.externU8ArrayToByteArray(getData())); } }