├── .github └── workflows │ ├── codeql.yml │ └── maven.yml ├── .gitignore ├── LICENSE ├── README.md ├── SECURITY.md ├── bom └── pom.xml ├── dependency-check └── pom.xml ├── pom.xml ├── pt-triemap └── pom.xml └── triemap ├── pom.xml └── src ├── main └── java │ ├── module-info.java │ └── tech │ └── pantheon │ └── triemap │ ├── AbstractEntry.java │ ├── AbstractEntrySet.java │ ├── AbstractIterator.java │ ├── AbstractKeySet.java │ ├── Branch.java │ ├── CNode.java │ ├── Constants.java │ ├── DefaultEntry.java │ ├── EntryNode.java │ ├── Equivalence.java │ ├── Gen.java │ ├── INode.java │ ├── ImmutableEntrySet.java │ ├── ImmutableIterator.java │ ├── ImmutableKeySet.java │ ├── ImmutableTrieMap.java │ ├── ImmutableTrieSet.java │ ├── KeySetIterator.java │ ├── LNode.java │ ├── LNodeEntries.java │ ├── LNodeEntry.java │ ├── MainNode.java │ ├── MutableEntrySet.java │ ├── MutableIterator.java │ ├── MutableKeySet.java │ ├── MutableTrieMap.java │ ├── MutableTrieSet.java │ ├── PresencePredicate.java │ ├── Result.java │ ├── SNode.java │ ├── SerializationProxy.java │ ├── TNode.java │ ├── TrieMap.java │ ├── TrieSet.java │ ├── VerifyException.java │ └── package-info.java └── test └── java └── tech └── pantheon └── triemap ├── AbstractEntryTest.java ├── CNodeTest.java ├── ConstantsTest.java ├── EquivalenceTest.java ├── INodeTest.java ├── ImmutableEntrySetTest.java ├── ImmutableKeySetTest.java ├── ImmutableTrieMapTest.java ├── ImmutableTrieSetTest.java ├── LNodeEntriesTest.java ├── LNodeEntryTest.java ├── MutableEntrySetTest.java ├── MutableIteratorTest.java ├── MutableKeySetTest.java ├── MutableTrieSetTest.java ├── SNodeTest.java ├── SnapshotTest.java ├── TNodeTest.java ├── TestCNodeFlagCollision.java ├── TestCNodeInsertionIncorrectOrder.java ├── TestConcurrentMapCompute.java ├── TestConcurrentMapComputeIfAbsent.java ├── TestConcurrentMapComputeIfPresent.java ├── TestConcurrentMapMerge.java ├── TestConcurrentMapPutIfAbsent.java ├── TestConcurrentMapRemove.java ├── TestConcurrentMapReplace.java ├── TestDelete.java ├── TestHashCollisions.java ├── TestHashCollisionsRemove.java ├── TestHashCollisionsRemoveIterator.java ├── TestInsert.java ├── TestMapIterator.java ├── TestMultiThreadAddDelete.java ├── TestMultiThreadInserts.java ├── TestMultiThreadMapIterator.java ├── TestReadOnlyAndUpdatableIterators.java ├── TestSerialization.java ├── VerifyExceptionTest.java └── ZeroHashInt.java /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [ "master" ] 9 | schedule: 10 | - cron: '36 20 * * 2' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | permissions: 17 | actions: read 18 | contents: read 19 | security-events: write 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | language: [ 'java' ] 25 | 26 | steps: 27 | - name: Checkout repository 28 | uses: actions/checkout@v3 29 | 30 | # Initializes the CodeQL tools for scanning. 31 | - name: Initialize CodeQL 32 | uses: github/codeql-action/init@v2 33 | with: 34 | languages: ${{ matrix.language }} 35 | # If you wish to specify custom queries, you can do so here or in a config file. 36 | # By default, queries listed here will override any specified in a config file. 37 | # Prefix the list here with "+" to use these queries and those in the config file. 38 | 39 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 40 | # queries: security-extended,security-and-quality 41 | 42 | - name: Set up JDK 17 43 | uses: actions/setup-java@v3 44 | with: 45 | java-version: '17' 46 | distribution: 'temurin' 47 | cache: maven 48 | 49 | - name: Build with Maven 50 | run: mvn --batch-mode --errors --show-version -Pq clean package 51 | 52 | - name: Perform CodeQL Analysis 53 | uses: github/codeql-action/analyze@v2 54 | with: 55 | category: "/language:${{matrix.language}}" 56 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Java CI with Maven 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | types: 11 | - opened 12 | - synchronize 13 | - reopened 14 | 15 | jobs: 16 | build: 17 | 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | with: 23 | fetch-depth: 0 24 | 25 | - name: Set up JDK 21 26 | uses: actions/setup-java@v4 27 | with: 28 | java-version: '21' 29 | distribution: 'temurin' 30 | 31 | - name: Cache Maven packages 32 | uses: actions/cache@v4 33 | with: 34 | path: | 35 | ~/.m2/repository/*/*/* 36 | !~/.m2/repository/tech/pantheon/triemap 37 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 38 | restore-keys: ${{ runner.os }}-maven- 39 | 40 | - name: Build with Maven 41 | run: mvn -B -e install --file pom.xml 42 | 43 | - name: Cache SonarCloud packages 44 | uses: actions/cache@v4 45 | with: 46 | path: ~/.sonar/cache 47 | key: ${{ runner.os }}-sonar 48 | restore-keys: ${{ runner.os }}-sonar 49 | 50 | - name: Analyze with SonarCloud 51 | env: 52 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 53 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 54 | if: env.SONAR_TOKEN != '' 55 | run: mvn -B -e verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=PANTHEONtech_triemap 56 | 57 | # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive 58 | #- name: Update dependency graph 59 | # uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | **/target 3 | **/target-ide/ 4 | .idea 5 | .classpath 6 | .project 7 | .settings 8 | *.iml 9 | *.ipr 10 | *.iws 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://github.com/PANTHEONtech/triemap/actions/workflows/maven.yml/badge.svg?event=push)](https://github.com/PANTHEONtech/triemap/actions/workflows/maven.yml) 2 | [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=PANTHEONtech_triemap&metric=coverage)](https://sonarcloud.io/summary/new_code?id=PANTHEONtech_triemap) 3 | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=PANTHEONtech_triemap&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=PANTHEONtech_triemap) 4 | [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/2172/badge)](https://bestpractices.coreinfrastructure.org/projects/2172) 5 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FPantheonTechnologies%2Ftriemap.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2FPantheonTechnologies%2Ftriemap?ref=badge_shield) 6 | [![CodeQL](https://github.com/PANTHEONtech/triemap/actions/workflows/codeql.yml/badge.svg?event=push)](https://github.com/PANTHEONtech/triemap/actions/workflows/codeql.yml) 7 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/tech.pantheon.triemap/triemap/badge.svg)](https://maven-badges.herokuapp.com/maven-central/tech.pantheon.triemap/triemap) 8 | [![Javadocs](https://www.javadoc.io/badge/tech.pantheon.triemap/triemap.svg)](https://www.javadoc.io/doc/tech.pantheon.triemap/triemap) 9 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 10 | 11 | ## About 12 | This is a Java port of a concurrent trie hash map implementation from the Scala collections library. It used to be an almost line-by-line 13 | conversion from Scala to Java. These days it has been refactored to be Java 8 friendly and make some original assertions impossible via 14 | refactoring. 15 | 16 | Idea + implementation techniques can be found in these reports written by Aleksandar Prokopec: 17 | * http://infoscience.epfl.ch/record/166908/files/ctries-techreport.pdf - this is a nice introduction to Ctries, along with a correctness proof 18 | * http://lamp.epfl.ch/~prokopec/ctries-snapshot.pdf - a more up-to-date writeup which describes the snapshot operation 19 | 20 | The code origins can be tracked through these links: 21 | * [Original Java port](https://github.com/romix/java-concurrent-hash-trie-map) 22 | * [Scala implementation](https://github.com/scala/scala/blob/930c85d6c96507d798d1847ea078eebf93dc0acb/src/library/scala/collection/concurrent/TrieMap.scala) 23 | 24 | Some of the tests and implementation details were borrowed from this project: 25 | * https://github.com/flegall/concurrent-hash-trie 26 | 27 | Implementation status : 28 | * The given implementation is complete and implements all features of the original Scala implementation including support for 29 | snapshots. 30 | * Wherever necessary, code was adapted to be more easily usable in Java, e.g. it returns Objects instead of Option as 31 | many methods of Scala's collections do. 32 | * This class implements all the ConcurrentMap & Iterator methods and passes all the tests. Can be used as a drop-in replacement 33 | for usual Java maps, including ConcurrentHashMap. 34 | * The code take advantage of Java 8 to supplant Scala constructs 35 | * The implementation is a Java 9+ JPMS module and can easily be depended upon by other modules 36 | 37 | 38 | ## What is a concurrent trie hash map also known as ctrie? 39 | ctrie is a lock-Free Concurrent Hash Array Mapped Trie. 40 | 41 | A concurrent hash-trie or Ctrie is a concurrent thread-safe lock-free implementation of a hash array mapped trie. 42 | 43 | It is used to implement the concurrent map abstraction. It has particularly scalable concurrent insert and remove operations 44 | and is memory-efficient. 45 | 46 | It supports O(1), atomic, lock-free snapshots which are used to implement linearizable lock-free size, iterator and clear operations. 47 | The cost of evaluating the (lazy) snapshot is distributed across subsequent updates, thus making snapshot evaluation horizontally scalable. 48 | 49 | The original Scala-based implementation of the Ctrie is a part of the Scala standard library since the version 2.10. 50 | 51 | More info about Ctries: 52 | 53 | - http://infoscience.epfl.ch/record/166908/files/ctries-techreport.pdf - this is a nice introduction to Ctries, along with a correctness proof 54 | - http://lamp.epfl.ch/~prokopec/ctries-snapshot.pdf - a more up-to-date writeup (more coherent with the current version of the code) which describes the snapshot operation 55 | 56 | ## Required Java versions 57 | There are multiple release trains of this library: 58 | * Versions 1.1.x require Java 8 or later 59 | * Versions 1.2.x require Java 11 or later 60 | * Versions 1.3.x require Java 17 or later 61 | * Versions 1.4.x require Java 17 or later 62 | 63 | ## License 64 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FPantheonTechnologies%2Ftriemap.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2FPantheonTechnologies%2Ftriemap?ref=badge_large) 65 | 66 | 67 | ## Usage 68 | Usage of this library is very simple. Simply import the class tech.pantheon.triemap.TrieMap and use it as a usual Map. 69 | 70 | ```java 71 | import tech.pantheon.triemap.TrieMap; 72 | 73 | Map myMap = TrieMap.create(); 74 | myMap.put("key", "value"); 75 | ``` 76 | 77 | ## Building the library 78 | 79 | Use a usual `mvn clean install` 80 | 81 | ## Using the library with Maven projects 82 | The prebuilt binaries of the library are available from Maven central. Please use the following dependency in your POM files: 83 | 84 | ```xml 85 | 86 | tech.pantheon.triemap 87 | triemap 88 | 1.3.0 89 | 90 | ``` 91 | 92 | ## External dependencies 93 | This library is self-contained. It does not depend on any additional libraries. In particular, it does not require the rather big Scala's 94 | standard library to be used. 95 | 96 | 97 | ## Contributing 98 | All contributions are welcome! The mechanics follows GitHub norms: we use GH issues to track bugs and improvements. In terms of coding style, 99 | the project follows OpenDaylight's code style -- which is a combination of Google's style guidelines and a few tweaks here and there -- these 100 | are enforced by CheckStyle. Each code contribution should have an attached unit test, for bug fixes this is a strict requirement. 101 | We are also using SpotBugs for static code analysis and prefer no @SuppressFBWarnings. If a suppression is needed, its scope must be minimal 102 | and it must carry a justification. 103 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Versioning 4 | 5 | We follow Semantic Versioning. Our APIs are expected to be extremely stable 6 | and therefore we do not expect to issue a major (2.0.0) version any time soon. 7 | 8 | Minor versions are used when new functionality is introduced, which includes 9 | changing the required baseline Java version. 10 | 11 | Patch versions are issued for bug and security fixes. 12 | 13 | ## Supported Versions 14 | 15 | Versions 1.4.x and 1.3.x (Java 17+), 1.2.x (Java 11+) and 1.1.x (Java 8+) are 16 | currently supported and are receiving any and all relevant bug fixes. 17 | 18 | ## Reporting a Vulnerability 19 | 20 | Please report any discovered or suspected security vulnerabilities to PANTHEON.tech product security team at secalert@pantheon.tech. 21 | 22 | -------------------------------------------------------------------------------- /bom/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 4.0.0 20 | 21 | 22 | org.opendaylight.odlparent 23 | odlparent-lite 24 | 14.0.5 25 | 26 | 27 | 28 | tech.pantheon.triemap 29 | bom 30 | 2.0.0-SNAPSHOT 31 | pom 32 | 33 | PANTHEON.tech :: TrieMap :: Bill of Materials 34 | Bill of Materials POM for the TrieMap project 35 | https://github.com/PantheonTechnologies/triemap 36 | 37 | 38 | 39 | 40 | tech.pantheon.triemap 41 | triemap 42 | 2.0.0-SNAPSHOT 43 | 44 | 45 | tech.pantheon.triemap 46 | pt-triemap 47 | 2.0.0-SNAPSHOT 48 | xml 49 | features 50 | 51 | 52 | 53 | 54 | 55 | 56 | release 57 | 58 | 59 | 60 | maven-gpg-plugin 61 | 3.2.7 62 | 63 | 64 | sign-artifacts 65 | verify 66 | 67 | sign 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | The Apache Software License, Version 2.0 80 | http://www.apache.org/licenses/LICENSE-2.0.txt 81 | repo 82 | 83 | 84 | 85 | scm:git:https://github.com/PantheonTechnologies/triemap.git 86 | https://github.com/PantheonTechnologies/triemap 87 | triemap-2.0.0-SNAPSHOT 88 | 89 | 90 | 91 | rovarga 92 | Robert Varga 93 | robert.varga@pantheon.tech 94 | PANTHEON.tech, s.r.o. 95 | https://www.pantheon.tech 96 | 97 | 98 | 99 | 100 | ossrh 101 | https://oss.sonatype.org/content/repositories/snapshots 102 | 103 | 104 | ossrh 105 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /dependency-check/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 4.0.0 20 | 21 | 22 | org.opendaylight.odlparent 23 | odlparent 24 | 14.0.5 25 | 26 | 27 | 28 | tech.pantheon.triemap 29 | dependency-check 30 | 2.0.0-SNAPSHOT 31 | 32 | PANTHEON.tech :: TrieMap :: Dependency Check 33 | Artifact for validating the contents of BOM 34 | https://github.com/PantheonTechnologies/triemap 35 | 36 | 37 | true 38 | true 39 | 40 | 41 | 42 | 43 | 44 | tech.pantheon.triemap 45 | bom 46 | 2.0.0-SNAPSHOT 47 | import 48 | pom 49 | 50 | 51 | 52 | 53 | 54 | 55 | tech.pantheon.triemap 56 | triemap 57 | 58 | 59 | tech.pantheon.triemap 60 | pt-triemap 61 | xml 62 | features 63 | 64 | 65 | 66 | 67 | 68 | The Apache Software License, Version 2.0 69 | http://www.apache.org/licenses/LICENSE-2.0.txt 70 | repo 71 | 72 | 73 | 74 | scm:git:https://github.com/PantheonTechnologies/triemap.git 75 | scm:git:https://github.com/PantheonTechnologies/triemap.git 76 | https://github.com/PantheonTechnologies/triemap 77 | triemap-2.0.0-SNAPSHOT 78 | 79 | 80 | 81 | rovarga 82 | Robert Varga 83 | robert.varga@pantheon.tech 84 | PANTHEON.tech, s.r.o. 85 | https://www.pantheon.tech 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 4.0.0 20 | 21 | org.opendaylight.odlparent 22 | odlparent-lite 23 | 14.0.5 24 | 25 | 26 | 27 | tech.pantheon.triemap 28 | triemap-aggregator 29 | 2.0.0-SNAPSHOT 30 | pom 31 | 32 | PANTHEON.tech :: TrieMap :: Aggregator 33 | Project top-level aggregator POM 34 | https://github.com/PantheonTechnologies/triemap 35 | 36 | 37 | bom 38 | dependency-check 39 | pt-triemap 40 | triemap 41 | 42 | 43 | 44 | true 45 | true 46 | pantheon-tech 47 | https://sonarcloud.io 48 | 49 | 50 | 51 | 52 | 53 | org.jacoco 54 | jacoco-maven-plugin 55 | 56 | 57 | org.eluder.coveralls 58 | coveralls-maven-plugin 59 | 4.3.0 60 | 61 | 62 | org.apache.maven.plugins 63 | maven-release-plugin 64 | 65 | true 66 | triemap-@{project.version} 67 | false 68 | release 69 | clean install 70 | true 71 | false 72 | deploy 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | The Apache Software License, Version 2.0 81 | http://www.apache.org/licenses/LICENSE-2.0.txt 82 | repo 83 | 84 | 85 | 86 | scm:git:https://github.com/PantheonTechnologies/triemap.git 87 | scm:git:https://github.com/PantheonTechnologies/triemap.git 88 | https://github.com/PantheonTechnologies/triemap 89 | HEAD 90 | 91 | 92 | 93 | rovarga 94 | Robert Varga 95 | robert.varga@pantheon.tech 96 | PANTHEON.tech, s.r.o. 97 | https://www.pantheon.tech 98 | 99 | 100 | 101 | 102 | ossrh 103 | https://oss.sonatype.org/content/repositories/snapshots 104 | 105 | 106 | ossrh 107 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /pt-triemap/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 4.0.0 20 | 21 | 22 | org.opendaylight.odlparent 23 | single-feature-parent 24 | 14.0.5 25 | 26 | 27 | 28 | tech.pantheon.triemap 29 | pt-triemap 30 | 2.0.0-SNAPSHOT 31 | feature 32 | 33 | PANTHEON.tech :: TrieMap :: Feature 34 | Concurrent Hash-trie Map 35 | https://github.com/PantheonTechnologies/triemap 36 | 37 | 38 | 39 | 40 | tech.pantheon.triemap 41 | bom 42 | 2.0.0-SNAPSHOT 43 | import 44 | pom 45 | 46 | 47 | 48 | 49 | 50 | 51 | tech.pantheon.triemap 52 | triemap 53 | 54 | 55 | 56 | 57 | 58 | release 59 | 60 | 61 | 62 | maven-gpg-plugin 63 | 3.2.7 64 | 65 | 66 | sign-artifacts 67 | verify 68 | 69 | sign 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | The Apache Software License, Version 2.0 82 | http://www.apache.org/licenses/LICENSE-2.0.txt 83 | repo 84 | 85 | 86 | 87 | scm:git:https://github.com/PantheonTechnologies/triemap.git 88 | scm:git:https://github.com/PantheonTechnologies/triemap.git 89 | https://github.com/PantheonTechnologies/triemap 90 | triemap-2.0.0-SNAPSHOT 91 | 92 | 93 | 94 | rovarga 95 | Robert Varga 96 | robert.varga@pantheon.tech 97 | PANTHEON.tech, s.r.o. 98 | https://www.pantheon.tech 99 | 100 | 101 | 102 | 103 | ossrh 104 | https://oss.sonatype.org/content/repositories/snapshots 105 | 106 | 107 | ossrh 108 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /triemap/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 4.0.0 20 | 21 | org.opendaylight.odlparent 22 | bnd-parent 23 | 14.0.5 24 | 25 | 26 | 27 | tech.pantheon.triemap 28 | triemap 29 | 2.0.0-SNAPSHOT 30 | 31 | PANTHEON.tech :: TrieMap 32 | Java implementation of a concurrent trie hash map from Scala collections library 33 | https://github.com/PantheonTechnologies/triemap 34 | 35 | 36 | true 37 | 38 | 39 | 40 | 41 | biz.aQute.bnd 42 | biz.aQute.bnd.annotation 43 | 44 | 45 | com.github.spotbugs 46 | spotbugs-annotations 47 | true 48 | 49 | 50 | org.eclipse.jdt 51 | org.eclipse.jdt.annotation 52 | 53 | 54 | 55 | 56 | 57 | 58 | maven-checkstyle-plugin 59 | 60 | 61 | check-license 62 | 63 | check 64 | 65 | 66 | 67 | true 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | release 78 | 79 | 80 | 81 | maven-gpg-plugin 82 | 3.2.7 83 | 84 | 85 | sign-artifacts 86 | verify 87 | 88 | sign 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | PANTHEON.tech 100 | https://pantheon.tech/ 101 | 102 | 103 | 104 | The Apache Software License, Version 2.0 105 | http://www.apache.org/licenses/LICENSE-2.0.txt 106 | repo 107 | 108 | 109 | 110 | scm:git:https://github.com/PantheonTechnologies/triemap.git 111 | scm:git:https://github.com/PantheonTechnologies/triemap.git 112 | https://github.com/PantheonTechnologies/triemap 113 | triemap-2.0.0-SNAPSHOT 114 | 115 | 116 | GitHub 117 | https://github.com/PANTHEONtech/triemap/issues 118 | 119 | 120 | GitHub 121 | https://github.com/PANTHEONtech/triemap/actions 122 | 123 | 124 | 125 | 126 | rovarga 127 | Robert Varga 128 | robert.varga@pantheon.tech 129 | PANTHEON.tech, s.r.o. 130 | https://www.pantheon.tech 131 | 132 | 133 | 134 | 135 | ossrh 136 | https://oss.sonatype.org/content/repositories/snapshots 137 | 138 | 139 | ossrh 140 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /triemap/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2019 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * An implementation of {@link java.util.concurrent.ConcurrentMap} based on 18 | * concurrent hash-trie. 19 | */ 20 | module tech.pantheon.triemap { 21 | exports tech.pantheon.triemap; 22 | 23 | requires static com.github.spotbugs.annotations; 24 | requires static org.eclipse.jdt.annotation; 25 | requires static org.osgi.annotation.bundle; 26 | } 27 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/AbstractEntry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2017 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import java.util.Map.Entry; 19 | 20 | /** 21 | * Base class for things that need to conform to {@link Entry} contract. 22 | * 23 | * @author Robert Varga 24 | */ 25 | abstract sealed class AbstractEntry implements DefaultEntry 26 | permits LNodeEntry, MutableIterator.MutableEntry { 27 | @Override 28 | public final int hashCode() { 29 | return hashCode(key(), value()); 30 | } 31 | 32 | static final int hashCode(final Object key, final Object value) { 33 | return key.hashCode() ^ value.hashCode(); 34 | } 35 | 36 | @Override 37 | public final boolean equals(final Object obj) { 38 | return equals(obj, key(), value()); 39 | } 40 | 41 | static final boolean equals(final Object obj, final Object key, final Object value) { 42 | return obj instanceof Entry entry && key.equals(entry.getKey()) && value.equals(entry.getValue()); 43 | } 44 | 45 | @Override 46 | public final String toString() { 47 | return toString(key(), value()); 48 | } 49 | 50 | static final String toString(final Object key, final Object value) { 51 | return key + "=" + value; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/AbstractEntrySet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2017 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static java.util.Objects.requireNonNull; 19 | 20 | import java.util.AbstractSet; 21 | import java.util.Map.Entry; 22 | import java.util.Spliterator; 23 | import java.util.Spliterators; 24 | import org.eclipse.jdt.annotation.NonNull; 25 | 26 | /** 27 | * Abstract base class for implementing {@link TrieMap} entry sets. 28 | * 29 | * @author Robert Varga 30 | * 31 | * @param the type of entry keys 32 | * @param the type of entry values 33 | */ 34 | abstract sealed class AbstractEntrySet> extends AbstractSet> 35 | permits ImmutableEntrySet, MutableEntrySet { 36 | final @NonNull M map; 37 | 38 | AbstractEntrySet(final M map) { 39 | this.map = requireNonNull(map); 40 | } 41 | 42 | @Override 43 | @SuppressWarnings("checkstyle:parameterName") 44 | public final boolean contains(final Object o) { 45 | if (!(o instanceof Entry entry)) { 46 | return false; 47 | } 48 | 49 | final var key = entry.getKey(); 50 | if (key == null) { 51 | return false; 52 | } 53 | final var value = entry.getValue(); 54 | return value != null && value.equals(map.get(key)); 55 | } 56 | 57 | @Override 58 | public final int size() { 59 | return map.size(); 60 | } 61 | 62 | @Override 63 | public final Spliterator> spliterator() { 64 | // TODO: this is backed by an Iterator, we should be able to do better 65 | return Spliterators.spliterator(map.iterator(), Long.MAX_VALUE, characteristics()); 66 | } 67 | 68 | abstract int characteristics(); 69 | } 70 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/AbstractIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2017 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static java.util.Objects.requireNonNull; 19 | import static tech.pantheon.triemap.Constants.MAX_DEPTH; 20 | 21 | import java.util.Iterator; 22 | import java.util.Map.Entry; 23 | import java.util.NoSuchElementException; 24 | 25 | /** 26 | * Abstract base class for iterators supporting {@link AbstractEntrySet} subclasses. 27 | * 28 | * @author Robert Varga 29 | * 30 | * @param the type of entry keys 31 | * @param the type of entry values 32 | */ 33 | abstract sealed class AbstractIterator implements Iterator> 34 | permits ImmutableIterator, MutableIterator { 35 | @SuppressWarnings("unchecked") 36 | private final Branch[][] nodeStack = new Branch[MAX_DEPTH][]; 37 | private final int[] positionStack = new int[MAX_DEPTH]; 38 | private final ImmutableTrieMap map; 39 | 40 | private LNodeEntries lnode; 41 | private EntryNode current; 42 | private int depth = -1; 43 | 44 | AbstractIterator(final ImmutableTrieMap map) { 45 | this.map = requireNonNull(map); 46 | readin(map.readRoot()); 47 | } 48 | 49 | @Override 50 | public final boolean hasNext() { 51 | return current != null || lnode != null; 52 | } 53 | 54 | @Override 55 | public final Entry next() { 56 | final DefaultEntry entry; 57 | 58 | // Check LNode iterator first 59 | if (lnode != null) { 60 | entry = lnode; 61 | lnode = lnode.next(); 62 | if (lnode == null) { 63 | advance(); 64 | } 65 | } else { 66 | entry = current; 67 | advance(); 68 | } 69 | 70 | if (entry == null) { 71 | throw new NoSuchElementException(); 72 | } 73 | 74 | return wrapEntry(entry); 75 | } 76 | 77 | /** 78 | * Wrap entry so it can be presented to the user. 79 | * 80 | * @param entry An immutable entry, guaranteed to be non-null 81 | * @return Wrapped entry, may not be null 82 | */ 83 | abstract Entry wrapEntry(DefaultEntry entry); 84 | 85 | /** 86 | * Read the contents of an INode's main node. 87 | * 88 | * @param in INode to be read. 89 | */ 90 | private void readin(final INode in) { 91 | final var m = in.gcasRead(map); 92 | if (m instanceof CNode cn) { 93 | // Enter the next level 94 | depth++; 95 | nodeStack[depth] = cn.array; 96 | positionStack[depth] = -1; 97 | advance(); 98 | } else if (m instanceof TNode tn) { 99 | current = tn; 100 | } else if (m instanceof LNode ln) { 101 | lnode = ln.entries; 102 | } else if (m == null) { 103 | current = null; 104 | } 105 | } 106 | 107 | private void advance() { 108 | if (depth >= 0) { 109 | int npos = positionStack[depth] + 1; 110 | if (npos < nodeStack[depth].length) { 111 | positionStack [depth] = npos; 112 | var elem = nodeStack[depth][npos]; 113 | if (elem instanceof SNode) { 114 | current = (SNode) elem; 115 | } else if (elem instanceof INode) { 116 | readin((INode) elem); 117 | } 118 | } else { 119 | depth -= 1; 120 | advance(); 121 | } 122 | } else { 123 | current = null; 124 | } 125 | } 126 | } -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/AbstractKeySet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static java.util.Objects.requireNonNull; 19 | 20 | import java.util.AbstractSet; 21 | import java.util.Collection; 22 | import java.util.Spliterator; 23 | import java.util.Spliterators; 24 | import org.eclipse.jdt.annotation.NonNull; 25 | 26 | /** 27 | * Abstract base class for key set views of a TrieMap. 28 | * 29 | * @author Robert Varga 30 | * 31 | * @param the type of keys 32 | */ 33 | abstract sealed class AbstractKeySet> extends AbstractSet 34 | permits ImmutableKeySet, MutableKeySet { 35 | final @NonNull M map; 36 | 37 | AbstractKeySet(final M map) { 38 | this.map = requireNonNull(map); 39 | } 40 | 41 | @Override 42 | @SuppressWarnings("checkstyle:parameterName") 43 | public final boolean addAll(final Collection c) { 44 | throw new UnsupportedOperationException(); 45 | } 46 | 47 | @Override 48 | @SuppressWarnings("checkstyle:parameterName") 49 | public final boolean contains(final Object o) { 50 | return map.containsKey(o); 51 | } 52 | 53 | @Override 54 | public final int size() { 55 | return map.size(); 56 | } 57 | 58 | @Override 59 | public final Spliterator spliterator() { 60 | // TODO: this is backed by an Iterator, we should be able to do better 61 | return Spliterators.spliterator(immutableIterator(), Long.MAX_VALUE, spliteratorCharacteristics()); 62 | } 63 | 64 | @Override 65 | public abstract KeySetIterator iterator(); 66 | 67 | final @NonNull KeySetIterator immutableIterator() { 68 | return new KeySetIterator<>(map.immutableIterator()); 69 | } 70 | 71 | abstract int spliteratorCharacteristics(); 72 | } 73 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/Branch.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2025 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | /** 19 | * A Branch: either an {@link INode} or an {@link SNode}. 20 | */ 21 | sealed interface Branch permits INode, SNode { 22 | /** 23 | * Return the number of entries for the purposes of {@link CNode#size(ImmutableTrieMap)}. 24 | * 25 | * @param ct TrieMap reference 26 | * @return The actual number of entries 27 | */ 28 | int elementSize(ImmutableTrieMap ct); 29 | } -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2017 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | /** 19 | * Various implementation-specific constants shared across classes. Normally we would be deriving both 20 | * {@link #LEVEL_BITS} and {@link #MAX_DEPTH} from {@link #HASH_BITS} and size of {@link CNode#bitmap}, but that would 21 | * mean they would be runtime constants. We really want them to be compile-time constants. Hence we seed them manually 22 | * and assert the constants are correct. 23 | * 24 | * @author Robert Varga 25 | */ 26 | final class Constants { 27 | /** 28 | * Size of the hash function, in bits. This corresponds to {@link Object#hashCode()}'s size. 29 | */ 30 | static final int HASH_BITS = Integer.SIZE; 31 | 32 | /** 33 | * Number of hash bits consumed in each CNode level. This corresponds to log2(HASH_BITS). 34 | */ 35 | static final int LEVEL_BITS = 5; 36 | 37 | /** 38 | * Maximum depth of a TrieMap. Maximum number of CNode levels. This corresponds to 39 | * {@code Math.ceil(HASH_BITS / LEVEL_BITS)}. 40 | */ 41 | static final int MAX_DEPTH = 7; 42 | 43 | private Constants() { 44 | // Hidden on purpose 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/DefaultEntry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2017 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import java.util.Map.Entry; 19 | import org.eclipse.jdt.annotation.NonNull; 20 | 21 | /** 22 | * Our {@link Entry} implementations are immutable by default. 23 | * 24 | * @author Robert Varga 25 | * 26 | * @param the type of key 27 | * @param the type of value 28 | */ 29 | sealed interface DefaultEntry extends Entry permits AbstractEntry, EntryNode { 30 | 31 | @NonNull K key(); 32 | 33 | @Override 34 | @Deprecated 35 | default K getKey() { 36 | return key(); 37 | } 38 | 39 | @NonNull V value(); 40 | 41 | @Override 42 | @Deprecated 43 | default V getValue() { 44 | return value(); 45 | } 46 | 47 | @Override 48 | default V setValue(final V value) { 49 | throw new UnsupportedOperationException(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/EntryNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2025 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | /** 19 | * Common glue between {@link DefaultEntry} and {@link SNode}/{@link TNode}. This exists primarily for 20 | * {@link AbstractIterator}'s sake. 21 | */ 22 | sealed interface EntryNode extends DefaultEntry permits SNode, TNode { 23 | // Nothing else 24 | } 25 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/Equivalence.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import java.io.Serializable; 19 | import org.eclipse.jdt.annotation.NonNullByDefault; 20 | 21 | /** 22 | * Internal equivalence class, similar to com.google.common.base.Equivalence, but explicitly not handling 23 | * nulls. We use equivalence only for keys, which are guaranteed to be non-null. 24 | * 25 | * @author Robert Varga 26 | */ 27 | @NonNullByDefault 28 | abstract class Equivalence implements Serializable { 29 | @java.io.Serial 30 | private static final long serialVersionUID = 1L; 31 | 32 | static final class Equals extends Equivalence { 33 | @java.io.Serial 34 | private static final long serialVersionUID = 1L; 35 | 36 | static final Equals INSTANCE = new Equals(); 37 | 38 | @Override 39 | Equivalence resolve() { 40 | return INSTANCE; 41 | } 42 | } 43 | 44 | @java.io.Serial 45 | final Object readResolve() { 46 | return resolve(); 47 | } 48 | 49 | abstract Equivalence resolve(); 50 | } 51 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/Gen.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | final class Gen { 19 | // Just an identity object 20 | } 21 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/ImmutableEntrySet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2017 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static tech.pantheon.triemap.ImmutableTrieMap.unsupported; 19 | 20 | import java.util.Collection; 21 | import java.util.Map.Entry; 22 | import java.util.Spliterator; 23 | import java.util.function.Predicate; 24 | 25 | /** 26 | * {@link AbstractEntrySet} implementation guarding against attempts to mutate the underlying map. 27 | * 28 | * @author Robert Varga 29 | * 30 | * @param the type of entry keys 31 | * @param the type of entry values 32 | */ 33 | final class ImmutableEntrySet extends AbstractEntrySet> { 34 | ImmutableEntrySet(final ImmutableTrieMap map) { 35 | super(map); 36 | } 37 | 38 | @Override 39 | public void clear() { 40 | throw unsupported(); 41 | } 42 | 43 | @Override 44 | public ImmutableIterator iterator() { 45 | return map.immutableIterator(); 46 | } 47 | 48 | @Override 49 | @SuppressWarnings("checkstyle:parameterName") 50 | public boolean remove(final Object o) { 51 | throw unsupported(); 52 | } 53 | 54 | @Override 55 | @SuppressWarnings("checkstyle:parameterName") 56 | public boolean removeAll(final Collection c) { 57 | throw unsupported(); 58 | } 59 | 60 | @Override 61 | @SuppressWarnings("checkstyle:parameterName") 62 | public boolean retainAll(final Collection c) { 63 | throw unsupported(); 64 | } 65 | 66 | @Override 67 | public boolean removeIf(final Predicate> filter) { 68 | throw unsupported(); 69 | } 70 | 71 | @Override 72 | int characteristics() { 73 | return Spliterator.DISTINCT | Spliterator.IMMUTABLE | Spliterator.NONNULL; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/ImmutableIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2017 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | /** 19 | * Specialized immutable iterator for use with {@link ImmutableEntrySet}. 20 | * 21 | * @author Robert Varga 22 | * 23 | * @param the type of entry keys 24 | * @param the type of entry values 25 | */ 26 | final class ImmutableIterator extends AbstractIterator { 27 | ImmutableIterator(final ImmutableTrieMap map) { 28 | super(map); 29 | } 30 | 31 | @Override 32 | DefaultEntry wrapEntry(final DefaultEntry entry) { 33 | return entry; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/ImmutableKeySet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static tech.pantheon.triemap.ImmutableTrieMap.unsupported; 19 | 20 | import java.util.Collection; 21 | import java.util.Spliterator; 22 | import java.util.function.Predicate; 23 | 24 | /** 25 | * An immutable view of a TrieMap's key set. 26 | * 27 | * @author Robert Varga 28 | * 29 | * @param the type of keys 30 | */ 31 | final class ImmutableKeySet extends AbstractKeySet> { 32 | ImmutableKeySet(final ImmutableTrieMap map) { 33 | super(map); 34 | } 35 | 36 | @Override 37 | public KeySetIterator iterator() { 38 | return immutableIterator(); 39 | } 40 | 41 | @Override 42 | public void clear() { 43 | throw unsupported(); 44 | } 45 | 46 | @Override 47 | @SuppressWarnings("checkstyle:parameterName") 48 | public boolean remove(final Object o) { 49 | throw unsupported(); 50 | } 51 | 52 | @Override 53 | @SuppressWarnings("checkstyle:parameterName") 54 | public boolean retainAll(final Collection c) { 55 | throw unsupported(); 56 | } 57 | 58 | @Override 59 | @SuppressWarnings("checkstyle:parameterName") 60 | public boolean removeAll(final Collection c) { 61 | throw unsupported(); 62 | } 63 | 64 | @Override 65 | public boolean removeIf(final Predicate filter) { 66 | throw unsupported(); 67 | } 68 | 69 | @Override 70 | int spliteratorCharacteristics() { 71 | return Spliterator.DISTINCT | Spliterator.IMMUTABLE | Spliterator.NONNULL; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/ImmutableTrieMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static java.util.Objects.requireNonNull; 19 | 20 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 21 | import java.util.Map; 22 | import java.util.function.BiFunction; 23 | import java.util.function.Function; 24 | 25 | /** 26 | * An immutable TrieMap. 27 | * 28 | * @author Robert Varga 29 | * 30 | * @param the type of keys maintained by this map 31 | * @param the type of mapped values 32 | */ 33 | public final class ImmutableTrieMap extends TrieMap { 34 | @java.io.Serial 35 | private static final long serialVersionUID = 1L; 36 | 37 | @SuppressFBWarnings(value = "SE_TRANSIENT_FIELD_NOT_RESTORED", justification = "Handled through writeReplace") 38 | private final transient INode root; 39 | 40 | ImmutableTrieMap(final INode root) { 41 | this.root = requireNonNull(root); 42 | } 43 | 44 | @Override 45 | public void clear() { 46 | throw unsupported(); 47 | } 48 | 49 | @Override 50 | public V compute(final K key, final BiFunction remappingFunction) { 51 | throw unsupported(); 52 | } 53 | 54 | @Override 55 | public V computeIfAbsent(final K key, final Function mappingFunction) { 56 | throw unsupported(); 57 | } 58 | 59 | @Override 60 | public V computeIfPresent(final K key, final BiFunction remappingFunction) { 61 | throw unsupported(); 62 | } 63 | 64 | @Override 65 | public V merge(final K key, final V value, final BiFunction remappingFunction) { 66 | throw unsupported(); 67 | } 68 | 69 | @Override 70 | public V put(final K key, final V value) { 71 | throw unsupported(); 72 | } 73 | 74 | @Override 75 | @SuppressWarnings("checkstyle:parameterName") 76 | public void putAll(final Map m) { 77 | throw unsupported(); 78 | } 79 | 80 | @Override 81 | public V putIfAbsent(final K key, final V value) { 82 | throw unsupported(); 83 | } 84 | 85 | @Override 86 | public V remove(final Object key) { 87 | throw unsupported(); 88 | } 89 | 90 | @Override 91 | public boolean remove(final Object key, final Object value) { 92 | throw unsupported(); 93 | } 94 | 95 | @Override 96 | public boolean replace(final K key, final V oldValue, final V newValue) { 97 | throw unsupported(); 98 | } 99 | 100 | @Override 101 | public V replace(final K key, final V value) { 102 | throw unsupported(); 103 | } 104 | 105 | @Override 106 | public int size() { 107 | return root.elementSize(this); 108 | } 109 | 110 | @Override 111 | public MutableTrieMap mutableSnapshot() { 112 | return new MutableTrieMap<>(root.copyToGen(this, new Gen())); 113 | } 114 | 115 | @Override 116 | public ImmutableTrieMap immutableSnapshot() { 117 | return this; 118 | } 119 | 120 | @Override 121 | ImmutableEntrySet createEntrySet() { 122 | return new ImmutableEntrySet<>(this); 123 | } 124 | 125 | @Override 126 | ImmutableKeySet createKeySet() { 127 | return new ImmutableKeySet<>(this); 128 | } 129 | 130 | @Override 131 | boolean isReadOnly() { 132 | return true; 133 | } 134 | 135 | @Override 136 | ImmutableIterator iterator() { 137 | return immutableIterator(); 138 | } 139 | 140 | @Override 141 | INode rdcssReadRoot(final boolean abort) { 142 | return root; 143 | } 144 | 145 | static UnsupportedOperationException unsupported() { 146 | return new UnsupportedOperationException("Attempted to modify a read-only view"); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/ImmutableTrieSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2019 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import java.util.Collection; 19 | 20 | /** 21 | * An immutable TrieSet. Does not allow modifications. 22 | * 23 | * @param the type of elements maintained by this set 24 | * @author Robert Varga 25 | */ 26 | public final class ImmutableTrieSet extends TrieSet { 27 | @java.io.Serial 28 | private static final long serialVersionUID = 1L; 29 | 30 | ImmutableTrieSet(final ImmutableTrieMap map) { 31 | super(map); 32 | } 33 | 34 | @Override 35 | public ImmutableTrieSet immutableSnapshot() { 36 | return this; 37 | } 38 | 39 | @Override 40 | @SuppressWarnings("checkstyle:parameterName") 41 | public boolean addAll(final Collection c) { 42 | throw new UnsupportedOperationException(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/KeySetIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package tech.pantheon.triemap; 9 | 10 | import static java.util.Objects.requireNonNull; 11 | 12 | import java.util.Iterator; 13 | 14 | /** 15 | * Iterator given out by {@link AbstractKeySet} implementations. 16 | */ 17 | final class KeySetIterator implements Iterator { 18 | private final AbstractIterator delegate; 19 | 20 | KeySetIterator(final AbstractIterator delegate) { 21 | this.delegate = requireNonNull(delegate); 22 | } 23 | 24 | @Override 25 | public boolean hasNext() { 26 | return delegate.hasNext(); 27 | } 28 | 29 | @Override 30 | public K next() { 31 | return delegate.next().getKey(); 32 | } 33 | 34 | @Override 35 | public void remove() { 36 | delegate.remove(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/LNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | final class LNode extends MainNode { 19 | // Internally-linked single list of of entries 20 | final LNodeEntries entries; 21 | final int size; 22 | 23 | LNode(final LNode prev, final LNodeEntries entries, final int size) { 24 | super(prev); 25 | this.entries = entries; 26 | this.size = size; 27 | } 28 | 29 | LNode(final SNode first, final SNode second) { 30 | entries = LNodeEntries.of(first.key(), first.value(), second.key(), second.value()); 31 | size = 2; 32 | } 33 | 34 | @Override 35 | int trySize() { 36 | return size; 37 | } 38 | 39 | @Override 40 | int size(final ImmutableTrieMap ct) { 41 | return size; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/LNodeEntries.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static tech.pantheon.triemap.PresencePredicate.ABSENT; 19 | import static tech.pantheon.triemap.PresencePredicate.PRESENT; 20 | import static tech.pantheon.triemap.Result.RESTART; 21 | 22 | import java.util.function.Function; 23 | import org.eclipse.jdt.annotation.NonNull; 24 | import org.eclipse.jdt.annotation.Nullable; 25 | 26 | /** 27 | * Similar to Scala's ListMap, this is a single-linked list of set of map entries. Aside from the java.util.Set 28 | * contract, this class fulfills the requirements for an immutable map entryset. 29 | * 30 | * @author Robert Varga 31 | * 32 | * @param the type of keys 33 | * @param the type of values 34 | */ 35 | abstract sealed class LNodeEntries extends LNodeEntry { 36 | // Visible for testing 37 | static final class Single extends LNodeEntries { 38 | Single(final @NonNull K key, final @NonNull V value) { 39 | super(key, value); 40 | } 41 | 42 | @Override 43 | LNodeEntries next() { 44 | return null; 45 | } 46 | } 47 | 48 | private static final class Multiple extends LNodeEntries { 49 | // Modified during remove only, otherwise final 50 | LNodeEntries next; 51 | 52 | // Used in remove() only 53 | Multiple(final LNodeEntries entry) { 54 | this(entry.key(), entry.value(), null); 55 | } 56 | 57 | Multiple(final @NonNull K key, final @NonNull V value, final LNodeEntries next) { 58 | super(key, value); 59 | this.next = next; 60 | } 61 | 62 | @Override 63 | LNodeEntries next() { 64 | return next; 65 | } 66 | } 67 | 68 | LNodeEntries(final @NonNull K key, final @NonNull V value) { 69 | super(key, value); 70 | } 71 | 72 | static LNodeEntries of(final @NonNull K k1, final @NonNull V v1, 73 | final @NonNull K k2, final @NonNull V v2) { 74 | return new Multiple<>(k1, v1, new Single<>(k2, v2)); 75 | } 76 | 77 | /** 78 | * Return the remainder of this list. Useful for implementing Iterator-like contract. Null indicates there are no 79 | * more entries. 80 | * 81 | * @return Remainder of this list, or null if nothing remains 82 | */ 83 | abstract LNodeEntries next(); 84 | 85 | final @Nullable V lookup(final @NonNull K key) { 86 | final var entry = findEntry(key); 87 | return entry != null ? entry.value() : null; 88 | } 89 | 90 | @Nullable Object computeIfAbsent(final MutableTrieMap ct, final INode in, final LNode ln, 91 | final @NonNull K key, final @NonNull Function fn) { 92 | final var entry = findEntry(key); 93 | if (entry != null) { 94 | return entry.value(); 95 | } 96 | 97 | final var val = fn.apply(key); 98 | return val == null || in.gcasWrite(ct, toInserted(ln, key, val)) ? val : RESTART; 99 | } 100 | 101 | final boolean insert(final MutableTrieMap ct, final INode in, final LNode ln, 102 | final @NonNull K key, final @NonNull V val) { 103 | final var entry = findEntry(key); 104 | return in.gcasWrite(ct, entry == null ? toInserted(ln, key, val) : toReplaced(ln, entry, val)); 105 | } 106 | 107 | @Nullable Object insertIf(final MutableTrieMap ct, final INode in, final LNode ln, 108 | final @NonNull K key, final @NonNull V val, final Object cond) { 109 | final var entry = findEntry(key); 110 | if (entry == null) { 111 | return cond != null && cond != ABSENT || in.gcasWrite(ct, toInserted(ln, key, val)) ? null : RESTART; 112 | } 113 | if (cond == ABSENT) { 114 | return entry.value(); 115 | } else if (cond == null || cond == PRESENT || cond.equals(entry.value())) { 116 | return in.gcasWrite(ct, toReplaced(ln, entry, val)) ? entry.value() : RESTART; 117 | } 118 | return null; 119 | } 120 | 121 | @Nullable Object remove(final MutableTrieMap ct, final INode in, final LNode ln, 122 | final @NonNull K key, final @Nullable Object cond, final int hc) { 123 | final var entry = findEntry(key); 124 | if (entry == null) { 125 | // Key was not found, hence no modification is needed 126 | return null; 127 | } 128 | if (cond != null && !cond.equals(entry.value())) { 129 | // Value does not match 130 | return null; 131 | } 132 | 133 | // While remove() can return null, that case will never happen here, as we are starting off with two entries 134 | // so we cannot observe a null return here. 135 | final var map = VerifyException.throwIfNull(removeEntry(entry)); 136 | 137 | // If the returned LNode would have only one element, we turn it intoa TNode, so it can be turned into SNode on 138 | // next lookup 139 | final var size = ln.size; 140 | final var next = size == 2 ? new TNode<>(ln, map.key(), map.value(), hc) : new LNode<>(ln, map, size - 1); 141 | 142 | return in.gcasWrite(ct, next) ? entry.value() : RESTART; 143 | } 144 | 145 | private LNode toInserted(final LNode ln, final @NonNull K key, final @NonNull V val) { 146 | return new LNode<>(ln, insertEntry(key, val), ln.size + 1); 147 | } 148 | 149 | private LNode toReplaced(final LNode ln, final LNodeEntry entry, final @NonNull V val) { 150 | return new LNode<>(ln, replace(entry, val), ln.size); 151 | } 152 | 153 | // Visible for testing 154 | final LNodeEntries replace(final LNodeEntry entry, final @NonNull V value) { 155 | final var removed = removeEntry(entry); 156 | return removed == null ? new Single<>(entry.key(), value) : new Multiple<>(entry.key(), value, removed); 157 | } 158 | 159 | // Visible for testing 160 | final @Nullable LNodeEntry findEntry(final @NonNull K key) { 161 | // We do not perform recursion on purpose here, so we do not run out of stack if the key hashing fails. 162 | var entry = this; 163 | do { 164 | if (key.equals(entry.key())) { 165 | return entry; 166 | } 167 | 168 | entry = entry.next(); 169 | } while (entry != null); 170 | 171 | return null; 172 | } 173 | 174 | // Visible for testing 175 | final LNodeEntries insertEntry(final @NonNull K key, final @NonNull V value) { 176 | return new Multiple<>(key, value, this); 177 | } 178 | 179 | // Visible for testing 180 | final LNodeEntries removeEntry(final LNodeEntry entry) { 181 | if (entry == this) { 182 | return next(); 183 | } 184 | 185 | // This will result in a list with a long tail, i.e last entry storing explicit null. Overhead is amortized 186 | // against the number of entries. We do not retain chains shorter than two, so the worst-case overhead is 187 | // half-a-reference for an entry. 188 | final var ret = new Multiple<>(this); 189 | 190 | var last = ret; 191 | var cur = next(); 192 | while (cur != null) { 193 | // We cannot use equals() here, as it is wired to key equality and we must never compare entries based on 194 | // that property. This method is intended to remove a known reference, so identity is what we want. 195 | if (entry == cur) { 196 | last.next = cur.next(); 197 | return ret; 198 | } 199 | 200 | final var tmp = new Multiple<>(cur); 201 | last.next = tmp; 202 | last = tmp; 203 | cur = cur.next(); 204 | } 205 | 206 | throw new VerifyException("Failed to find entry " + entry); 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/LNodeEntry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import org.eclipse.jdt.annotation.NonNull; 19 | 20 | /** 21 | * A single entry in {@link LNodeEntries}, implements {@link DefaultEntry} in order to prevent instantiation of objects 22 | * for iteration. 23 | * 24 | * @author Robert Varga 25 | * 26 | * @param the type of key 27 | * @param the type of value 28 | */ 29 | abstract sealed class LNodeEntry extends AbstractEntry permits LNodeEntries { 30 | private final @NonNull K key; 31 | private final @NonNull V value; 32 | 33 | LNodeEntry(final @NonNull K key, final @NonNull V value) { 34 | this.key = key; 35 | this.value = value; 36 | } 37 | 38 | @Override 39 | public final K key() { 40 | return key; 41 | } 42 | 43 | @Override 44 | public final V value() { 45 | return value; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/MainNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | /** 19 | * A {@link MainNode}: one of {@link CNode}, {@link LNode} or {@link TNode}. 20 | */ 21 | abstract sealed class MainNode extends INode.TryGcas permits CNode, LNode, TNode { 22 | 23 | static final int NO_SIZE = -1; 24 | 25 | /** 26 | * Constructor for {@link CorLNode}, e.g. {@link CNode} and {@link LNode}, instances which are considered already 27 | * committed. 28 | */ 29 | MainNode() { 30 | super(); 31 | } 32 | 33 | /** 34 | * Constructor for instances which are succeeding a previous {@link CNode} node. 35 | */ 36 | MainNode(final CNode prev) { 37 | super(prev); 38 | } 39 | 40 | /** 41 | * Constructor for instances which are succeeding a previous {@link LNode} node. 42 | */ 43 | MainNode(final LNode prev) { 44 | super(prev); 45 | } 46 | 47 | /** 48 | * Return the number of entries in this node, or {@link #NO_SIZE} if it is not known. 49 | */ 50 | abstract int trySize(); 51 | 52 | /** 53 | * Return the number of entries in this node, traversing it if need be. 54 | * 55 | * @param ct TrieMap reference 56 | * @return The actual number of entries. 57 | */ 58 | abstract int size(ImmutableTrieMap ct); 59 | } 60 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/MutableEntrySet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2017 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import java.util.Map.Entry; 19 | import java.util.Spliterator; 20 | 21 | /** 22 | * Support for EntrySet operations required by the Map interface. 23 | * 24 | * @param the type of keys 25 | * @param the type of values 26 | */ 27 | final class MutableEntrySet extends AbstractEntrySet> { 28 | MutableEntrySet(final MutableTrieMap map) { 29 | super(map); 30 | } 31 | 32 | @Override 33 | public void clear() { 34 | map.clear(); 35 | } 36 | 37 | @Override 38 | public MutableIterator iterator() { 39 | return map.iterator(); 40 | } 41 | 42 | @Override 43 | @SuppressWarnings("checkstyle:parameterName") 44 | public boolean remove(final Object o) { 45 | if (!(o instanceof Entry entry)) { 46 | return false; 47 | } 48 | 49 | final var key = entry.getKey(); 50 | if (key == null) { 51 | return false; 52 | } 53 | final var value = entry.getValue(); 54 | if (value == null) { 55 | return false; 56 | } 57 | 58 | return map.remove(key, value); 59 | } 60 | 61 | @Override 62 | int characteristics() { 63 | return Spliterator.DISTINCT | Spliterator.CONCURRENT | Spliterator.NONNULL; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/MutableIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2017 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | /** 19 | * Specialized immutable iterator for use with {@link ImmutableEntrySet}. 20 | * 21 | * @author Robert Varga 22 | * 23 | * @param the type of entry keys 24 | * @param the type of entry values 25 | */ 26 | final class MutableIterator extends AbstractIterator { 27 | private final MutableTrieMap mutable; 28 | 29 | private MutableEntry lastReturned; 30 | 31 | MutableIterator(final MutableTrieMap map) { 32 | super(map.immutableSnapshot()); 33 | mutable = map; 34 | } 35 | 36 | @Override 37 | public void remove() { 38 | if (lastReturned == null) { 39 | throw new IllegalStateException(); 40 | } 41 | mutable.remove(lastReturned.key()); 42 | lastReturned = null; 43 | } 44 | 45 | @Override 46 | MutableEntry wrapEntry(final DefaultEntry entry) { 47 | final var ret = new MutableEntry<>(mutable, entry); 48 | lastReturned = ret; 49 | return ret; 50 | } 51 | 52 | /** 53 | * A mutable view of an entry in the map. Since the backing map is concurrent, its {@link #getValue()} and 54 | * {@link #setValue(Object)} methods cannot guarantee consistency with the base map and may produce surprising 55 | * results when the map is concurrently modified, either directly or via another entry/iterator. 56 | * 57 | *

The behavior is similar to what Java 8's ConcurrentHashMap does, which is probably the most consistent 58 | * handling of this case without requiring expensive and revalidation. 59 | */ 60 | static final class MutableEntry extends AbstractEntry { 61 | private final MutableTrieMap map; 62 | private final DefaultEntry delegate; 63 | 64 | @SuppressWarnings("null") 65 | private V newValue = null; 66 | 67 | private MutableEntry(final MutableTrieMap map, final DefaultEntry delegate) { 68 | this.map = map; 69 | this.delegate = delegate; 70 | } 71 | 72 | @Override 73 | public K key() { 74 | return delegate.key(); 75 | } 76 | 77 | /** 78 | * {@inheritDoc} 79 | * 80 | * @implSpec 81 | * This implementation returns the most uptodate value we have observed via this entry. It does not reflect 82 | * concurrent modifications, nor does it throw {@link IllegalStateException} if the entry is removed. 83 | */ 84 | @Override 85 | public V value() { 86 | final var val = newValue; 87 | return val != null ? val : delegate.value(); 88 | } 89 | 90 | /** 91 | * {@inheritDoc} 92 | * 93 | * @implSpec 94 | * This implementation returns the most uptodate value we have observed via this entry. It does not reflect 95 | * concurrent modifications, nor does it throw {@link IllegalStateException} if the entry is removed. 96 | */ 97 | @Override 98 | public V setValue(final V value) { 99 | final var ret = value(); 100 | map.put(key(), value); 101 | newValue = value; 102 | return ret; 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/MutableKeySet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import java.util.Spliterator; 19 | 20 | /** 21 | * A mutable view of a TrieMap's key set. 22 | * 23 | * @author Robert Varga 24 | * 25 | * @param the type of keys 26 | */ 27 | final class MutableKeySet extends AbstractKeySet> { 28 | MutableKeySet(final MutableTrieMap map) { 29 | super(map); 30 | } 31 | 32 | @Override 33 | public KeySetIterator iterator() { 34 | return new KeySetIterator<>(map.iterator()); 35 | } 36 | 37 | @Override 38 | public void clear() { 39 | map.clear(); 40 | } 41 | 42 | @Override 43 | @SuppressWarnings("checkstyle:parameterName") 44 | public boolean remove(final Object o) { 45 | return map.remove(o) != null; 46 | } 47 | 48 | @Override 49 | int spliteratorCharacteristics() { 50 | return Spliterator.DISTINCT | Spliterator.CONCURRENT | Spliterator.NONNULL; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/MutableTrieSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2019 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import java.util.Collection; 19 | 20 | /** 21 | * A mutable TrieSet. 22 | * 23 | * @param the type of elements maintained by this set 24 | * @author Robert Varga 25 | */ 26 | public final class MutableTrieSet extends TrieSet { 27 | @java.io.Serial 28 | private static final long serialVersionUID = 0L; 29 | 30 | MutableTrieSet(final MutableTrieMap map) { 31 | super(map); 32 | } 33 | 34 | @Override 35 | public ImmutableTrieSet immutableSnapshot() { 36 | return new ImmutableTrieSet<>(map().immutableSnapshot()); 37 | } 38 | 39 | @Override 40 | @SuppressWarnings("checkstyle:parameterName") 41 | public boolean addAll(final Collection c) { 42 | boolean ret = false; 43 | for (var e : c) { 44 | ret |= add(e); 45 | } 46 | return ret; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/PresencePredicate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2017 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | /** 19 | * Presence predicate. These are specialized objects passed down from callers to indicate an assertion about 20 | * whether a mapping is required. 21 | * 22 | * @author Robert Varga 23 | */ 24 | enum PresencePredicate { 25 | ABSENT, 26 | PRESENT; 27 | } 28 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/Result.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2020 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | /** 19 | + * Virtual result for lookup/insert methods indicating that the lookup needs to be restarted. This is a faster version 20 | + * of throwing a checked exception to control restart. 21 | */ 22 | enum Result { 23 | RESTART; 24 | } 25 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/SNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import org.eclipse.jdt.annotation.NonNull; 19 | import org.eclipse.jdt.annotation.Nullable; 20 | 21 | record SNode(@NonNull K key, @NonNull V value, int hc) implements Branch, EntryNode { 22 | SNode(final TNode tn) { 23 | this(tn.key, tn.value, tn.hc); 24 | } 25 | 26 | @Nullable V lookup(final int otherHc, final K otherKey) { 27 | return matches(otherHc, otherKey) ? value : null; 28 | } 29 | 30 | boolean matches(final int otherHc, final Object otherKey) { 31 | return hc == otherHc && otherKey.equals(key); 32 | } 33 | 34 | @Override 35 | public int elementSize(final ImmutableTrieMap ct) { 36 | return 1; 37 | } 38 | 39 | @Override 40 | public int hashCode() { 41 | return AbstractEntry.hashCode(key, value); 42 | } 43 | 44 | @Override 45 | public boolean equals(final Object obj) { 46 | return AbstractEntry.equals(obj, key, value); 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return AbstractEntry.toString(key, value); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/SerializationProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2017 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static java.util.Objects.requireNonNull; 19 | 20 | import java.io.Externalizable; 21 | import java.io.IOException; 22 | import java.io.InvalidObjectException; 23 | import java.io.NotActiveException; 24 | import java.io.ObjectInput; 25 | import java.io.ObjectOutput; 26 | import java.io.ObjectStreamException; 27 | import java.io.StreamCorruptedException; 28 | 29 | /** 30 | * External serialization object for use with TrieMap objects. This hides the implementation details, such as object 31 | * hierarchy. It also makes handling read-only snapshots more elegant. 32 | * 33 | * @author Robert Varga 34 | */ 35 | final class SerializationProxy implements Externalizable { 36 | @java.io.Serial 37 | private static final long serialVersionUID = 1L; 38 | 39 | private transient TrieMap map; 40 | private transient boolean readOnly; 41 | 42 | @SuppressWarnings("checkstyle:redundantModifier") 43 | public SerializationProxy() { 44 | // For Externalizable 45 | } 46 | 47 | @SuppressWarnings({ "unchecked", "rawtypes" }) 48 | SerializationProxy(final ImmutableTrieMap map, final boolean readOnly) { 49 | this.map = (TrieMap) requireNonNull(map); 50 | this.readOnly = readOnly; 51 | } 52 | 53 | @Override 54 | public void writeExternal(final ObjectOutput out) throws IOException { 55 | out.writeObject(Equivalence.Equals.INSTANCE); 56 | out.writeInt(map.size()); 57 | for (var e : map.entrySet()) { 58 | out.writeObject(e.getKey()); 59 | out.writeObject(e.getValue()); 60 | } 61 | out.writeBoolean(readOnly); 62 | } 63 | 64 | @Override 65 | public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException { 66 | final var eqObj = in.readObject(); 67 | if (!(eqObj instanceof Equivalence)) { 68 | throw new InvalidObjectException("Expected Equivalence object instead of " + eqObj); 69 | } 70 | 71 | final var tmp = new MutableTrieMap<>(); 72 | final int size = in.readInt(); 73 | if (size < 0) { 74 | throw new StreamCorruptedException("Expected non-negative size instead of " + size); 75 | } 76 | 77 | for (int i = 0; i < size; ++i) { 78 | final var k = requireNonNull(in.readObject()); 79 | final var v = requireNonNull(in.readObject()); 80 | final var r = tmp.readRoot(); 81 | if (!r.insert(tmp, r.gen, TrieMap.computeHash(k), k, v, 0, null)) { 82 | throw new VerifyException("Concurrent modification during serialization"); 83 | } 84 | } 85 | 86 | map = in.readBoolean() ? tmp.immutableSnapshot() : tmp; 87 | } 88 | 89 | @java.io.Serial 90 | private Object readResolve() throws ObjectStreamException { 91 | if (map == null) { 92 | throw new NotActiveException(); 93 | } 94 | return map; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/TNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import org.eclipse.jdt.annotation.NonNull; 19 | 20 | final class TNode extends MainNode implements EntryNode { 21 | final @NonNull K key; 22 | final @NonNull V value; 23 | final int hc; 24 | 25 | // Visible for testing 26 | TNode(final CNode prev, final @NonNull K key, final @NonNull V value, final int hc) { 27 | super(prev); 28 | this.key = key; 29 | this.value = value; 30 | this.hc = hc; 31 | } 32 | 33 | TNode(final CNode prev, final SNode sn) { 34 | this(prev, sn.key(), sn.value(), sn.hc()); 35 | } 36 | 37 | TNode(final LNode prev, final @NonNull K key, final @NonNull V value, final int hc) { 38 | super(prev); 39 | this.key = key; 40 | this.value = value; 41 | this.hc = hc; 42 | } 43 | 44 | 45 | @Override 46 | public K key() { 47 | return key; 48 | } 49 | 50 | @Override 51 | public V value() { 52 | return value; 53 | } 54 | 55 | @Override 56 | int trySize() { 57 | return 1; 58 | } 59 | 60 | @Override 61 | int size(final ImmutableTrieMap ct) { 62 | return 1; 63 | } 64 | 65 | @Override 66 | public int hashCode() { 67 | return AbstractEntry.hashCode(key, value); 68 | } 69 | 70 | @Override 71 | public boolean equals(final Object obj) { 72 | return AbstractEntry.equals(obj, key, value); 73 | } 74 | 75 | @Override 76 | public String toString() { 77 | return AbstractEntry.toString(key, value); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/TrieMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static java.util.Objects.requireNonNull; 19 | import static tech.pantheon.triemap.Result.RESTART; 20 | 21 | import java.io.Serializable; 22 | import java.util.AbstractMap; 23 | import java.util.Set; 24 | import java.util.concurrent.ConcurrentMap; 25 | import java.util.function.Function; 26 | 27 | /** 28 | * This is a port of Scala's TrieMap class from the Scala Collections library. This implementation does not support 29 | * null keys nor null values. 30 | * 31 | * @author Aleksandar Prokopec (original Scala implementation) 32 | * @author Roman Levenstein (original Java 6 port) 33 | * @author Robert Varga 34 | * 35 | * @param the type of keys maintained by this map 36 | * @param the type of mapped values 37 | */ 38 | public abstract sealed class TrieMap extends AbstractMap implements ConcurrentMap, Serializable 39 | permits ImmutableTrieMap, MutableTrieMap { 40 | @java.io.Serial 41 | private static final long serialVersionUID = 1L; 42 | 43 | private transient AbstractEntrySet entrySet; 44 | // Note: AbstractMap.keySet is something we do not have access to. At some point we should just not subclass 45 | // AbstractMap and lower our memory footprint. 46 | private transient AbstractKeySet theKeySet; 47 | 48 | TrieMap() { 49 | // Hidden on purpose 50 | } 51 | 52 | /** 53 | * Create a new {@link MutableTrieMap}. 54 | * 55 | * @param key type 56 | * @param value type 57 | * @return A new {@link MutableTrieMap}. 58 | */ 59 | public static MutableTrieMap create() { 60 | return new MutableTrieMap<>(); 61 | } 62 | 63 | /** 64 | * Returns a snapshot of this TrieMap. This operation is lock-free and linearizable. Modification operations on 65 | * this Map and the returned one are isolated from each other. 66 | * 67 | *

The snapshot is lazily updated - the first time some branch in the snapshot or this TrieMap are accessed, 68 | * they are rewritten. This means that the work of rebuilding both the snapshot and this TrieMap is distributed 69 | * across all the threads doing updates or accesses subsequent to the snapshot creation. 70 | * 71 | * @return A read-write TrieMap containing the contents of this map. 72 | */ 73 | public abstract MutableTrieMap mutableSnapshot(); 74 | 75 | /** 76 | * Returns a read-only snapshot of this TrieMap. This operation is lock-free and linearizable. 77 | * 78 | *

The snapshot is lazily updated - the first time some branch of this TrieMap are accessed, it is rewritten. 79 | * The work of creating the snapshot is thus distributed across subsequent updates and accesses on this TrieMap 80 | * by all threads. Note that the snapshot itself is never rewritten unlike when calling {@link #mutableSnapshot()}, 81 | * but the obtained snapshot cannot be modified. 82 | * 83 | *

This method is used by other methods such as `size` and `iterator`. 84 | * 85 | * @return A read-only TrieMap containing the contents of this map. 86 | */ 87 | public abstract ImmutableTrieMap immutableSnapshot(); 88 | 89 | @Override 90 | public final boolean containsKey(final Object key) { 91 | return get(key) != null; 92 | } 93 | 94 | @Override 95 | public final boolean containsValue(final Object value) { 96 | return super.containsValue(requireNonNull(value)); 97 | } 98 | 99 | @Override 100 | public final Set> entrySet() { 101 | final AbstractEntrySet ret; 102 | return (ret = entrySet) != null ? ret : (entrySet = createEntrySet()); 103 | } 104 | 105 | @Override 106 | public final Set keySet() { 107 | final AbstractKeySet ret; 108 | return (ret = theKeySet) != null ? ret : (theKeySet = createKeySet()); 109 | } 110 | 111 | @Override 112 | @SuppressWarnings("unchecked") 113 | public final V get(final Object key) { 114 | final var k = (K) requireNonNull(key); 115 | final var hc = computeHash(k); 116 | 117 | // Keep looping as long as RESTART is being returned 118 | Object res; 119 | do { 120 | final var root = readRoot(); 121 | res = root.lookup(this, root.gen, hc, k, 0, null); 122 | } while (res == RESTART); 123 | 124 | return (V) res; 125 | } 126 | 127 | @Override 128 | public abstract void clear(); 129 | 130 | @Override 131 | public abstract V put(K key, V value); 132 | 133 | @Override 134 | public abstract V putIfAbsent(K key, V value); 135 | 136 | @Override 137 | public abstract V remove(Object key); 138 | 139 | @Override 140 | public abstract boolean remove(Object key, Object value); 141 | 142 | @Override 143 | public abstract boolean replace(K key, V oldValue, V newValue); 144 | 145 | @Override 146 | public abstract V replace(K key, V value); 147 | 148 | @Override 149 | public abstract V computeIfAbsent(K key, Function mappingFunction); 150 | 151 | @Override 152 | public abstract int size(); 153 | 154 | /* internal methods implemented by subclasses */ 155 | 156 | abstract AbstractEntrySet createEntrySet(); 157 | 158 | abstract AbstractKeySet createKeySet(); 159 | 160 | abstract boolean isReadOnly(); 161 | 162 | abstract INode rdcssReadRoot(boolean abort); 163 | 164 | /** 165 | * Return an iterator over a TrieMap. 166 | * 167 | *

If this is a read-only snapshot, it would return a read-only iterator. 168 | * 169 | *

If it is the original TrieMap or a non-readonly snapshot, it would return an iterator that would allow for 170 | * updates. 171 | * 172 | * @return An iterator. 173 | */ 174 | abstract AbstractIterator iterator(); 175 | 176 | /* internal methods provided for subclasses */ 177 | 178 | /** 179 | * Return an iterator over a TrieMap. This is a read-only iterator. 180 | * 181 | * @return A read-only iterator. 182 | */ 183 | final ImmutableIterator immutableIterator() { 184 | return new ImmutableIterator<>(immutableSnapshot()); 185 | } 186 | 187 | static final int computeHash(final Object key) { 188 | int hash = key.hashCode(); 189 | 190 | // This function ensures that hashCodes that differ only by 191 | // constant multiples at each bit position have a bounded 192 | // number of collisions (approximately 8 at default load factor). 193 | hash ^= hash >>> 20 ^ hash >>> 12; 194 | hash ^= hash >>> 7 ^ hash >>> 4; 195 | return hash; 196 | } 197 | 198 | /** 199 | * Replace this set with its {@link SerializationProxy}. 200 | * 201 | * @return {@link SerializationProxy} 202 | */ 203 | @java.io.Serial 204 | final Object writeReplace() { 205 | return new SerializationProxy(immutableSnapshot(), isReadOnly()); 206 | } 207 | 208 | /* package-protected utility methods */ 209 | 210 | final INode readRoot() { 211 | return rdcssReadRoot(false); 212 | } 213 | 214 | // FIXME: abort = false by default 215 | final INode readRoot(final boolean abort) { 216 | return rdcssReadRoot(abort); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/TrieSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2019 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static java.util.Objects.requireNonNull; 19 | 20 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 21 | import java.io.Externalizable; 22 | import java.io.IOException; 23 | import java.io.ObjectInput; 24 | import java.io.ObjectOutput; 25 | import java.io.Serializable; 26 | import java.io.StreamCorruptedException; 27 | import java.util.Collection; 28 | import java.util.Iterator; 29 | import java.util.Set; 30 | import java.util.Spliterator; 31 | import java.util.function.Consumer; 32 | import java.util.function.Predicate; 33 | import java.util.stream.Stream; 34 | 35 | /** 36 | * An implementation of {@link Set} interface backed by a {@link TrieMap}. The implementation is fully concurrent 37 | * and additionally supports O(1) {@link MutableTrieSet mutable} and {@link ImmutableTrieSet immutable} isolated 38 | * snapshots. 39 | * 40 | * @param the type of elements maintained by this set 41 | */ 42 | public abstract sealed class TrieSet implements Set, Serializable permits ImmutableTrieSet, MutableTrieSet { 43 | @java.io.Serial 44 | private static final long serialVersionUID = 0L; 45 | 46 | @SuppressFBWarnings(value = "SE_TRANSIENT_FIELD_NOT_RESTORED", justification = "Handled through writeReplace") 47 | private final transient TrieMap map; 48 | // Cached map keyset view, so we do not re-checking it all over 49 | @SuppressFBWarnings(value = "SE_TRANSIENT_FIELD_NOT_RESTORED", justification = "Handled through writeReplace") 50 | private final transient AbstractKeySet set; 51 | 52 | TrieSet(final TrieMap map) { 53 | this.map = requireNonNull(map); 54 | set = map.createKeySet(); 55 | } 56 | 57 | /** 58 | * Create a new {@link MutableTrieSet}. 59 | * 60 | * @param element type 61 | * @return A new {@link MutableTrieSet}. 62 | */ 63 | public static MutableTrieSet create() { 64 | return new MutableTrieSet<>(TrieMap.create()); 65 | } 66 | 67 | /** 68 | * Returns a snapshot of this TrieSet. This operation is lock-free and linearizable. Modification operations on this 69 | * Set and the returned one are isolated from each other. 70 | * 71 | *

The snapshot is lazily updated - the first time some branch in the snapshot or this TrieSet are accessed, they 72 | * are rewritten. This means that the work of rebuilding both the snapshot and this TrieSet is distributed across 73 | * all the threads doing updates or accesses subsequent to the snapshot creation. 74 | * 75 | * @return A read-write TrieSet containing the contents of this set. 76 | */ 77 | public final MutableTrieSet mutableSnapshot() { 78 | return new MutableTrieSet<>(map.mutableSnapshot()); 79 | } 80 | 81 | /** 82 | * Returns a read-only snapshot of this TrieSet. This operation is lock-free and linearizable. 83 | * 84 | *

The snapshot is lazily updated - the first time some branch of this TrieSet are accessed, it is rewritten. The 85 | * work of creating the snapshot is thus distributed across subsequent updates and accesses on this TrieSet by all 86 | * threads. Note that the snapshot itself is never rewritten unlike when calling {@link #mutableSnapshot()}, but the 87 | * obtained snapshot cannot be modified. 88 | * 89 | * @return A read-only TrieSet containing the contents of this map. 90 | */ 91 | public abstract ImmutableTrieSet immutableSnapshot(); 92 | 93 | @Override 94 | @SuppressWarnings("checkstyle:parameterName") 95 | public final boolean remove(final Object o) { 96 | return map.remove(o) != null; 97 | } 98 | 99 | @Override 100 | @SuppressWarnings("checkstyle:parameterName") 101 | public final boolean removeAll(final Collection c) { 102 | return set.removeAll(c); 103 | } 104 | 105 | @Override 106 | @SuppressWarnings("checkstyle:parameterName") 107 | public final boolean add(final E e) { 108 | return map.putIfAbsent(e, Boolean.TRUE) == null; 109 | } 110 | 111 | @Override 112 | @SuppressWarnings("checkstyle:parameterName") 113 | public final boolean contains(final Object o) { 114 | return map.containsKey(o); 115 | } 116 | 117 | @Override 118 | @SuppressWarnings("checkstyle:parameterName") 119 | public final boolean containsAll(final Collection c) { 120 | return set.containsAll(c); 121 | } 122 | 123 | @Override 124 | @SuppressWarnings("checkstyle:parameterName") 125 | public final boolean retainAll(final Collection c) { 126 | return set.retainAll(c); 127 | } 128 | 129 | @Override 130 | public final boolean isEmpty() { 131 | return map.isEmpty(); 132 | } 133 | 134 | @Override 135 | public final int size() { 136 | return map.size(); 137 | } 138 | 139 | @Override 140 | public final void clear() { 141 | map.clear(); 142 | } 143 | 144 | @Override 145 | public final Object[] toArray() { 146 | return set.toArray(); 147 | } 148 | 149 | @Override 150 | @SuppressWarnings("checkstyle:parameterName") 151 | public final T[] toArray(final T[] a) { 152 | return set.toArray(a); 153 | } 154 | 155 | @Override 156 | public final void forEach(final Consumer action) { 157 | set.forEach(action); 158 | } 159 | 160 | @Override 161 | public final boolean removeIf(final Predicate filter) { 162 | return set.removeIf(filter); 163 | } 164 | 165 | @Override 166 | public final Iterator iterator() { 167 | return set.iterator(); 168 | } 169 | 170 | @Override 171 | public final Spliterator spliterator() { 172 | return set.spliterator(); 173 | } 174 | 175 | @Override 176 | public final Stream stream() { 177 | return set.stream(); 178 | } 179 | 180 | @Override 181 | public final Stream parallelStream() { 182 | return set.parallelStream(); 183 | } 184 | 185 | @Override 186 | public final int hashCode() { 187 | return set.hashCode(); 188 | } 189 | 190 | @Override 191 | public final boolean equals(final Object obj) { 192 | return obj == this || set.equals(obj); 193 | } 194 | 195 | @Override 196 | public final String toString() { 197 | return set.toString(); 198 | } 199 | 200 | /** 201 | * Replace this set with its {@link SerializedForm}. 202 | * 203 | * @return {@link SerializedForm} 204 | */ 205 | @java.io.Serial 206 | final Object writeReplace() { 207 | return new SerializedForm(this); 208 | } 209 | 210 | final TrieMap map() { 211 | return map; 212 | } 213 | 214 | private static final class SerializedForm implements Externalizable { 215 | @java.io.Serial 216 | private static final long serialVersionUID = 0L; 217 | 218 | private transient TrieSet set; 219 | 220 | @SuppressWarnings("checkstyle:redundantModifier") 221 | public SerializedForm() { 222 | // For Externalizable 223 | } 224 | 225 | SerializedForm(final TrieSet set) { 226 | this.set = requireNonNull(set); 227 | } 228 | 229 | @Override 230 | public void writeExternal(final ObjectOutput out) throws IOException { 231 | final var snap = set.immutableSnapshot(); 232 | out.writeBoolean(set instanceof ImmutableTrieSet); 233 | out.writeInt(snap.size()); 234 | for (Object e : snap) { 235 | out.writeObject(e); 236 | } 237 | } 238 | 239 | @Override 240 | public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException { 241 | final boolean readOnly = in.readBoolean(); 242 | final int size = in.readInt(); 243 | if (size < 0) { 244 | throw new StreamCorruptedException("Expected non-negative size instead of " + size); 245 | } 246 | 247 | final var read = TrieSet.create(); 248 | for (int i = 0; i < size; ++i) { 249 | read.add(in.readObject()); 250 | } 251 | 252 | set = readOnly ? read.immutableSnapshot() : read; 253 | } 254 | 255 | @java.io.Serial 256 | Object readResolve() { 257 | return set; 258 | } 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/VerifyException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import org.eclipse.jdt.annotation.NonNull; 19 | import org.eclipse.jdt.annotation.Nullable; 20 | 21 | final class VerifyException extends RuntimeException { 22 | @java.io.Serial 23 | private static final long serialVersionUID = 1L; 24 | 25 | VerifyException(final @NonNull String message) { 26 | super(message); 27 | } 28 | 29 | static @NonNull T throwIfNull(final @Nullable T obj) { 30 | if (obj == null) { 31 | throw new VerifyException("Unexpected null reference"); 32 | } 33 | return obj; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /triemap/src/main/java/tech/pantheon/triemap/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2017 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Implementation of {@link java.util.concurrent.ConcurrentMap} on top 18 | * of a concurrent hash-trie. 19 | */ 20 | @org.osgi.annotation.bundle.Export 21 | package tech.pantheon.triemap; -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/AbstractEntryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertFalse; 20 | import static org.junit.jupiter.api.Assertions.assertTrue; 21 | 22 | import java.util.Map; 23 | import org.junit.jupiter.api.Test; 24 | 25 | class AbstractEntryTest { 26 | @Test 27 | void testEqual() { 28 | final var key = new Object(); 29 | final var value = new Object(); 30 | assertFalse(AbstractEntry.equals(null, key, value)); 31 | assertFalse(AbstractEntry.equals(key, key, value)); 32 | 33 | final var entry = Map.entry(key, value); 34 | assertTrue(AbstractEntry.equals(entry, key, value)); 35 | assertFalse(AbstractEntry.equals(entry, value, value)); 36 | assertFalse(AbstractEntry.equals(entry, key, key)); 37 | } 38 | 39 | @Test 40 | void testHash() { 41 | final var key = new Object(); 42 | final var value = new Object(); 43 | assertEquals(key.hashCode() ^ value.hashCode(), AbstractEntry.hashCode(key, value)); 44 | } 45 | 46 | @Test 47 | void testString() { 48 | assertEquals("foo=bar", AbstractEntry.toString("foo", "bar")); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/CNodeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertThrows; 20 | 21 | import org.junit.jupiter.api.Test; 22 | 23 | class CNodeTest { 24 | @Test 25 | void testTrySize() { 26 | assertEquals(MainNode.NO_SIZE, new CNode<>(new Gen()).trySize()); 27 | } 28 | 29 | @Test 30 | void testInvalidElement() { 31 | assertThrows(VerifyException.class, () -> CNode.invalidElement(null)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/ConstantsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package tech.pantheon.triemap; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | 12 | import org.junit.jupiter.api.Test; 13 | 14 | class ConstantsTest { 15 | @Test 16 | void hashBits() throws Exception { 17 | // We assume Object.hashCode() is 32 bits 18 | assertEquals(int.class, Object.class.getDeclaredMethod("hashCode").getReturnType()); 19 | assertEquals(Integer.SIZE, Constants.HASH_BITS); 20 | } 21 | 22 | @Test 23 | void levelBits() throws Exception { 24 | // CNode.bitmap can store 32 bits 25 | assertEquals(int.class, CNode.class.getDeclaredField("bitmap").getType()); 26 | assertEquals((int) (Math.log(Integer.SIZE) / Math.log(2)), Constants.LEVEL_BITS); 27 | } 28 | 29 | @Test 30 | void maxDepth() throws Exception { 31 | assertEquals((int) Math.ceil((double)Constants.HASH_BITS / Constants.LEVEL_BITS), Constants.MAX_DEPTH); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/EquivalenceTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 PANTHEON.tech, s.r.o. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package tech.pantheon.triemap; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | import static org.junit.jupiter.api.Assertions.assertSame; 12 | 13 | import java.io.ByteArrayInputStream; 14 | import java.io.ByteArrayOutputStream; 15 | import java.io.ObjectInputStream; 16 | import java.io.ObjectOutputStream; 17 | import java.util.HexFormat; 18 | import org.junit.jupiter.api.Test; 19 | 20 | class EquivalenceTest { 21 | @Test 22 | void readResolveWorks() throws Exception { 23 | final var baos = new ByteArrayOutputStream(); 24 | try (var oos = new ObjectOutputStream(baos)) { 25 | oos.writeObject(Equivalence.Equals.INSTANCE); 26 | } 27 | 28 | final var bytes = baos.toByteArray(); 29 | assertEquals(""" 30 | aced000573720028746563682e70616e7468656f6e2e747269656d61702e4571756976616c656e636524457175616c7300000000000\ 31 | 0000102000078720021746563682e70616e7468656f6e2e747269656d61702e4571756976616c656e63650000000000000001020000\ 32 | 7870""", HexFormat.of().formatHex(bytes)); 33 | 34 | try (var ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) { 35 | assertSame(Equivalence.Equals.INSTANCE, ois.readObject()); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/INodeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertThrows; 20 | 21 | import org.junit.jupiter.api.Test; 22 | import tech.pantheon.triemap.INode.FailedGcas; 23 | 24 | class INodeTest { 25 | @Test 26 | void testInvalidElement() { 27 | assertThrows(VerifyException.class, () -> INode.invalidElement(null)); 28 | } 29 | 30 | @Test 31 | void testFailedGcasToString() { 32 | final var tnode = new TNode<>(new CNode<>(new Gen()), new Object(), new Object(), 123); 33 | assertEquals("FailedNode(" + tnode + ")", new FailedGcas<>(tnode).toString()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/ImmutableEntrySetTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertFalse; 20 | import static org.junit.jupiter.api.Assertions.assertThrows; 21 | import static org.junit.jupiter.api.Assertions.assertTrue; 22 | 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.function.Predicate; 26 | import org.junit.jupiter.api.BeforeEach; 27 | import org.junit.jupiter.api.Test; 28 | 29 | class ImmutableEntrySetTest { 30 | private ImmutableEntrySet set; 31 | 32 | @BeforeEach 33 | void before() { 34 | set = TrieMap.create().immutableSnapshot().createEntrySet(); 35 | } 36 | 37 | @Test 38 | void testIsEmpty() { 39 | assertTrue(set.isEmpty()); 40 | } 41 | 42 | @Test 43 | void testSize() { 44 | assertEquals(0, set.size()); 45 | } 46 | 47 | @Test 48 | void testClear() { 49 | assertThrows(UnsupportedOperationException.class, () -> set.clear()); 50 | } 51 | 52 | @Test 53 | void testRemove() { 54 | final var arg = new Object(); 55 | assertThrows(UnsupportedOperationException.class, () -> set.remove(arg)); 56 | } 57 | 58 | @Test 59 | void testRemoveAll() { 60 | final var arg = List.of(); 61 | assertThrows(UnsupportedOperationException.class, () -> set.removeAll(arg)); 62 | } 63 | 64 | @Test 65 | void testRemoveIf() { 66 | final Predicate arg = obj -> false; 67 | assertThrows(UnsupportedOperationException.class, () -> set.removeIf(arg)); 68 | } 69 | 70 | @Test 71 | void testRetainAll() { 72 | final var arg = List.of(); 73 | assertThrows(UnsupportedOperationException.class, () -> set.retainAll(arg)); 74 | } 75 | 76 | @Test 77 | void testIteratorSetValue() { 78 | final var map = TrieMap.create(); 79 | map.put("a", "b"); 80 | assertEquals(Map.of("a", "b"), map); 81 | 82 | final var snap = map.immutableSnapshot(); 83 | assertEquals(Map.of("a", "b"), snap); 84 | 85 | final var it = snap.createEntrySet().iterator(); 86 | assertTrue(it.hasNext()); 87 | 88 | final var entry = it.next(); 89 | assertEquals(Map.entry("a", "b"), entry); 90 | assertFalse(it.hasNext()); 91 | 92 | assertThrows(UnsupportedOperationException.class, () -> entry.setValue("c")); 93 | assertEquals(Map.entry("a", "b"), entry); 94 | assertEquals(Map.of("a", "b"), snap); 95 | } 96 | 97 | @Test 98 | void testSpliteratorSetValue() { 99 | final var map = TrieMap.create(); 100 | map.put("a", "b"); 101 | assertEquals(Map.of("a", "b"), map); 102 | 103 | final var snap = map.immutableSnapshot(); 104 | final var sp = snap.createEntrySet().spliterator(); 105 | assertTrue(sp.tryAdvance(entry -> { 106 | assertThrows(UnsupportedOperationException.class, () -> entry.setValue("c")); 107 | })); 108 | assertEquals(Map.of("a", "b"), snap); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/ImmutableKeySetTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertFalse; 20 | import static org.junit.jupiter.api.Assertions.assertThrows; 21 | import static org.junit.jupiter.api.Assertions.assertTrue; 22 | 23 | import java.util.List; 24 | import java.util.function.Predicate; 25 | import org.junit.jupiter.api.BeforeEach; 26 | import org.junit.jupiter.api.Test; 27 | 28 | class ImmutableKeySetTest { 29 | private ImmutableKeySet set; 30 | 31 | @BeforeEach 32 | void before() { 33 | set = TrieMap.create().immutableSnapshot().createKeySet(); 34 | } 35 | 36 | @Test 37 | void testIsEmpty() { 38 | assertTrue(set.isEmpty()); 39 | } 40 | 41 | @Test 42 | void testSize() { 43 | assertEquals(0, set.size()); 44 | } 45 | 46 | @Test 47 | void testClear() { 48 | assertThrows(UnsupportedOperationException.class, () -> set.clear()); 49 | } 50 | 51 | @Test 52 | void testRemove() { 53 | final var arg = new Object(); 54 | assertThrows(UnsupportedOperationException.class, () -> set.remove(arg)); 55 | } 56 | 57 | @Test 58 | void testRemoveAll() { 59 | final var arg = List.of(); 60 | assertThrows(UnsupportedOperationException.class, () -> set.removeAll(arg)); 61 | } 62 | 63 | @Test 64 | void testRemoveIf() { 65 | final Predicate arg = obj -> false; 66 | assertThrows(UnsupportedOperationException.class, () -> set.removeIf(arg)); 67 | } 68 | 69 | @Test 70 | void testRetainAll() { 71 | final var arg = List.of(); 72 | assertThrows(UnsupportedOperationException.class, () -> set.retainAll(arg)); 73 | } 74 | 75 | @Test 76 | void testIterator() { 77 | assertFalse(set.iterator().hasNext()); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/ImmutableTrieMapTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertThrows; 19 | 20 | import org.junit.jupiter.api.BeforeEach; 21 | import org.junit.jupiter.api.Test; 22 | 23 | class ImmutableTrieMapTest { 24 | private ImmutableTrieMap map; 25 | 26 | @BeforeEach 27 | void before() { 28 | map = ImmutableTrieMap.create().immutableSnapshot(); 29 | } 30 | 31 | @Test 32 | void testClear() { 33 | assertThrows(UnsupportedOperationException.class, () -> map.clear()); 34 | } 35 | 36 | @Test 37 | void testCompute() { 38 | assertThrows(UnsupportedOperationException.class, () -> map.compute(null, null)); 39 | } 40 | 41 | @Test 42 | void testComputeIfAbsent() { 43 | assertThrows(UnsupportedOperationException.class, () -> map.computeIfAbsent(null, null)); 44 | } 45 | 46 | @Test 47 | void testComputeIfPresent() { 48 | assertThrows(UnsupportedOperationException.class, () -> map.computeIfPresent(null, null)); 49 | } 50 | 51 | @Test 52 | void testMerge() { 53 | assertThrows(UnsupportedOperationException.class, () -> map.merge(null, null, null)); 54 | } 55 | 56 | @Test 57 | void testPut() { 58 | assertThrows(UnsupportedOperationException.class, () -> map.put(null, null)); 59 | } 60 | 61 | @Test 62 | void testPutAll() { 63 | assertThrows(UnsupportedOperationException.class, () -> map.putAll(null)); 64 | } 65 | 66 | @Test 67 | void testPutIfAbsent() { 68 | assertThrows(UnsupportedOperationException.class, () -> map.putIfAbsent(null, null)); 69 | } 70 | 71 | @Test 72 | void testRemove() { 73 | assertThrows(UnsupportedOperationException.class, () -> map.remove(null)); 74 | } 75 | 76 | @Test 77 | void testRemoveExact() { 78 | assertThrows(UnsupportedOperationException.class, () -> map.remove(null, null)); 79 | } 80 | 81 | @Test 82 | void testReplace() { 83 | assertThrows(UnsupportedOperationException.class, () -> map.replace(null, null)); 84 | } 85 | 86 | @Test 87 | void testReplaceExact() { 88 | assertThrows(UnsupportedOperationException.class, () -> map.replace(null, null, null)); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/ImmutableTrieSetTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved. 3 | * 4 | * This program and the accompanying materials are made available under the 5 | * terms of the Eclipse Public License v1.0 which accompanies this distribution, 6 | * and is available at http://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package tech.pantheon.triemap; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertSame; 11 | import static org.junit.jupiter.api.Assertions.assertThrows; 12 | 13 | import java.util.List; 14 | import java.util.function.Predicate; 15 | import org.junit.jupiter.api.Test; 16 | 17 | class ImmutableTrieSetTest { 18 | private final ImmutableTrieSet set = ImmutableTrieSet.create().immutableSnapshot(); 19 | 20 | @Test 21 | void immutableSnapshotIsSame() { 22 | assertSame(set, set.immutableSnapshot()); 23 | } 24 | 25 | @Test 26 | void addThrows() { 27 | final var obj = new Object(); 28 | assertThrows(UnsupportedOperationException.class, () -> set.add(obj)); 29 | 30 | final var coll = List.of(); 31 | assertThrows(UnsupportedOperationException.class, () -> set.addAll(coll)); 32 | } 33 | 34 | @Test 35 | void clearThrows() { 36 | assertThrows(UnsupportedOperationException.class, () -> set.clear()); 37 | } 38 | 39 | @Test 40 | void removeThrows() { 41 | final var obj = new Object(); 42 | assertThrows(UnsupportedOperationException.class, () -> set.remove(obj)); 43 | 44 | final var coll = List.of(); 45 | assertThrows(UnsupportedOperationException.class, () -> set.removeAll(coll)); 46 | assertThrows(UnsupportedOperationException.class, () -> set.retainAll(coll)); 47 | } 48 | 49 | @Test 50 | void removeIfThrows() { 51 | final Predicate pred = obj -> true; 52 | assertThrows(UnsupportedOperationException.class, () -> set.removeIf(pred)); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/LNodeEntriesTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static java.lang.Boolean.FALSE; 19 | import static java.lang.Boolean.TRUE; 20 | import static org.junit.jupiter.api.Assertions.assertEquals; 21 | import static org.junit.jupiter.api.Assertions.assertNull; 22 | import static org.junit.jupiter.api.Assertions.assertSame; 23 | import static org.junit.jupiter.api.Assertions.assertThrows; 24 | 25 | import java.util.Map; 26 | import org.junit.jupiter.api.Test; 27 | 28 | class LNodeEntriesTest { 29 | private LNodeEntries map = LNodeEntries.of(1, TRUE, 2, TRUE); 30 | 31 | @Test 32 | void testReplaceInvalid() { 33 | final var lnode = new LNodeEntries.Single<>(1, TRUE); 34 | final var ex = assertThrows(VerifyException.class, () -> map.replace(lnode, FALSE)); 35 | assertEquals("Failed to find entry 1=true", ex.getMessage()); 36 | } 37 | 38 | @Test 39 | void testReplaceHead() { 40 | final var modified = map.replace(map, FALSE); 41 | assertEquals(map.next(), modified.next()); 42 | assertEquals(Map.entry(1, FALSE), modified); 43 | 44 | final var trimmed = modified.removeEntry(modified); 45 | assertEquals(Map.entry(2, TRUE), trimmed.replace(trimmed, TRUE)); 46 | } 47 | 48 | @Test 49 | void testReplaceTail() { 50 | final var modified = map.replace(map.next(), FALSE); 51 | assertEquals(map, modified.next()); 52 | assertEquals(Map.entry(2, FALSE), modified); 53 | 54 | final var trimmed = modified.removeEntry(modified); 55 | assertEquals(Map.entry(1, TRUE), trimmed.replace(trimmed, TRUE)); 56 | } 57 | 58 | @Test 59 | void testRemoveHead() { 60 | final var modified = map.removeEntry(map); 61 | assertSame(map.next(), modified); 62 | assertNull(modified.removeEntry(modified)); 63 | } 64 | 65 | @Test 66 | void testRemoveTail() { 67 | final LNodeEntries modified = map.removeEntry(map.next()); 68 | assertEquals(map, modified); 69 | assertNull(modified.removeEntry(modified)); 70 | } 71 | 72 | /** 73 | * Test if Listmap.get() does not cause stack overflow. 74 | */ 75 | @Test 76 | void testGetOverflow() { 77 | // 30K seems to be enough to trigger the problem locally 78 | for (int i = 3; i < 30000; ++i) { 79 | map = map.insertEntry(i, TRUE); 80 | } 81 | 82 | assertNull(map.findEntry(0)); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/LNodeEntryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertThrows; 20 | 21 | import java.util.Map; 22 | import org.junit.jupiter.api.Test; 23 | 24 | class LNodeEntryTest { 25 | private static final String KEY1 = "key1"; 26 | private static final String KEY2 = "key2"; 27 | private static final String VALUE = "value"; 28 | 29 | private final LNodeEntries entry = LNodeEntries.of(KEY1, VALUE, KEY2, VALUE); 30 | 31 | @Test 32 | void testEntryUtil() { 33 | assertEquals(AbstractEntry.hashCode(KEY1, VALUE), entry.hashCode()); 34 | assertEquals(AbstractEntry.toString(KEY1, VALUE), entry.toString()); 35 | 36 | final var testEntry = Map.entry(KEY1, VALUE); 37 | assertEquals(AbstractEntry.equals(testEntry, KEY1, VALUE), entry.equals(testEntry)); 38 | } 39 | 40 | @Test 41 | void testSetValue() { 42 | assertThrows(UnsupportedOperationException.class, () -> entry.setValue(null)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/MutableEntrySetTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertFalse; 20 | import static org.junit.jupiter.api.Assertions.assertThrows; 21 | import static org.junit.jupiter.api.Assertions.assertTrue; 22 | 23 | import java.util.AbstractMap.SimpleImmutableEntry; 24 | import java.util.Map; 25 | import org.junit.jupiter.api.BeforeEach; 26 | import org.junit.jupiter.api.Test; 27 | 28 | class MutableEntrySetTest { 29 | private static final String KEY = "key"; 30 | private static final String VALUE = "value"; 31 | private static final String VALUE2 = "value2"; 32 | 33 | private MutableEntrySet set; 34 | private MutableTrieMap map; 35 | 36 | @BeforeEach 37 | void before() { 38 | map = TrieMap.create(); 39 | map.put(KEY, VALUE); 40 | set = map.createEntrySet(); 41 | } 42 | 43 | @Test 44 | void testAdd() { 45 | assertThrows(UnsupportedOperationException.class, () -> set.add(null)); 46 | } 47 | 48 | @Test 49 | void testClear() { 50 | set.clear(); 51 | assertTrue(map.isEmpty()); 52 | assertTrue(set.isEmpty()); 53 | } 54 | 55 | @Test 56 | void testContains() { 57 | assertFalse(set.contains(null)); 58 | assertFalse(set.contains(new SimpleImmutableEntry<>(null, VALUE))); 59 | assertFalse(set.contains(new SimpleImmutableEntry<>(KEY, null))); 60 | assertFalse(set.contains(Map.entry(KEY, KEY))); 61 | assertFalse(set.contains(Map.entry(VALUE, KEY))); 62 | assertTrue(set.contains(Map.entry(KEY, VALUE))); 63 | } 64 | 65 | @Test 66 | void testRemove() { 67 | assertFalse(set.remove(null)); 68 | assertEquals(1, map.size()); 69 | assertEquals(1, set.size()); 70 | assertFalse(set.remove(new SimpleImmutableEntry<>(null, VALUE))); 71 | assertEquals(1, map.size()); 72 | assertEquals(1, set.size()); 73 | assertFalse(set.remove(new SimpleImmutableEntry<>(KEY, null))); 74 | assertEquals(1, map.size()); 75 | assertEquals(1, set.size()); 76 | assertFalse(set.remove(Map.entry(KEY, KEY))); 77 | assertEquals(1, map.size()); 78 | assertEquals(1, set.size()); 79 | assertTrue(set.remove(Map.entry(KEY, VALUE))); 80 | assertTrue(map.isEmpty()); 81 | } 82 | 83 | @Test 84 | void testIterator() { 85 | final var it = set.iterator(); 86 | assertTrue(it.hasNext()); 87 | assertEquals(Map.entry(KEY, VALUE), it.next()); 88 | assertFalse(it.hasNext()); 89 | } 90 | 91 | @Test 92 | void testIteratorSetValue() { 93 | final var it = set.iterator(); 94 | assertTrue(it.hasNext()); 95 | 96 | final var entry = it.next(); 97 | assertEquals(Map.entry(KEY, VALUE), entry); 98 | assertFalse(it.hasNext()); 99 | 100 | entry.setValue(VALUE2); 101 | assertEquals(Map.entry(KEY, VALUE2), entry); 102 | assertEquals(Map.of(KEY, VALUE2), map); 103 | } 104 | 105 | @Test 106 | void testSpliteratorSetValue() { 107 | final var sp = set.spliterator(); 108 | assertTrue(sp.tryAdvance(entry -> { 109 | assertEquals(Map.entry(KEY, VALUE), entry); 110 | entry.setValue(VALUE2); 111 | assertEquals(Map.entry(KEY, VALUE2), entry); 112 | })); 113 | assertEquals(Map.of(KEY, VALUE2), map); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/MutableIteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertThrows; 20 | 21 | import java.util.Map; 22 | import org.junit.jupiter.api.BeforeEach; 23 | import org.junit.jupiter.api.Test; 24 | 25 | class MutableIteratorTest { 26 | private static final String KEY = "key"; 27 | private static final String VALUE = "value"; 28 | 29 | private MutableIterator it; 30 | 31 | @BeforeEach 32 | void before() { 33 | final var map = TrieMap.create(); 34 | map.put(KEY, VALUE); 35 | it = map.iterator(); 36 | } 37 | 38 | @Test 39 | void testEntryUtil() { 40 | final var entry = it.next(); 41 | 42 | assertEquals(AbstractEntry.hashCode(KEY, VALUE), entry.hashCode()); 43 | assertEquals(AbstractEntry.toString(KEY, VALUE), entry.toString()); 44 | 45 | final var testEntry = Map.entry(KEY, VALUE); 46 | assertEquals(AbstractEntry.equals(testEntry, KEY, VALUE), entry.equals(entry)); 47 | } 48 | 49 | @Test 50 | void testRemoveWithoutNext() { 51 | assertThrows(IllegalStateException.class, () -> it.remove()); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/MutableKeySetTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertFalse; 20 | import static org.junit.jupiter.api.Assertions.assertThrows; 21 | import static org.junit.jupiter.api.Assertions.assertTrue; 22 | 23 | import org.junit.jupiter.api.BeforeEach; 24 | import org.junit.jupiter.api.Test; 25 | 26 | class MutableKeySetTest { 27 | private static final String KEY = "key"; 28 | private static final String VALUE = "value"; 29 | 30 | private MutableKeySet set; 31 | private MutableTrieMap map; 32 | 33 | @BeforeEach 34 | void before() { 35 | map = TrieMap.create(); 36 | map.put(KEY, VALUE); 37 | set = map.createKeySet(); 38 | } 39 | 40 | @Test 41 | void testAdd() { 42 | assertThrows(UnsupportedOperationException.class, () -> set.add(null)); 43 | } 44 | 45 | @Test 46 | void testAddAll() { 47 | assertThrows(UnsupportedOperationException.class, () -> set.addAll(null)); 48 | } 49 | 50 | @Test 51 | void testClear() { 52 | set.clear(); 53 | assertTrue(map.isEmpty()); 54 | assertTrue(set.isEmpty()); 55 | } 56 | 57 | 58 | @Test 59 | void testContains() { 60 | assertTrue(set.contains(KEY)); 61 | assertFalse(set.contains(VALUE)); 62 | } 63 | 64 | @Test 65 | void testContainsNull() { 66 | assertThrows(NullPointerException.class, () -> set.contains(null)); 67 | } 68 | 69 | @Test 70 | void testRemove() { 71 | assertFalse(set.remove(VALUE)); 72 | assertEquals(1, map.size()); 73 | assertEquals(1, set.size()); 74 | assertTrue(set.remove(KEY)); 75 | assertTrue(map.isEmpty()); 76 | } 77 | 78 | @Test 79 | void testRemoveNull() { 80 | assertThrows(NullPointerException.class, () -> set.remove(null)); 81 | } 82 | 83 | @Test 84 | void testIterator() { 85 | final var it = set.iterator(); 86 | assertTrue(it.hasNext()); 87 | assertEquals(KEY, it.next()); 88 | assertFalse(it.hasNext()); 89 | } 90 | 91 | @Test 92 | void testImmutableIterator() { 93 | final var it = set.immutableIterator(); 94 | assertTrue(it.hasNext()); 95 | assertEquals(KEY, it.next()); 96 | assertFalse(it.hasNext()); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/SNodeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertSame; 20 | 21 | import java.util.Map; 22 | import org.junit.jupiter.api.Test; 23 | 24 | class SNodeTest { 25 | private static final String KEY = "key"; 26 | private static final String VALUE = "value"; 27 | private static final int HASH = 1337; 28 | 29 | private final SNode snode = new SNode<>(KEY, VALUE, HASH); 30 | 31 | @Test 32 | void testCopyTombed() { 33 | final var tnode = new TNode<>(new CNode<>(new Gen()), snode); 34 | assertEquals(snode.hashCode(), tnode.hashCode()); 35 | assertSame(snode.key(), tnode.key()); 36 | assertSame(snode.value(), tnode.value()); 37 | } 38 | 39 | @Test 40 | void testEntryUtil() { 41 | assertEquals(AbstractEntry.hashCode(KEY, VALUE), snode.hashCode()); 42 | assertEquals(AbstractEntry.toString(KEY, VALUE), snode.toString()); 43 | 44 | final var entry = Map.entry(KEY, VALUE); 45 | assertEquals(AbstractEntry.equals(entry, KEY, VALUE), snode.equals(entry)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/SnapshotTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2017 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertFalse; 20 | import static org.junit.jupiter.api.Assertions.assertTrue; 21 | 22 | import org.junit.jupiter.api.BeforeEach; 23 | import org.junit.jupiter.api.Test; 24 | 25 | class SnapshotTest { 26 | private TrieMap map; 27 | 28 | @BeforeEach 29 | void setUp() { 30 | map = TrieMap.create(); 31 | map.put("k1", "v1"); 32 | map.put("k2", "v2"); 33 | } 34 | 35 | private static void assertMutableIsolation(final TrieMap m1, final TrieMap m2) { 36 | assertTrue(m2.containsKey("k1")); 37 | assertTrue(m2.containsKey("k2")); 38 | 39 | m1.remove("k1"); 40 | assertFalse(m1.containsKey("k1")); 41 | assertTrue(m2.containsKey("k1")); 42 | 43 | m2.remove("k2"); 44 | assertFalse(m1.containsKey("k1")); 45 | assertTrue(m2.containsKey("k1")); 46 | 47 | assertEquals(1, m1.size()); 48 | assertEquals(1, m2.size()); 49 | } 50 | 51 | @Test 52 | void testMutableSnapshotIsolation() { 53 | assertMutableIsolation(map, map.mutableSnapshot()); 54 | } 55 | 56 | @Test 57 | void testMutableSnapshotIsolationAcrossImmutable() { 58 | final var snap = map.immutableSnapshot(); 59 | assertTrue(snap.containsKey("k1")); 60 | assertTrue(snap.containsKey("k2")); 61 | 62 | assertMutableIsolation(map, snap.mutableSnapshot()); 63 | 64 | assertTrue(snap.containsKey("k1")); 65 | assertTrue(snap.containsKey("k2")); 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TNodeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertSame; 20 | 21 | import java.util.Map; 22 | import org.junit.jupiter.api.Test; 23 | 24 | class TNodeTest { 25 | private static final String KEY = "key"; 26 | private static final String VALUE = "value"; 27 | private static final int HASH = 1337; 28 | 29 | private final TNode tnode = new TNode<>(new CNode<>(new Gen()), KEY, VALUE, HASH); 30 | 31 | @Test 32 | void testCopyUntombed() { 33 | final var snode = new SNode<>(tnode); 34 | assertEquals(tnode.hashCode(), snode.hashCode()); 35 | assertSame(tnode.key(), snode.key()); 36 | assertSame(tnode.value(), snode.value()); 37 | } 38 | 39 | @Test 40 | void testSize() { 41 | assertEquals(1, tnode.trySize()); 42 | assertEquals(1, tnode.size(null)); 43 | } 44 | 45 | @Test 46 | void testEntryUtil() { 47 | assertEquals(AbstractEntry.hashCode(KEY, VALUE), tnode.hashCode()); 48 | assertEquals(AbstractEntry.toString(KEY, VALUE), tnode.toString()); 49 | 50 | final var entry = Map.entry(KEY, VALUE); 51 | assertEquals(AbstractEntry.equals(entry, KEY, VALUE), tnode.equals(entry)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestCNodeFlagCollision.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertNull; 19 | import static org.junit.jupiter.api.Assertions.assertSame; 20 | 21 | import org.junit.jupiter.api.Test; 22 | 23 | class TestCNodeFlagCollision { 24 | @Test 25 | void testCNodeFlagCollision() { 26 | final var map = TrieMap.create(); 27 | final Integer z15169 = 15169; 28 | final Integer z28336 = 28336; 29 | 30 | assertNull(map.get(z15169)); 31 | assertNull(map.get(z28336)); 32 | 33 | map.put(z15169, z15169); 34 | assertSame(z15169, map.get(z15169)); 35 | assertNull(map.get(z28336)); 36 | 37 | map.put(z28336, z28336); 38 | assertSame(z15169, map.get(z15169)); 39 | assertSame(z28336, map.get(z28336)); 40 | 41 | map.remove(z15169); 42 | 43 | assertNull(map.get(z15169)); 44 | assertSame(z28336, map.get(z28336)); 45 | 46 | map.remove(z28336); 47 | 48 | assertNull(map.get(z15169)); 49 | assertNull(map.get(z28336)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestCNodeInsertionIncorrectOrder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertSame; 19 | 20 | import org.junit.jupiter.api.Test; 21 | 22 | class TestCNodeInsertionIncorrectOrder { 23 | 24 | @Test 25 | void testCNodeInsertionIncorrectOrder() { 26 | final var map = TrieMap.create(); 27 | final Integer z3884 = 3884; 28 | final Integer z4266 = 4266; 29 | map.put(z3884, z3884); 30 | assertSame(z3884, map.get(z3884)); 31 | 32 | map.put(z4266, z4266); 33 | assertSame(z3884, map.get(z3884)); 34 | assertSame(z4266, map.get(z4266)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestConcurrentMapCompute.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | 20 | import org.junit.jupiter.api.Test; 21 | 22 | class TestConcurrentMapCompute { 23 | private static final int COUNT = 50 * 1000; 24 | 25 | @Test 26 | void testConcurrentMapCompute() { 27 | final var map = TrieMap.create(); 28 | 29 | for (int i = 0; i < COUNT; i++) { 30 | assertEquals(i + " -> null", map.compute(i, (k, v) -> k + " -> " + v)); 31 | assertEquals(i + " -> " + i + " -> null", map.compute(i, (k, v) -> k + " -> " + v)); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestConcurrentMapComputeIfAbsent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertNull; 20 | import static org.junit.jupiter.api.Assertions.assertSame; 21 | import static org.junit.jupiter.api.Assertions.fail; 22 | 23 | import org.junit.jupiter.api.Test; 24 | 25 | class TestConcurrentMapComputeIfAbsent { 26 | private static final int COUNT = 50 * 1000; 27 | 28 | @Test 29 | void testConcurrentMapComputeIfAbsentReturnsComputedValue() { 30 | final var map = TrieMap.create(); 31 | for (int i = 0; i < COUNT; i++) { 32 | assertEquals(Integer.toString(i), map.computeIfAbsent(i, Object::toString)); 33 | } 34 | } 35 | 36 | @Test 37 | void testConcurrentMapComputeIfAbsentDoesNotComputeIfPresent() { 38 | final var map = TrieMap.create(); 39 | for (int i = 0; i < COUNT; i++) { 40 | map.put(i, Integer.toString(i)); 41 | assertEquals(Integer.toString(i), map.computeIfAbsent(i, 42 | ignored -> fail("Should not have called function"))); 43 | } 44 | } 45 | 46 | @Test 47 | void testConflictingHash() { 48 | final var k1 = new ZeroHashInt(1); 49 | final var k2 = new ZeroHashInt(2); 50 | final var k3 = new ZeroHashInt(3); 51 | final var k3dup = new ZeroHashInt(3); 52 | final var v1 = new ZeroHashInt(4); 53 | final var v2 = new ZeroHashInt(5); 54 | final var v3 = new ZeroHashInt(6); 55 | 56 | final var map = TrieMap.create(); 57 | // Pre-populate an LNode 58 | assertSame(v1, map.computeIfAbsent(k1, k -> v1)); 59 | assertSame(v2, map.computeIfAbsent(k2, k -> v2)); 60 | assertSame(v3, map.computeIfAbsent(k3, k -> v3)); 61 | 62 | // Check with identical key 63 | assertSame(v3, map.computeIfAbsent(k3, k -> v3)); 64 | // Check with equivalent key 65 | assertSame(v3, map.computeIfAbsent(k3dup, k -> v3)); 66 | } 67 | 68 | @Test 69 | void testComputeNull() { 70 | final var map = TrieMap.create(); 71 | assertNull(map.computeIfAbsent("key", k -> null)); 72 | assertEquals("{}", map.toString()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestConcurrentMapComputeIfPresent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertFalse; 20 | import static org.junit.jupiter.api.Assertions.assertNull; 21 | import static org.junit.jupiter.api.Assertions.assertSame; 22 | import static org.junit.jupiter.api.Assertions.fail; 23 | 24 | import org.junit.jupiter.api.Test; 25 | 26 | class TestConcurrentMapComputeIfPresent { 27 | private static final int COUNT = 50 * 1000; 28 | 29 | @Test 30 | void testConcurrentMapComputeIfPresentDoesNotComputeIfAbsent() { 31 | final var map = TrieMap.create(); 32 | 33 | for (int i = 0; i < COUNT; i++) { 34 | assertNull(map.computeIfPresent(i, (k, v) -> fail("Should not have called function"))); 35 | assertFalse(map.containsKey(i)); 36 | } 37 | } 38 | 39 | 40 | @Test 41 | void testConcurrentMapComputeIfPresent() { 42 | final var map = TrieMap.create(); 43 | 44 | for (int i = 0; i < COUNT; i++) { 45 | map.put(i, "42"); 46 | assertEquals(i + " -> 42", map.computeIfPresent(i, (k, v) -> k + " -> " + v)); 47 | assertEquals(i + " -> 42", map.get(i)); 48 | } 49 | } 50 | 51 | @Test 52 | void testConcurrentMapComputeIfPresentRemovesValueIfComputesNull() { 53 | final var map = TrieMap.create(); 54 | 55 | for (int i = 0; i < COUNT; i++) { 56 | map.put(i, "42"); 57 | assertNull(map.computeIfPresent(i, (k, v) -> null)); 58 | assertFalse(map.containsKey(i)); 59 | } 60 | } 61 | 62 | @Test 63 | void testConflictingHash() { 64 | final var k1 = new ZeroHashInt(1); 65 | final var k2 = new ZeroHashInt(2); 66 | final var k3 = new ZeroHashInt(3); 67 | final var k3dup = new ZeroHashInt(3); 68 | final var v1 = new ZeroHashInt(4); 69 | final var v2 = new ZeroHashInt(5); 70 | final var v3 = new ZeroHashInt(6); 71 | 72 | final var map = TrieMap.create(); 73 | // Pre-populate an LNode 74 | assertNull(map.putIfAbsent(k1, v1)); 75 | assertNull(map.putIfAbsent(k2, v2)); 76 | assertNull(map.putIfAbsent(k3, v3)); 77 | 78 | // Check with identical key 79 | assertSame(v3, map.computeIfPresent(k3, (k, v) -> v3)); 80 | // Check with equivalent key 81 | assertSame(v3, map.computeIfPresent(k3dup, (k, v) -> v3)); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestConcurrentMapMerge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.fail; 20 | 21 | import org.junit.jupiter.api.Test; 22 | 23 | class TestConcurrentMapMerge { 24 | private static final int COUNT = 50 * 1000; 25 | 26 | @Test 27 | void testConcurrentMapMergeWhenValueAbsent() { 28 | final var map = TrieMap.create(); 29 | 30 | for (int i = 0; i < COUNT; i++) { 31 | final var newVal = Integer.toString(i + 10); 32 | assertEquals(newVal, map.merge(i, newVal, (ov, nv) -> fail("Should not have been called"))); 33 | } 34 | } 35 | 36 | @Test 37 | void testConcurrentMapMergeWhenValuePresent() { 38 | final var map = TrieMap.create(); 39 | 40 | for (int i = 0; i < COUNT; i++) { 41 | final var newVal = Integer.toString(i + 10); 42 | map.put(i, newVal); 43 | assertEquals(newVal + newVal, map.merge(i, newVal, (ov, nv) -> "" + ov + nv)); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestConcurrentMapPutIfAbsent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertNull; 20 | import static org.junit.jupiter.api.Assertions.assertSame; 21 | 22 | import org.junit.jupiter.api.Test; 23 | 24 | class TestConcurrentMapPutIfAbsent { 25 | private static final int COUNT = 50 * 1000; 26 | 27 | @Test 28 | void testConcurrentMapPutIfAbsent() { 29 | final var map = TrieMap.create(); 30 | 31 | for (int i = 0; i < COUNT; i++) { 32 | assertNull(map.putIfAbsent(i, i + 10)); 33 | assertEquals(i + 10, map.get(i)); 34 | } 35 | } 36 | 37 | @Test 38 | void testConcurrentMapPutIfAbsentSkipsIfPresent() { 39 | final var map = TrieMap.create(); 40 | 41 | for (int i = 0; i < COUNT; i++) { 42 | map.put(i, i + 10); 43 | assertEquals(i + 10, map.putIfAbsent(i, i + 20)); 44 | } 45 | } 46 | 47 | 48 | @Test 49 | void testConflictingHash() { 50 | final var k1 = new ZeroHashInt(1); 51 | final var k2 = new ZeroHashInt(2); 52 | final var k3 = new ZeroHashInt(3); 53 | final var k3dup = new ZeroHashInt(3); 54 | final var v1 = new ZeroHashInt(4); 55 | final var v2 = new ZeroHashInt(5); 56 | final var v3 = new ZeroHashInt(6); 57 | 58 | final var map = TrieMap.create(); 59 | // Pre-populate an LNode 60 | assertNull(map.putIfAbsent(k1, v1)); 61 | assertNull(map.putIfAbsent(k2, v2)); 62 | assertNull(map.putIfAbsent(k3, v3)); 63 | 64 | // Check with identical key 65 | assertSame(v3, map.putIfAbsent(k3, v3)); 66 | // Check with equivalent key 67 | assertSame(v3, map.putIfAbsent(k3dup, v3)); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestConcurrentMapRemove.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertFalse; 19 | import static org.junit.jupiter.api.Assertions.assertNull; 20 | import static org.junit.jupiter.api.Assertions.assertTrue; 21 | 22 | import org.junit.jupiter.api.Test; 23 | 24 | class TestConcurrentMapRemove { 25 | private static final int COUNT = 50 * 1000; 26 | 27 | @Test 28 | void testConcurrentMapRemove() { 29 | final var map = TrieMap.create(); 30 | 31 | for (int i = 128; i < COUNT; i++) { 32 | assertFalse(map.remove(i, i)); 33 | assertNull(map.put(i, i)); 34 | assertFalse(map.remove(i, "lol")); 35 | assertTrue(map.containsKey(i)); 36 | assertTrue(map.remove(i, i)); 37 | assertFalse(map.containsKey(i)); 38 | assertNull(map.put(i, i)); 39 | } 40 | } 41 | 42 | @Test 43 | void testConflictingHash() { 44 | final var k1 = new ZeroHashInt(1); 45 | final var k2 = new ZeroHashInt(2); 46 | final var k3 = new ZeroHashInt(3); 47 | final var k3dup = new ZeroHashInt(3); 48 | final var v1 = new ZeroHashInt(4); 49 | final var v2 = new ZeroHashInt(5); 50 | final var v3 = new ZeroHashInt(6); 51 | final var v3dup = new ZeroHashInt(6); 52 | 53 | final var map = TrieMap.create(); 54 | // Pre-populate an LNode 55 | assertNull(map.putIfAbsent(k1, v1)); 56 | assertNull(map.putIfAbsent(k2, v2)); 57 | assertNull(map.putIfAbsent(k3, v3)); 58 | 59 | assertFalse(map.remove(k3, v2)); 60 | assertTrue(map.remove(k3dup, v3dup)); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestConcurrentMapReplace.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertFalse; 20 | import static org.junit.jupiter.api.Assertions.assertNull; 21 | import static org.junit.jupiter.api.Assertions.assertTrue; 22 | 23 | import org.junit.jupiter.api.Test; 24 | 25 | class TestConcurrentMapReplace { 26 | private static final int COUNT = 50 * 1000; 27 | 28 | @Test 29 | void testConcurrentMapReplace() { 30 | final var map = TrieMap.create(); 31 | 32 | for (int i = 0; i < COUNT; i++) { 33 | assertNull(map.replace(i, "lol")); 34 | assertFalse(map.replace(i, i, "lol2")); 35 | assertNull(map.put(i, i)); 36 | assertEquals(i, map.replace(i, "lol")); 37 | assertFalse(map.replace(i, i, "lol2")); 38 | assertTrue(map.replace(i, "lol", i)); 39 | } 40 | } 41 | 42 | @Test 43 | void testConflictingHash() { 44 | final var k1 = new ZeroHashInt(1); 45 | final var k2 = new ZeroHashInt(2); 46 | final var k3 = new ZeroHashInt(3); 47 | final var k3dup = new ZeroHashInt(3); 48 | final var v1 = new ZeroHashInt(4); 49 | final var v2 = new ZeroHashInt(5); 50 | final var v3 = new ZeroHashInt(6); 51 | final var v3dup = new ZeroHashInt(6); 52 | final var k4 = new ZeroHashInt(7); 53 | 54 | final var map = TrieMap.create(); 55 | assertNull(map.put(k3, v3)); 56 | 57 | // First check for SNode 58 | assertNull(map.replace(k1, v1)); 59 | assertFalse(map.replace(k1, v1, v2)); 60 | assertFalse(map.replace(k3, v1, v3)); 61 | assertFalse(map.replace(k3dup, v1, v3dup)); 62 | assertTrue(map.replace(k3dup, v3dup, v1)); 63 | assertTrue(map.replace(k3dup, v1, v3)); 64 | 65 | // Bump up to LNode 66 | assertNull(map.put(k1, v1)); 67 | assertNull(map.put(k2, v2)); 68 | 69 | // Completely mismatched 70 | assertFalse(map.replace(k1, v2, v3)); 71 | 72 | // Identical value match 73 | assertTrue(map.replace(k2, v2, v3)); 74 | // Equivalent value match 75 | assertTrue(map.replace(k2, v3dup, v2)); 76 | 77 | // Equivalent match 78 | assertTrue(map.replace(k3dup, v3dup, v2)); 79 | 80 | // No match 81 | assertNull(map.replace(k4, v1)); 82 | assertFalse(map.replace(k4, v1, v2)); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestDelete.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertNotNull; 20 | import static org.junit.jupiter.api.Assertions.assertNull; 21 | import static org.junit.jupiter.api.Assertions.assertThrows; 22 | import static org.junit.jupiter.api.Assertions.assertTrue; 23 | 24 | import org.junit.jupiter.api.Test; 25 | 26 | class TestDelete { 27 | @Test 28 | void testNullSimple() { 29 | var tm = TrieMap.create(); 30 | assertThrows(NullPointerException.class, () -> tm.remove(null)); 31 | } 32 | 33 | @Test 34 | void testNullKey() { 35 | var tm = TrieMap.create(); 36 | assertThrows(NullPointerException.class, () -> tm.remove(null, "")); 37 | } 38 | 39 | @Test 40 | void testNullValue() { 41 | var tm = TrieMap.create(); 42 | assertThrows(NullPointerException.class, () -> tm.remove("", null)); 43 | } 44 | 45 | @Test 46 | void testNullBoth() { 47 | var tm = TrieMap.create(); 48 | assertThrows(NullPointerException.class, () -> tm.remove(null, null)); 49 | } 50 | 51 | @Test 52 | void testClear() { 53 | final var bt = TrieMap.create(); 54 | bt.put(1, 1); 55 | bt.clear(); 56 | assertTrue(bt.isEmpty()); 57 | assertEquals(0, bt.size()); 58 | } 59 | 60 | @Test 61 | void testDelete() { 62 | final var bt = TrieMap.create(); 63 | 64 | for (int i = 0; i < 10000; i++) { 65 | assertNull(bt.put(Integer.valueOf(i), Integer.valueOf(i))); 66 | assertEquals(Integer.valueOf(i), bt.get(Integer.valueOf(i))); 67 | } 68 | 69 | checkAddInsert(bt, 536); 70 | checkAddInsert(bt, 4341); 71 | checkAddInsert(bt, 8437); 72 | 73 | for (int i = 0; i < 10000; i++) { 74 | assertNotNull(bt.remove(Integer.valueOf(i))); 75 | assertNull(bt.get(Integer.valueOf(i))); 76 | } 77 | 78 | bt.toString(); 79 | } 80 | 81 | /** 82 | * Test if the Map.remove(Object, Object) method works correctly for hash collisions, which are handled by LNode. 83 | */ 84 | @Test 85 | void testRemoveObjectLNode() { 86 | final var bt = TrieMap.create(); 87 | 88 | for (int i = 0; i < 100; i++) { 89 | final var v = new ZeroHashInt(i); 90 | assertNull(bt.put(v, v)); 91 | } 92 | 93 | for (int i = 0; i < 100; i++) { 94 | final var v = new ZeroHashInt(i); 95 | assertTrue(bt.remove(v, v)); 96 | } 97 | } 98 | 99 | private static void checkAddInsert(final TrieMap bt, final int key) { 100 | final Integer v = key; 101 | bt.remove(v); 102 | Integer foundV = bt.get(v); 103 | assertNull(foundV); 104 | assertNull(bt.put(v, v)); 105 | foundV = bt.get(v); 106 | assertEquals(v, foundV); 107 | 108 | assertEquals(v, bt.put(v, Integer.valueOf(-1))); 109 | assertEquals(Integer.valueOf(-1), bt.put(v, v)); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestHashCollisions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertNotNull; 20 | import static org.junit.jupiter.api.Assertions.assertNull; 21 | 22 | import org.junit.jupiter.api.Test; 23 | 24 | class TestHashCollisions { 25 | @Test 26 | void testHashCollisions() { 27 | final var bt = TrieMap.create(); 28 | 29 | insertStrings(bt); 30 | insertChars(bt); 31 | insertInts(bt); 32 | insertBytes(bt); 33 | 34 | removeStrings(bt); 35 | removeChars(bt); 36 | removeInts(bt); 37 | removeBytes(bt); 38 | 39 | insertStrings(bt); 40 | insertInts(bt); 41 | insertBytes(bt); 42 | insertChars(bt); 43 | 44 | removeBytes(bt); 45 | removeStrings(bt); 46 | removeChars(bt); 47 | removeInts(bt); 48 | 49 | insertStrings(bt); 50 | insertInts(bt); 51 | insertBytes(bt); 52 | insertChars(bt); 53 | 54 | removeStrings(bt); 55 | removeChars(bt); 56 | removeInts(bt); 57 | removeBytes(bt); 58 | 59 | insertStrings(bt); 60 | insertInts(bt); 61 | insertBytes(bt); 62 | insertChars(bt); 63 | 64 | removeChars(bt); 65 | removeInts(bt); 66 | removeBytes(bt); 67 | removeStrings(bt); 68 | 69 | insertStrings(bt); 70 | insertInts(bt); 71 | insertBytes(bt); 72 | insertChars(bt); 73 | 74 | removeInts(bt); 75 | removeBytes(bt); 76 | removeStrings(bt); 77 | removeChars(bt); 78 | } 79 | 80 | private static void insertChars(final TrieMap bt) { 81 | assertNull(bt.put('a', 'a')); 82 | assertNull(bt.put('b', 'b')); 83 | assertNull(bt.put('c', 'c')); 84 | assertNull(bt.put('d', 'd')); 85 | assertNull(bt.put('e', 'e')); 86 | 87 | assertEquals('a', bt.put('a', 'a')); 88 | assertEquals('b', bt.put('b', 'b')); 89 | assertEquals('c', bt.put('c', 'c')); 90 | assertEquals('d', bt.put('d', 'd')); 91 | assertEquals('e', bt.put('e', 'e')); 92 | } 93 | 94 | private static void insertStrings(final TrieMap bt) { 95 | assertNull(bt.put("a", "a")); 96 | assertNull(bt.put("b", "b")); 97 | assertNull(bt.put("c", "c")); 98 | assertNull(bt.put("d", "d")); 99 | assertNull(bt.put("e", "e")); 100 | 101 | assertEquals("a", bt.put("a", "a")); 102 | assertEquals("b", bt.put("b", "b")); 103 | assertEquals("c", bt.put("c", "c")); 104 | assertEquals("d", bt.put("d", "d")); 105 | assertEquals("e", bt.put("e", "e")); 106 | } 107 | 108 | private static void insertBytes(final TrieMap bt) { 109 | for (byte i = 0; i < 128 && i >= 0; i++) { 110 | final Byte bigB = i; 111 | assertNull(bt.put(bigB, bigB)); 112 | assertEquals(bigB, bt.put(bigB, bigB)); 113 | } 114 | } 115 | 116 | private static void insertInts(final TrieMap bt) { 117 | for (int i = 0; i < 128; i++) { 118 | final Integer bigI = i; 119 | assertNull(bt.put(bigI, bigI)); 120 | assertEquals(bigI, bt.put(bigI, bigI)); 121 | } 122 | } 123 | 124 | private static void removeChars(final TrieMap bt) { 125 | assertNotNull(bt.get('a')); 126 | assertNotNull(bt.get('b')); 127 | assertNotNull(bt.get('c')); 128 | assertNotNull(bt.get('d')); 129 | assertNotNull(bt.get('e')); 130 | 131 | assertNotNull(bt.remove('a')); 132 | assertNotNull(bt.remove('b')); 133 | assertNotNull(bt.remove('c')); 134 | assertNotNull(bt.remove('d')); 135 | assertNotNull(bt.remove('e')); 136 | 137 | assertNull(bt.remove('a')); 138 | assertNull(bt.remove('b')); 139 | assertNull(bt.remove('c')); 140 | assertNull(bt.remove('d')); 141 | assertNull(bt.remove('e')); 142 | 143 | assertNull(bt.get('a')); 144 | assertNull(bt.get('b')); 145 | assertNull(bt.get('c')); 146 | assertNull(bt.get('d')); 147 | assertNull(bt.get('e')); 148 | } 149 | 150 | private static void removeStrings(final TrieMap bt) { 151 | assertNotNull(bt.get("a")); 152 | assertNotNull(bt.get("b")); 153 | assertNotNull(bt.get("c")); 154 | assertNotNull(bt.get("d")); 155 | assertNotNull(bt.get("e")); 156 | 157 | assertNotNull(bt.remove("a")); 158 | assertNotNull(bt.remove("b")); 159 | assertNotNull(bt.remove("c")); 160 | assertNotNull(bt.remove("d")); 161 | assertNotNull(bt.remove("e")); 162 | 163 | assertNull(bt.remove("a")); 164 | assertNull(bt.remove("b")); 165 | assertNull(bt.remove("c")); 166 | assertNull(bt.remove("d")); 167 | assertNull(bt.remove("e")); 168 | 169 | assertNull(bt.get("a")); 170 | assertNull(bt.get("b")); 171 | assertNull(bt.get("c")); 172 | assertNull(bt.get("d")); 173 | assertNull(bt.get("e")); 174 | } 175 | 176 | private static void removeInts(final TrieMap bt) { 177 | for (int i = 0; i < 128; i++) { 178 | final Integer bigI = i; 179 | assertNotNull(bt.get(bigI)); 180 | assertNotNull(bt.remove(bigI)); 181 | assertNull(bt.remove(bigI)); 182 | assertNull(bt.get(bigI)); 183 | } 184 | } 185 | 186 | private static void removeBytes(final TrieMap bt) { 187 | for (byte i = 0; i < 128 && i >= 0; i++) { 188 | final Byte bigB = i; 189 | assertNotNull(bt.get(bigB)); 190 | assertNotNull(bt.remove(bigB)); 191 | assertNull(bt.remove(bigB)); 192 | assertNull(bt.get(bigB)); 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestHashCollisionsRemove.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertTrue; 20 | 21 | import org.junit.jupiter.api.Test; 22 | 23 | class TestHashCollisionsRemove { 24 | private static final int COUNT = 50000; 25 | 26 | @Test 27 | void testHashCollisionsRemove() { 28 | final var bt = TrieMap.create(); 29 | 30 | for (int j = 0; j < COUNT; j++) { 31 | for (final Object o : TestMultiThreadMapIterator.getObjects(j)) { 32 | bt.put(o, o); 33 | } 34 | } 35 | 36 | for (int j = 0; j < COUNT; j++) { 37 | for (final Object o : TestMultiThreadMapIterator.getObjects(j)) { 38 | bt.remove(o); 39 | } 40 | } 41 | 42 | assertEquals(0, bt.size()); 43 | assertTrue(bt.isEmpty()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestHashCollisionsRemoveIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertTrue; 20 | 21 | import java.util.ArrayList; 22 | import org.junit.jupiter.api.Test; 23 | 24 | class TestHashCollisionsRemoveIterator { 25 | private static final int COUNT = 50000; 26 | 27 | @Test 28 | void testHashCollisionsRemoveIterator() { 29 | final var bt = TrieMap.create(); 30 | for (int j = 0; j < COUNT; j++) { 31 | bt.put(Integer.valueOf(j), Integer.valueOf(j)); 32 | } 33 | 34 | final var list = new ArrayList<>(COUNT); 35 | final var it = bt.entrySet().iterator(); 36 | while (it.hasNext()) { 37 | list.add(it.next().getKey()); 38 | it.remove(); 39 | } 40 | 41 | assertEquals(0, bt.size()); 42 | assertTrue(bt.isEmpty()); 43 | assertEquals(COUNT, list.size()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestInsert.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertNull; 20 | import static org.junit.jupiter.api.Assertions.assertThrows; 21 | import static org.junit.jupiter.api.Assertions.assertTrue; 22 | 23 | import org.junit.jupiter.api.Test; 24 | 25 | class TestInsert { 26 | @Test 27 | void testInsert() { 28 | final var bt = TrieMap.create(); 29 | assertNull(bt.put("a", "a")); 30 | assertTrue(bt.containsValue("a")); 31 | assertNull(bt.put("b", "b")); 32 | assertNull(bt.put("c", "b")); 33 | assertNull(bt.put("d", "b")); 34 | assertNull(bt.put("e", "b")); 35 | 36 | for (int i = 0; i < 10000; i++) { 37 | assertNull(bt.put(Integer.valueOf(i), Integer.valueOf(i))); 38 | assertEquals(Integer.valueOf(i), bt.get(Integer.valueOf(i))); 39 | } 40 | 41 | bt.toString(); 42 | } 43 | 44 | @Test 45 | void testNullKey() { 46 | final var tm = TrieMap.create(); 47 | assertThrows(NullPointerException.class, () -> tm.put(null, "")); 48 | } 49 | 50 | @Test 51 | void testNullValue() { 52 | final var tm = TrieMap.create(); 53 | assertThrows(NullPointerException.class, () -> tm.put("", null)); 54 | } 55 | 56 | @Test 57 | void testNullBoth() { 58 | final var tm = TrieMap.create(); 59 | assertThrows(NullPointerException.class, () -> tm.put(null, null)); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestMapIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertFalse; 20 | import static org.junit.jupiter.api.Assertions.assertNull; 21 | import static org.junit.jupiter.api.Assertions.assertSame; 22 | import static org.junit.jupiter.api.Assertions.assertThrows; 23 | import static org.junit.jupiter.api.Assertions.assertTrue; 24 | 25 | import java.util.HashSet; 26 | import java.util.Iterator; 27 | import java.util.NoSuchElementException; 28 | import java.util.Random; 29 | import org.junit.jupiter.api.Test; 30 | 31 | class TestMapIterator { 32 | @Test 33 | void testMapIterator() { 34 | final Random random = new Random(); 35 | 36 | for (int i = 0; i < 60 * 1000; i += 400 + random.nextInt(400)) { 37 | final var bt = TrieMap.create(); 38 | for (int j = 0; j < i; j++) { 39 | assertNull(bt.put(Integer.valueOf(j), Integer.valueOf(j))); 40 | } 41 | int count = 0; 42 | final var set = new HashSet(); 43 | for (var e : bt.entrySet()) { 44 | set.add(e.getKey()); 45 | count++; 46 | } 47 | for (var j : set) { 48 | assertTrue(bt.containsKey(j)); 49 | } 50 | for (var j : bt.keySet()) { 51 | assertTrue(set.contains(j)); 52 | } 53 | 54 | assertEquals(i, count); 55 | assertEquals(i, bt.size()); 56 | 57 | for (var e : bt.entrySet()) { 58 | assertSame(e.getValue(), bt.get(e.getKey())); 59 | e.setValue(e.getValue() + 1); 60 | assertEquals((Object)e.getValue(), e.getKey() + 1); 61 | assertEquals(e.getValue(), bt.get(e.getKey())); 62 | e.setValue(e.getValue() - 1); 63 | } 64 | 65 | final var it = bt.keySet().iterator(); 66 | while (it.hasNext()) { 67 | final var k = it.next(); 68 | assertTrue(bt.containsKey(k)); 69 | it.remove(); 70 | assertFalse(bt.containsKey(k)); 71 | } 72 | 73 | assertEquals(0, bt.size()); 74 | assertTrue(bt.isEmpty()); 75 | } 76 | } 77 | 78 | @Test 79 | void testMapImmutableIterator() { 80 | final Random random = new Random(); 81 | 82 | for (int i = 0; i < 60 * 1000; i += 400 + random.nextInt(400)) { 83 | final var bt = TrieMap.create(); 84 | for (int j = 0; j < i; j++) { 85 | assertNull(bt.put(Integer.valueOf(j), Integer.valueOf(j))); 86 | } 87 | int count = 0; 88 | final var set = new HashSet(); 89 | for (var e : bt.entrySet()) { 90 | set.add(e.getKey()); 91 | count++; 92 | } 93 | for (var j : set) { 94 | assertTrue(bt.containsKey(j)); 95 | } 96 | for (var j : bt.keySet()) { 97 | assertTrue(set.contains(j)); 98 | } 99 | 100 | assertEquals(i, count); 101 | assertEquals(i, bt.size()); 102 | } 103 | } 104 | 105 | @Test 106 | void testEmptyIterator() { 107 | failAdvance(TrieMap.create().iterator()); 108 | } 109 | 110 | @Test 111 | void testEmptyReadOnlyIterator() { 112 | failAdvance(TrieMap.create().immutableIterator()); 113 | } 114 | 115 | @Test 116 | void testEmptyReadOnlySnapshotIterator() { 117 | failAdvance(TrieMap.create().immutableSnapshot().iterator()); 118 | } 119 | 120 | private static void failAdvance(final Iterator it) { 121 | assertFalse(it.hasNext()); 122 | assertThrows(NoSuchElementException.class, it::next); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestMultiThreadAddDelete.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertFalse; 20 | import static org.junit.jupiter.api.Assertions.assertTrue; 21 | 22 | import java.util.concurrent.Executors; 23 | import java.util.concurrent.TimeUnit; 24 | import org.junit.jupiter.api.Test; 25 | import org.slf4j.Logger; 26 | import org.slf4j.LoggerFactory; 27 | 28 | class TestMultiThreadAddDelete { 29 | private static final Logger LOG = LoggerFactory.getLogger(TestMultiThreadAddDelete.class); 30 | private static final int RETRIES = 1; 31 | private static final int N_THREADS = 7; 32 | private static final int COUNT = 50 * 1000; 33 | 34 | @Test 35 | void testMultiThreadAddDelete() throws InterruptedException { 36 | for (int j = 0; j < RETRIES; j++) { 37 | final var bt = TrieMap.create(); 38 | 39 | { 40 | final var es = Executors.newFixedThreadPool(N_THREADS); 41 | for (int i = 0; i < N_THREADS; i++) { 42 | final int threadNo = i; 43 | es.execute(() -> { 44 | for (int k = 0; k < COUNT; k++) { 45 | if (k % N_THREADS == threadNo) { 46 | bt.put(Integer.valueOf(k), Integer.valueOf(k)); 47 | } 48 | } 49 | }); 50 | } 51 | es.shutdown(); 52 | es.awaitTermination(5, TimeUnit.MINUTES); 53 | } 54 | 55 | assertEquals(COUNT, bt.size()); 56 | assertFalse(bt.isEmpty()); 57 | 58 | { 59 | final var es = Executors.newFixedThreadPool(N_THREADS); 60 | for (int i = 0; i < N_THREADS; i++) { 61 | final int threadNo = i; 62 | es.execute(() -> { 63 | for (int k = 0; k < COUNT; k++) { 64 | if (k % N_THREADS == threadNo) { 65 | bt.remove(Integer.valueOf(k)); 66 | } 67 | } 68 | }); 69 | } 70 | es.shutdown(); 71 | es.awaitTermination(5, TimeUnit.MINUTES); 72 | } 73 | 74 | 75 | assertEquals(0, bt.size()); 76 | assertTrue(bt.isEmpty()); 77 | 78 | { 79 | final var es = Executors.newFixedThreadPool(N_THREADS); 80 | for (int i = 0; i < N_THREADS; i++) { 81 | final int threadNo = i; 82 | es.execute(() -> { 83 | for (int j1 = 0; j1 < COUNT; j1++) { 84 | if (j1 % N_THREADS == threadNo) { 85 | bt.put(Integer.valueOf(j1), Integer.valueOf(j1)); 86 | if (!bt.containsKey(Integer.valueOf(j1))) { 87 | LOG.error("Key {} not present", j1); 88 | } 89 | bt.remove(Integer.valueOf(j1)); 90 | if (bt.containsKey(Integer.valueOf(j1))) { 91 | LOG.error("Key {} is still present", j1); 92 | } 93 | } 94 | } 95 | }); 96 | } 97 | es.shutdown(); 98 | es.awaitTermination(5, TimeUnit.MINUTES); 99 | } 100 | 101 | assertEquals(0, bt.size()); 102 | assertTrue(bt.isEmpty()); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestMultiThreadInserts.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | 20 | import java.util.concurrent.Executors; 21 | import java.util.concurrent.TimeUnit; 22 | import org.junit.jupiter.api.Test; 23 | 24 | class TestMultiThreadInserts { 25 | @Test 26 | void testMultiThreadInserts() throws InterruptedException { 27 | final int nThreads = 2; 28 | final var es = Executors.newFixedThreadPool(nThreads); 29 | final var bt = TrieMap.create(); 30 | for (int i = 0; i < nThreads; i++) { 31 | final int threadNo = i; 32 | es.execute(() -> { 33 | for (int j = 0; j < 500 * 1000; j++) { 34 | if (j % nThreads == threadNo) { 35 | bt.put(Integer.valueOf(j), Integer.valueOf(j)); 36 | } 37 | } 38 | }); 39 | } 40 | 41 | es.shutdown(); 42 | es.awaitTermination(5, TimeUnit.MINUTES); 43 | 44 | for (int j = 0; j < 500 * 1000; j++) { 45 | assertEquals(Integer.valueOf(j), bt.get(Integer.valueOf(j))); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestMultiThreadMapIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertTrue; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | import java.util.concurrent.ConcurrentHashMap; 24 | import java.util.concurrent.Executors; 25 | import java.util.concurrent.TimeUnit; 26 | import org.junit.jupiter.api.Test; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | class TestMultiThreadMapIterator { 31 | private static final Logger LOG = LoggerFactory.getLogger(TestMultiThreadMapIterator.class); 32 | private static final int NTHREADS = 7; 33 | 34 | @Test 35 | void testMultiThreadMapIterator() throws InterruptedException { 36 | final var bt = TrieMap.create(); 37 | for (int j = 0; j < 50 * 1000; j++) { 38 | for (var o : getObjects(j)) { 39 | bt.put(o, o); 40 | } 41 | } 42 | 43 | LOG.debug("Size of initialized map is {}", bt.size()); 44 | int count = 0; 45 | { 46 | final var es = Executors.newFixedThreadPool(NTHREADS); 47 | for (int i = 0; i < NTHREADS; i++) { 48 | final int threadNo = i; 49 | es.execute(() -> { 50 | for (var e : bt.entrySet()) { 51 | if (accepts(threadNo, NTHREADS, e.getKey())) { 52 | String newValue = "TEST:" + threadNo; 53 | e.setValue(newValue); 54 | } 55 | } 56 | }); 57 | } 58 | 59 | es.shutdown(); 60 | es.awaitTermination(5, TimeUnit.MINUTES); 61 | } 62 | 63 | count = 0; 64 | for (var kv : bt.entrySet()) { 65 | assertTrue(kv.getValue() instanceof String); 66 | count++; 67 | } 68 | assertEquals(50000 + 2000 + 1000 + 100, count); 69 | 70 | final var removed = new ConcurrentHashMap<>(); 71 | { 72 | final var es = Executors.newFixedThreadPool(NTHREADS); 73 | for (int i = 0; i < NTHREADS; i++) { 74 | final int threadNo = i; 75 | es.execute(() -> { 76 | for (var it = bt.entrySet().iterator(); it.hasNext();) { 77 | final var e = it.next(); 78 | Object key = e.getKey(); 79 | if (accepts(threadNo, NTHREADS, key)) { 80 | if (null == bt.get(key)) { 81 | LOG.error("Key {} is not present", key); 82 | } 83 | it.remove(); 84 | if (null != bt.get(key)) { 85 | LOG.error("Key {} is still present", key); 86 | } 87 | removed.put(key, key); 88 | } 89 | } 90 | }); 91 | } 92 | 93 | es.shutdown(); 94 | es.awaitTermination(5, TimeUnit.MINUTES); 95 | } 96 | 97 | count = 0; 98 | for (var value : bt.keySet()) { 99 | value.toString(); 100 | count++; 101 | } 102 | for (var o : bt.keySet()) { 103 | if (!removed.contains(bt.get(o))) { 104 | LOG.error("Not removed: {}", o); 105 | } 106 | } 107 | assertEquals(0, count); 108 | assertEquals(0, bt.size()); 109 | assertTrue(bt.isEmpty()); 110 | } 111 | 112 | protected static boolean accepts(final int threadNo, final int nrThreads, final Object key) { 113 | final int val = getKeyValue(key); 114 | return val >= 0 ? val % nrThreads == threadNo : false; 115 | } 116 | 117 | private static int getKeyValue(final Object key) { 118 | if (key instanceof Integer integer) { 119 | return integer; 120 | } else if (key instanceof Character character) { 121 | return Math.abs(Character.getNumericValue(character) + 1); 122 | } else if (key instanceof Short shortValue) { 123 | return shortValue.intValue() + 2; 124 | } else if (key instanceof Byte byteValue) { 125 | return byteValue.intValue() + 3; 126 | } else { 127 | return -1; 128 | } 129 | } 130 | 131 | static List getObjects(final int value) { 132 | final var results = new ArrayList<>(4); 133 | results.add(Integer.valueOf(value)); 134 | if (value < 2000) { 135 | results.add(Character.valueOf((char) value)); 136 | } 137 | if (value < 1000) { 138 | results.add(Short.valueOf((short) value)); 139 | } 140 | if (value < 100) { 141 | results.add(Byte.valueOf((byte) value)); 142 | } 143 | 144 | return results; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestReadOnlyAndUpdatableIterators.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertNull; 20 | import static org.junit.jupiter.api.Assertions.assertThrows; 21 | 22 | import java.util.Iterator; 23 | import java.util.Map.Entry; 24 | import org.junit.jupiter.api.BeforeEach; 25 | import org.junit.jupiter.api.Test; 26 | 27 | /** 28 | * Test that read-only iterators do not allow for any updates. 29 | * Test that non read-only iterators allow for updates. 30 | */ 31 | class TestReadOnlyAndUpdatableIterators { 32 | private static final int MAP_SIZE = 200; 33 | 34 | private TrieMap bt; 35 | 36 | @BeforeEach 37 | void setUp() { 38 | bt = TrieMap.create(); 39 | for (int j = 0; j < MAP_SIZE; j++) { 40 | assertNull(bt.put(Integer.valueOf(j), Integer.valueOf(j))); 41 | } 42 | } 43 | 44 | private static void trySet(final Iterator> it) { 45 | final var entry = it.next(); 46 | assertThrows(UnsupportedOperationException.class, () -> entry.setValue(0)); 47 | } 48 | 49 | private static void tryRemove(final Iterator it) { 50 | it.next(); 51 | assertThrows(UnsupportedOperationException.class, () -> it.remove()); 52 | } 53 | 54 | @Test 55 | void testReadOnlyIteratorSet() { 56 | trySet(bt.immutableIterator()); 57 | } 58 | 59 | @Test 60 | void testReadOnlyIteratorRemove() { 61 | tryRemove(bt.immutableIterator()); 62 | } 63 | 64 | @Test 65 | void testReadOnlySnapshotReadOnlyIteratorSet() { 66 | trySet(bt.immutableSnapshot().immutableIterator()); 67 | } 68 | 69 | @Test 70 | void testReadOnlySnapshotReadOnlyIteratorRemove() { 71 | tryRemove(bt.immutableSnapshot().immutableIterator()); 72 | } 73 | 74 | @Test 75 | void testReadOnlySnapshotIteratorSet() { 76 | trySet(bt.immutableSnapshot().iterator()); 77 | } 78 | 79 | @Test 80 | void testReadOnlySnapshotIteratorRemove() { 81 | tryRemove(bt.immutableSnapshot().iterator()); 82 | } 83 | 84 | @Test 85 | void testIterator() { 86 | var it = bt.iterator(); 87 | it.next().setValue(0); 88 | it.remove(); 89 | 90 | // All changes are done on the original map 91 | assertEquals(MAP_SIZE - 1, bt.size()); 92 | } 93 | 94 | @Test 95 | void testSnapshotIterator() { 96 | var snapshot = bt.mutableSnapshot(); 97 | var it = snapshot.iterator(); 98 | it.next().setValue(0); 99 | it.remove(); 100 | 101 | // All changes are done on the snapshot, not on the original map 102 | // Map size should remain unchanged 103 | assertEquals(MAP_SIZE, bt.size()); 104 | // snapshot size was changed 105 | assertEquals(MAP_SIZE - 1, snapshot.size()); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/TestSerialization.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertInstanceOf; 20 | 21 | import java.io.ByteArrayInputStream; 22 | import java.io.ByteArrayOutputStream; 23 | import java.io.IOException; 24 | import java.io.ObjectInputStream; 25 | import java.io.ObjectOutputStream; 26 | import org.junit.jupiter.api.Test; 27 | 28 | class TestSerialization { 29 | @Test 30 | void testSerialization() throws IOException, ClassNotFoundException { 31 | var map = TrieMap.create(); 32 | 33 | map.put("dude-0", "tom"); 34 | map.put("dude-1", "john"); 35 | map.put("dude-3", "ravi"); 36 | map.put("dude-4", "alex"); 37 | 38 | var expected = map.immutableSnapshot(); 39 | 40 | final var bos = new ByteArrayOutputStream(); 41 | try (var oos = new ObjectOutputStream(bos)) { 42 | oos.writeObject(expected); 43 | } 44 | 45 | final TrieMap actual; 46 | try (var ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) { 47 | actual = assertInstanceOf(TrieMap.class, ois.readObject()); 48 | } 49 | 50 | assertEquals(expected, actual); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/VerifyExceptionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertSame; 19 | import static org.junit.jupiter.api.Assertions.assertThrows; 20 | 21 | import org.junit.jupiter.api.Test; 22 | 23 | class VerifyExceptionTest { 24 | @Test 25 | void testThrowIfNullSimple() { 26 | assertThrows(VerifyException.class, () -> VerifyException.throwIfNull(null)); 27 | } 28 | 29 | @Test 30 | void testThrowIfNullSame() { 31 | final Object obj = new Object(); 32 | assertSame(obj, VerifyException.throwIfNull(obj)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /triemap/src/test/java/tech/pantheon/triemap/ZeroHashInt.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2016 PANTHEON.tech, s.r.o. and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tech.pantheon.triemap; 17 | 18 | import java.util.StringJoiner; 19 | 20 | /** 21 | * Utility key/value class which attacks the hasing function, causing all objects to be put into a single bucket. 22 | * 23 | * @author Robert Varga 24 | */ 25 | final class ZeroHashInt { 26 | private final int value; 27 | 28 | ZeroHashInt(final int value) { 29 | this.value = value; 30 | } 31 | 32 | @Override 33 | public int hashCode() { 34 | return 0; 35 | } 36 | 37 | @Override 38 | public boolean equals(final Object obj) { 39 | return obj instanceof ZeroHashInt other && value == other.value; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return new StringJoiner(", ", ZeroHashInt.class.getSimpleName() + "{", "}") 45 | .add("value=" + value) 46 | .add("identity=" + System.identityHashCode(ZeroHashInt.class)) 47 | .toString(); 48 | } 49 | } 50 | --------------------------------------------------------------------------------