├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── android-performance ├── README.md ├── build.gradle └── src │ ├── androidTest │ └── java │ │ └── org │ │ └── greenrobot │ │ └── essentials │ │ └── androidperf │ │ ├── AbstractAndroidBenchmark.java │ │ ├── LongHashMapAndroidBenchmark.java │ │ ├── LongHashSetAndroidBenchmark.java │ │ ├── PipelineStreamAndroidBenchmark.java │ │ └── StringSplitAndroidBenchmark.java │ └── main │ └── AndroidManifest.xml ├── android-test ├── README.md ├── build.gradle └── src │ ├── androidTest │ └── java │ │ └── org │ │ └── greenrobot │ │ └── essentials │ │ ├── AndroidDateUtilsTest.java │ │ ├── AndroidLimitedInputStreamTest.java │ │ ├── AndroidObjectCacheTest.java │ │ ├── AndroidPrimitiveArrayUtilsTest.java │ │ ├── AndroidStringUtilsTest.java │ │ ├── collections │ │ ├── AndroidLongHashMapTest.java │ │ ├── AndroidLongHashSetTest.java │ │ └── AndroidMultimapTest.java │ │ ├── hash │ │ ├── AndroidChecksumStreamTest.java │ │ ├── AndroidFNV32Test.java │ │ ├── AndroidFNV64Test.java │ │ ├── AndroidMurmur3ASpeedTest.java │ │ ├── AndroidMurmur3ATest.java │ │ ├── AndroidMurmur3FTest.java │ │ └── AndroidPrimitiveDataChecksumTest.java │ │ └── io │ │ ├── AndroidCircularByteBufferTest.java │ │ ├── AndroidFileUtilsTest.java │ │ └── AndroidPipelineOutputStreamTest.java │ └── main │ └── AndroidManifest.xml ├── build-common ├── .gitignore ├── checkstyle-rules │ ├── .gitignore │ ├── pom.xml │ └── src │ │ └── main │ │ └── resources │ │ └── checkstyle-greenrobot.xml ├── parent-pom-with-checks.xml ├── parent-pom.xml └── pom.xml ├── build.gradle ├── config └── checkstyle │ └── checkstyle-greenrobot.xml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── ide └── intellij-codestyle.jar ├── java-essentials-performance ├── README.md ├── benchmarks.gradle ├── build.gradle └── src │ └── main │ └── java │ └── org │ └── greenrobot │ └── essentials │ └── javaperf │ ├── BenchmarkRunner.java │ ├── LongHashMapBenchmark.java │ ├── LongHashSetBenchmark.java │ ├── PipelineStreamBenchmark.java │ ├── StringHexBenchmark.java │ └── StringSplitBenchmark.java ├── java-essentials ├── build.gradle ├── checkstyle-suppressions.xml ├── findbugs-exclude.xml ├── hash-functions-results │ └── i7-3720QM-2_6GHz-Java7u72.txt ├── pom.xml └── src │ ├── main │ └── java │ │ └── org │ │ └── greenrobot │ │ └── essentials │ │ ├── DateUtils.java │ │ ├── ObjectCache.java │ │ ├── PrimitiveArrayUtils.java │ │ ├── StringUtils.java │ │ ├── collections │ │ ├── AbstractMultimap.java │ │ ├── LongHashMap.java │ │ ├── LongHashSet.java │ │ ├── Multimap.java │ │ └── MultimapSet.java │ │ ├── hash │ │ ├── Checksum128.java │ │ ├── FNV32.java │ │ ├── FNV64.java │ │ ├── Murmur3A.java │ │ ├── Murmur3F.java │ │ └── PrimitiveDataChecksum.java │ │ └── io │ │ ├── CircularByteBuffer.java │ │ ├── FileUtils.java │ │ ├── IoUtils.java │ │ ├── LimitedInputStream.java │ │ ├── PipelineOutputStream.java │ │ └── RepeaterInputStream.java │ └── test │ └── java │ └── org │ └── greenrobot │ └── essentials │ ├── DateUtilsTest.java │ ├── LimitedInputStreamTest.java │ ├── ObjectCacheTest.java │ ├── PrimitiveArrayUtilsTest.java │ ├── StringUtilsJvmTest.java │ ├── StringUtilsTest.java │ ├── collections │ ├── LongHashMapTest.java │ ├── LongHashSetTest.java │ └── MultimapTest.java │ ├── hash │ ├── AbstractAllChecksumTest.java │ ├── AbstractChecksumTest.java │ ├── ChecksumStreamTest.java │ ├── FNV32Test.java │ ├── FNV64Test.java │ ├── HashCollider.java │ ├── Murmur3ASpeedTest.java │ ├── Murmur3ATest.java │ ├── Murmur3FTest.java │ ├── PrimitiveDataChecksumTest.java │ └── otherhashes │ │ ├── MessageDigestChecksum.java │ │ ├── Murmur2Checksum.java │ │ ├── Murmur2bChecksum.java │ │ ├── Murmur3aGuavaChecksum.java │ │ ├── Murmur3fGuavaChecksum.java │ │ ├── MurmurHash2.java │ │ ├── MurmurHash2b.java │ │ ├── MurmurHash3Yonik.java │ │ └── MurmurHash3YonikChecksum.java │ └── io │ ├── CircularByteBufferTest.java │ ├── FileUtilsTest.java │ └── PipelineOutputStreamTest.java ├── javadoc-style ├── background.gif └── stylesheet.css ├── pom.xml ├── settings.gradle └── web-resources ├── hash-functions-benchmark.pdf ├── hash-functions-bit-distribution-quality.png ├── hash-functions-collisions.png └── hash-functions-performance.png /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .idea/ 3 | .project 4 | .settings/ 5 | bin/ 6 | build/ 7 | target/ 8 | *.iml 9 | .gradle/ 10 | 11 | # Local configuration files (sdk path, etc) 12 | local.properties 13 | gradle.properties 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: android 3 | jdk: 4 | # https://docs.travis-ci.com/user/languages/java/#testing-against-multiple-jdks 5 | - openjdk8 6 | - oraclejdk8 7 | - oraclejdk11 8 | - openjdk11 9 | 10 | # http://docs.travis-ci.com/user/languages/android/ 11 | android: 12 | components: 13 | - build-tools-29.0.2 14 | - android-29 15 | 16 | before_install: 17 | - mkdir "$ANDROID_HOME/licenses" || true 18 | - echo -e "24333f8a63b6825ea9c5514f83c2829b004d1fee" > "$ANDROID_HOME/licenses/android-sdk-license" 19 | 20 | before_script: 21 | - chmod +x gradlew 22 | # - echo no | android create avd --force -n test -t android-10 --abi armeabi 23 | # - emulator -avd test -no-skin -no-audio -no-window & 24 | # - android-wait-for-emulator 25 | # - adb shell input keyevent 82 & 26 | 27 | # Currently connectedCheck fails, so don't run unit test on Emulator for now. Issue: 28 | # com.android.builder.testing.ConnectedDevice > hasTests[test(AVD) - 2.3.3] FAILED 29 | # No tests found. 30 | 31 | script: 32 | - TERM=dumb ./gradlew check 33 | -------------------------------------------------------------------------------- /android-performance/README.md: -------------------------------------------------------------------------------- 1 | Essentials Performance Benchmarks on Android 2 | ============================================ 3 | 4 | Benchmarks implemented as unit tests for Android. 5 | 6 | It's possible to run them in Android Studio/IntelliJ Idea (open statistics window in unit tests panel to see timings). 7 | 8 | If you would like to create a beautiful table, you can run benchmarks and generate complete TSV-report, using: 9 | 10 | ./gradlew android-performance:measurePerformance 11 | 12 | The report can be found then in **android-performance/build/reports/android-performance.tsv** file. It can be imported into Google Spread Sheets, for example. -------------------------------------------------------------------------------- /android-performance/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:4.0.2' // 4.1 not yet supported by IntelliJ 9 | } 10 | } 11 | 12 | apply plugin: 'com.android.application' 13 | 14 | repositories { 15 | google() 16 | jcenter() 17 | } 18 | 19 | dependencies { 20 | implementation project(':java-essentials-performance') 21 | implementation 'com.android.support.test:runner:1.0.2' 22 | implementation 'com.android.support.test:rules:1.0.2' 23 | implementation "com.google.guava:guava:$guavaVersion-android" 24 | } 25 | 26 | android { 27 | compileSdkVersion 29 // When updating, don't forget to adjust .travis.yml 28 | 29 | compileOptions { 30 | sourceCompatibility = JavaVersion.VERSION_1_7 31 | targetCompatibility = JavaVersion.VERSION_1_7 32 | } 33 | 34 | defaultConfig { 35 | minSdkVersion 9 36 | versionCode 1 37 | versionName "1.0" 38 | 39 | testApplicationId "de.greenrobot.essentials.androidperformance" 40 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 41 | } 42 | 43 | lintOptions { 44 | abortOnError false 45 | } 46 | } 47 | 48 | ext.reportsDir = file("$buildDir/outputs/androidTest-results/connected") 49 | ext.outputReportFile = reportFile(file("$buildDir/reports"), "android-performance-%index%.tsv") 50 | 51 | task generateReport { 52 | inputs.dir reportsDir 53 | 54 | doLast { 55 | // take first file from reports directory 56 | def xmlFile = fileTree(dir: reportsDir, include: '*.xml').first() 57 | def suite = new XmlSlurper().parse(xmlFile) 58 | def testData = { String caseName -> 59 | def m = caseName =~ /performance\[(.+)\]/ 60 | if (m) { 61 | def (name, impl, runs) = m[0][1].split("[/:]") + [null] 62 | runs = runs ? runs.replaceAll("[^0-9]", "") as int : 1 63 | [name: name, impl: impl, runs: runs] 64 | } else { 65 | System.err.println "Can't parse data from test name '$caseName'" 66 | [:] 67 | } 68 | 69 | } 70 | def results = suite.testcase 71 | .collect { testData(it.@name.toString()) + [time: it.@time.toString() as float] } 72 | .findAll { it.name != null } 73 | .collect { 74 | [name: it.name, impl: it.impl, time: it.time * 1000 / it.runs] 75 | } 76 | 77 | def allImpl = results*.impl.unique() 78 | def header = ([""] + allImpl).join('\t') + "\n" 79 | def tsv = header + results.groupBy { it.name } 80 | .collect { name, value -> 81 | def times = value.collectEntries { [(it.impl): it.time] } 82 | [name] + allImpl.collect { times[it].trunc(3).toString().replaceAll('[.]', ',') } 83 | } 84 | .collect { it.join('\t') } 85 | .join('\n') 86 | 87 | outputReportFile.write tsv 88 | } 89 | } 90 | 91 | task measurePerformance(dependsOn: ['connectedDebugAndroidTest', 'generateReport']) -------------------------------------------------------------------------------- /android-performance/src/androidTest/java/org/greenrobot/essentials/androidperf/AbstractAndroidBenchmark.java: -------------------------------------------------------------------------------- 1 | package org.greenrobot.essentials.androidperf; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.junit.runners.Parameterized; 6 | 7 | @RunWith(Parameterized.class) 8 | public abstract class AbstractAndroidBenchmark { 9 | @Parameterized.Parameter(0) 10 | public Runnable runnable; 11 | 12 | @Parameterized.Parameter(1) 13 | public int runCount; 14 | 15 | @Test 16 | public void performance() throws Exception { 17 | final Runnable runnable = this.runnable; 18 | final int runCount = this.runCount; 19 | for (int i = 0; i < runCount; i++) { 20 | runnable.run(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /android-performance/src/androidTest/java/org/greenrobot/essentials/androidperf/LongHashMapAndroidBenchmark.java: -------------------------------------------------------------------------------- 1 | package org.greenrobot.essentials.androidperf; 2 | 3 | import org.greenrobot.essentials.javaperf.LongHashMapBenchmark; 4 | import org.junit.runners.Parameterized; 5 | 6 | import java.util.Arrays; 7 | import java.util.Collection; 8 | 9 | public class LongHashMapAndroidBenchmark extends AbstractAndroidBenchmark { 10 | @Parameterized.Parameters(name = "{0}:{1}") 11 | public static Collection parameters() { 12 | return Arrays.asList(new Object[][]{ 13 | {new LongHashMapBenchmark.StdImpl(), 10}, 14 | {new LongHashMapBenchmark.LibImpl(), 10}, 15 | {new LongHashMapBenchmark.PreallocStdImpl(), 10}, 16 | {new LongHashMapBenchmark.PreallocLibImpl(), 10}, 17 | }); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /android-performance/src/androidTest/java/org/greenrobot/essentials/androidperf/LongHashSetAndroidBenchmark.java: -------------------------------------------------------------------------------- 1 | package org.greenrobot.essentials.androidperf; 2 | 3 | import org.greenrobot.essentials.javaperf.LongHashSetBenchmark; 4 | import org.junit.runners.Parameterized; 5 | 6 | import java.util.Arrays; 7 | import java.util.Collection; 8 | 9 | public class LongHashSetAndroidBenchmark extends AbstractAndroidBenchmark { 10 | @Parameterized.Parameters(name = "{0}:{1}") 11 | public static Collection parameters() { 12 | return Arrays.asList(new Object[][]{ 13 | {new LongHashSetBenchmark.StdImpl(), 10}, 14 | {new LongHashSetBenchmark.LibImpl(), 10}, 15 | {new LongHashSetBenchmark.PreallocStdImpl(), 10}, 16 | {new LongHashSetBenchmark.PreallocLibImpl(), 10}, 17 | }); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /android-performance/src/androidTest/java/org/greenrobot/essentials/androidperf/PipelineStreamAndroidBenchmark.java: -------------------------------------------------------------------------------- 1 | package org.greenrobot.essentials.androidperf; 2 | 3 | import org.greenrobot.essentials.javaperf.PipelineStreamBenchmark; 4 | import org.junit.runners.Parameterized; 5 | 6 | import java.util.Arrays; 7 | import java.util.Collection; 8 | 9 | public class PipelineStreamAndroidBenchmark extends AbstractAndroidBenchmark { 10 | public static final int STREAM_LENGTH = 100 * 1024; // 100KB 11 | 12 | @Parameterized.Parameters(name = "{0}:{1}") 13 | public static Collection parameters() { 14 | return Arrays.asList(new Object[][]{ 15 | {new PipelineStreamBenchmark.StdImpl(STREAM_LENGTH), 10}, 16 | {new PipelineStreamBenchmark.LibImpl(STREAM_LENGTH), 1000}, 17 | }); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /android-performance/src/androidTest/java/org/greenrobot/essentials/androidperf/StringSplitAndroidBenchmark.java: -------------------------------------------------------------------------------- 1 | package org.greenrobot.essentials.androidperf; 2 | 3 | import org.greenrobot.essentials.javaperf.StringSplitBenchmark; 4 | import org.junit.runners.Parameterized; 5 | 6 | import java.util.Arrays; 7 | import java.util.Collection; 8 | 9 | public class StringSplitAndroidBenchmark extends AbstractAndroidBenchmark { 10 | @Parameterized.Parameters(name = "{0}") 11 | public static Collection parameters() { 12 | return Arrays.asList(new Object[][]{ 13 | {new StringSplitBenchmark.TinyStdImpl(), 100}, 14 | {new StringSplitBenchmark.TinyLibImpl(), 100}, 15 | {new StringSplitBenchmark.ShortStdImpl(), 100}, 16 | {new StringSplitBenchmark.ShortLibImpl(), 100}, 17 | {new StringSplitBenchmark.LongStdImpl(), 100}, 18 | {new StringSplitBenchmark.LongLibImpl(), 100}, 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /android-performance/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android-test/README.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | Ensure a running emulator or device. Then just run the gradle task `connectedDebugAndroidTest`. 3 | 4 | To run within IntelliJ, make sure to set an Android SDK as the Module SDK for the `android-test` module. Then create a 5 | AndroidTests run configuration. Select the `android-test` module, optionally the class and method to test. Set the test 6 | runner to `android.support.test.runner.AndroidJUnitRunner`. -------------------------------------------------------------------------------- /android-test/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:4.0.2' // 4.1 not yet supported by IntelliJ 9 | } 10 | } 11 | 12 | apply plugin: 'com.android.application' 13 | 14 | repositories { 15 | google() 16 | jcenter() 17 | } 18 | 19 | dependencies { 20 | implementation project(':java-essentials') 21 | androidTestImplementation project(':java-essentials') 22 | androidTestImplementation project(path: ':java-essentials', configuration: 'tests') 23 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 24 | androidTestImplementation 'com.android.support.test:rules:1.0.2' 25 | androidTestImplementation "com.google.guava:guava:$guavaVersion-android" 26 | } 27 | 28 | android { 29 | compileSdkVersion 29 // When updating, don't forget to adjust .travis.yml 30 | 31 | compileOptions { 32 | sourceCompatibility = JavaVersion.VERSION_1_7 33 | targetCompatibility = JavaVersion.VERSION_1_7 34 | } 35 | 36 | defaultConfig { 37 | minSdkVersion 9 38 | versionCode 1 39 | versionName "1.0" 40 | 41 | testApplicationId "de.greenrobot.essentials.test" 42 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 43 | } 44 | 45 | lintOptions { 46 | abortOnError false 47 | } 48 | } 49 | 50 | /** Run this task to regenerate tests classes in this project to match tests in the main project */ 51 | task generateTestsFromJavaProject { 52 | doLast { 53 | def testsSrcDir = findProject(':java-essentials').file('src/test/java') 54 | def tests = fileTree(dir: testsSrcDir, include: '**/*Test.java') 55 | def FILTER = { file -> 56 | !file.name.contains("Abstract") 57 | } 58 | tests.filter(FILTER).each { sourceFile -> 59 | def className = sourceFile.name[0.. 2 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /build-common/.gitignore: -------------------------------------------------------------------------------- 1 | /.settings 2 | /.project 3 | -------------------------------------------------------------------------------- /build-common/checkstyle-rules/.gitignore: -------------------------------------------------------------------------------- 1 | /.settings 2 | /target 3 | /.classpath 4 | /.project 5 | -------------------------------------------------------------------------------- /build-common/checkstyle-rules/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | org.greenrobot 6 | checkstyle-rules 7 | 3.0.0-SNAPSHOT 8 | 9 | 10 | 11 | 12 | org.apache.maven.wagon 13 | wagon-webdav-jackrabbit 14 | 2.10 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | deploy-to-mvn-central 30 | 31 | 32 | 33 | 34 | 35 | ossrh 36 | https://oss.sonatype.org/content/repositories/snapshots 37 | 38 | 39 | ossrh 40 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 41 | 42 | 43 | 44 | 45 | 46 | org.apache.maven.plugins 47 | maven-gpg-plugin 48 | 1.6 49 | 50 | 51 | sign-artifacts 52 | verify 53 | 54 | sign 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | greenrobot-common's Checkstyle ruleset 66 | Checkstyle configuration file 67 | https://github.com/greenrobot/essentials 68 | 69 | 70 | The Apache License, Version 2.0 71 | http://www.apache.org/licenses/LICENSE-2.0.txt 72 | 73 | 74 | 75 | 76 | greenrobot 77 | greenrobot 78 | http://greenrobot.org 79 | 80 | 81 | 82 | scm:git@github.com:greenrobot/essentials.git 83 | scm:git@github.com:greenrobot/essentials.git 84 | https://github.com/greenrobot/essentials 85 | 86 | -------------------------------------------------------------------------------- /build-common/checkstyle-rules/src/main/resources/checkstyle-greenrobot.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 105 | 107 | 108 | 109 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /build-common/parent-pom-with-checks.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | org.greenrobot 6 | parent-pom-with-checks 7 | 3.0.0-SNAPSHOT 8 | pom 9 | 10 | 11 | org.greenrobot 12 | parent-pom 13 | 3.0.0-SNAPSHOT 14 | parent-pom.xml 15 | 16 | 17 | 18 | 3.0.1 19 | 2.17 20 | 6.15 21 | 3.0.0-SNAPSHOT 22 | 23 | 24 | 25 | 26 | 27 | 28 | org.apache.maven.plugins 29 | maven-checkstyle-plugin 30 | ${checkstyle-maven-version} 31 | 32 | 33 | org.greenrobot 34 | checkstyle-rules 35 | ${checkstyle-rules-version} 36 | 37 | 38 | com.puppycrawl.tools 39 | checkstyle 40 | ${checkstyle-version} 41 | 42 | 43 | 44 | checkstyle-greenrobot.xml 45 | true 46 | 10 47 | 48 | 49 | 50 | verify 51 | 52 | check 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.codehaus.mojo 61 | findbugs-maven-plugin 62 | ${findbugs-version} 63 | 64 | 65 | 67 | 14 68 | true 69 | 70 | 71 | 72 | 73 | check 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | org.apache.maven.plugins 86 | maven-checkstyle-plugin 87 | ${checkstyle-maven-version} 88 | 89 | checkstyle-greenrobot.xml 90 | 91 | 92 | 93 | 94 | 95 | org.codehaus.mojo 96 | findbugs-maven-plugin 97 | ${findbugs-version} 98 | 99 | 100 | 101 | 102 | greenrobot's parent POM with code checks 103 | Some defaults for Maven builds including checks for Java code (currently Checkstyle and FindBugs) 104 | 105 | -------------------------------------------------------------------------------- /build-common/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | org.greenrobot 6 | build-common-modules 7 | 3.0.0 8 | pom 9 | 10 | 11 | parent-pom.xml 12 | checkstyle-rules 13 | parent-pom-with-checks.xml 14 | 15 | 16 | 17 | 18 | 19 | org.apache.maven.plugins 20 | maven-deploy-plugin 21 | 2.8.2 22 | 23 | true 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | guavaVersion = '30.0' 4 | } 5 | } 6 | 7 | if (JavaVersion.current().isJava8Compatible()) { 8 | allprojects { 9 | tasks.withType(Javadoc) { 10 | options.addStringOption('Xdoclint:none', '-quiet') 11 | } 12 | } 13 | } 14 | 15 | 16 | wrapper { 17 | gradleVersion = '6.7' 18 | distributionType = org.gradle.api.tasks.wrapper.Wrapper.DistributionType.ALL 19 | } 20 | 21 | def reportFile(File parentDir, String templateName) { 22 | def file 23 | for (int i = 0; i < 1000; i++) { 24 | file = new File(parentDir, templateName.replaceAll('%index%', i as String)) 25 | if (!file.exists()) { 26 | return file; 27 | } 28 | } 29 | return file 30 | } -------------------------------------------------------------------------------- /config/checkstyle/checkstyle-greenrobot.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 112 | 114 | 115 | 116 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greenrobot/essentials/a129506eb62ea6d68d248b02e9617d925e510b76/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /ide/intellij-codestyle.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greenrobot/essentials/a129506eb62ea6d68d248b02e9617d925e510b76/ide/intellij-codestyle.jar -------------------------------------------------------------------------------- /java-essentials-performance/README.md: -------------------------------------------------------------------------------- 1 | Essentials Performance Benchmarks 2 | ================================= 3 | 4 | There are performance benchmarks for some of the library classes. 5 | 6 | Each benchmark is running in separate JVM with warming up. 7 | 8 | Running 9 | ------- 10 | 11 | To run all benchmarks from the root project use: 12 | 13 | ./gradlew java-essentials-performance:allBenchmarks 14 | 15 | Report 16 | ------ 17 | After running benchmarks you can find report in `java-essentials-performance/build/reports/performance.tsv` 18 | 19 | Configuration 20 | ------------- 21 | To change number of runs or time for warming up see [benchmarks.gradle](benchmarks.gradle). 22 | 23 | -------------------------------------------------------------------------------- /java-essentials-performance/benchmarks.gradle: -------------------------------------------------------------------------------- 1 | /** 2 | * Runs benchmark in separate JVM 3 | * Return output, as a string 4 | * */ 5 | def benchmark(className, runs, warmUpSeconds, useWallTime = false) { 6 | def nullOutputStream = new OutputStream() { 7 | @Override 8 | public void write(int b) {} 9 | } 10 | def input = new PipedInputStream() 11 | def pipe = new PipedOutputStream(input) 12 | project.javaexec { 13 | main = 'org.greenrobot.essentials.javaperf.BenchmarkRunner' 14 | classpath = project.sourceSets.main.runtimeClasspath 15 | args = ["org.greenrobot.essentials.javaperf.${className}", "$runs", "$warmUpSeconds", 16 | useWallTime ? "wall" : "nowall"] 17 | // This is odd 18 | errorOutput = System.out 19 | standardOutput = pipe 20 | } 21 | def line = input.readLines()[0] 22 | println line 23 | return line 24 | } 25 | 26 | ext.benchmarks = [ 27 | [tests: ['LongHashSetBenchmark$StdImpl', 'LongHashSetBenchmark$LibImpl'], runs: 1000, warmUp: 5], 28 | [tests: ['LongHashSetBenchmark$PreallocStdImpl', 'LongHashSetBenchmark$PreallocLibImpl'], runs: 1000, warmUp: 5], 29 | [tests: ['LongHashMapBenchmark$StdImpl', 'LongHashMapBenchmark$LibImpl'], runs: 1000, warmUp: 5], 30 | [tests: ['LongHashMapBenchmark$PreallocStdImpl', 'LongHashMapBenchmark$PreallocLibImpl'], runs: 1000, warmUp: 5], 31 | [tests: ['PipelineStreamBenchmark$StdImpl', 'PipelineStreamBenchmark$LibImpl'], runs: 100, warmUp: 10, forceWallTime: true], 32 | [tests: ['StringSplitBenchmark$TinyStdImpl', 'StringSplitBenchmark$TinyLibImpl'], runs: 10000, warmUp: 10], 33 | [tests: ['StringSplitBenchmark$ShortStdImpl', 'StringSplitBenchmark$ShortLibImpl'], runs: 10000, warmUp: 10], 34 | [tests: ['StringSplitBenchmark$LongStdImpl', 'StringSplitBenchmark$LongLibImpl'], runs: 10000, warmUp: 10], 35 | [tests: ['StringHexBenchmark$StdImpl', 'StringHexBenchmark$LibImpl'], runs: 1000, warmUp: 10] 36 | ] 37 | 38 | ext.reportFile = reportFile(file("$buildDir/reports"), "performance-%index%.tsv") 39 | 40 | // using cpu time for measurements instead of wall time can be specified with -DcpuTime 41 | ext.useWallTime = !project.hasProperty('cpuTime') 42 | 43 | /** Runs each of benchmarks in separate JVM with wall time */ 44 | task allBenchmarks { 45 | dependsOn compileJava 46 | inputs.dir(sourceSets.main.java.srcDirs.first()).skipWhenEmpty() 47 | 48 | doFirst { 49 | reportFile.parentFile.mkdirs() 50 | } 51 | 52 | doLast { 53 | println "Using ${useWallTime ? 'wall' : 'cpu'} time for benchmarks" 54 | def lineResults = benchmarks.collect { bm -> 55 | bm.tests.collect { benchmark(it, bm.runs, bm.warmUp, useWallTime || bm.forceWallTime) } 56 | }.flatten() 57 | 58 | def testData = { lineResult -> 59 | def m = lineResult =~ /([^\/]+)\/(\w+):([0-9.,]+)/ 60 | if (m) { 61 | [name: m[0][1], impl: m[0][2], time: m[0][3] as float] 62 | } else [:] 63 | } 64 | def results = lineResults.collect(testData).findAll{it.name != null} 65 | def allImpl = results*.impl.unique() 66 | def header = ([""] + allImpl).join('\t') + "\n" 67 | def tsv = header + 68 | results.groupBy { it.name } 69 | .collect { name, value -> 70 | def times = value.collectEntries { [(it.impl): it.time] } 71 | [name] + allImpl.collect { times[it].trunc(3).toString().replaceAll('[.]', ',') } 72 | } 73 | .collect { it.join('\t') } 74 | .join('\n') 75 | 76 | reportFile.write tsv 77 | } 78 | } -------------------------------------------------------------------------------- /java-essentials-performance/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | sourceCompatibility = 1.7 4 | targetCompatibility = 1.7 5 | 6 | repositories { 7 | mavenCentral() 8 | } 9 | 10 | dependencies { 11 | implementation project(':java-essentials') 12 | implementation "com.google.guava:guava:$guavaVersion-jre" 13 | } 14 | 15 | apply from: 'benchmarks.gradle' -------------------------------------------------------------------------------- /java-essentials-performance/src/main/java/org/greenrobot/essentials/javaperf/BenchmarkRunner.java: -------------------------------------------------------------------------------- 1 | package org.greenrobot.essentials.javaperf; 2 | 3 | import java.lang.management.ManagementFactory; 4 | import java.lang.management.ThreadMXBean; 5 | import java.util.ArrayList; 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | import static java.lang.System.err; 10 | import static java.lang.System.out; 11 | 12 | public class BenchmarkRunner { 13 | public static void main(String[] args) throws Exception { 14 | final String className = args[0]; 15 | final int times = Integer.parseInt(args[1]); 16 | final int warmUpSeconds = Integer.parseInt(args[2]); 17 | final boolean useWallTime = args[3].equals("wall"); 18 | 19 | final Runnable test = (Runnable) Class.forName(className).newInstance(); 20 | final double median; 21 | if (useWallTime) { 22 | median = runWallTime(test, times, warmUpSeconds); 23 | } else { 24 | median = run(test, times, warmUpSeconds); 25 | } 26 | out.println(test + ":" + median); 27 | } 28 | 29 | public static double run(Runnable test, int times, int warmUpSeconds) { 30 | final long warmUpNs = warmUpSeconds * 1_000_000_000L; 31 | final String testName = getTestName(test); 32 | err.println("Running " + testName + " " + times + " times on Java " + System.getProperty("java.version") + 33 | " (" + System.getProperty("java.vendor") + ")"); 34 | final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); 35 | err.println("Warming up for " + warmUpSeconds + " seconds..."); 36 | final long warmStartCpuTime = threadMXBean.getCurrentThreadCpuTime(); 37 | // warm up 38 | while (threadMXBean.getCurrentThreadCpuTime() < warmStartCpuTime + warmUpNs) { 39 | test.run(); 40 | } 41 | err.println("Run testing"); 42 | final List results = new ArrayList<>(times); 43 | for (int i = 0; i < times; i++) { 44 | final long startCpuTime = threadMXBean.getCurrentThreadCpuTime(); 45 | test.run(); 46 | final long endCpuTime = threadMXBean.getCurrentThreadCpuTime(); 47 | results.add((endCpuTime - startCpuTime) / 1e6); 48 | } 49 | 50 | return getMedian(results); 51 | } 52 | 53 | public static double runWallTime(Runnable test, int times, int warmUpSeconds) { 54 | final long warmUpNs = warmUpSeconds * 1_000_000_000L; 55 | final String testName = getTestName(test); 56 | err.println("Running " + testName + " " + times + " times on Java " + System.getProperty("java.version") + 57 | " (" + System.getProperty("java.vendor") + ")"); 58 | err.println("Warming up for " + warmUpSeconds + " seconds..."); 59 | final long warmStartCpuTime = System.nanoTime(); 60 | // warm up 61 | while (System.nanoTime() < warmStartCpuTime + warmUpNs) { 62 | test.run(); 63 | } 64 | err.println("Run testing"); 65 | final List results = new ArrayList<>(times); 66 | for (int i = 0; i < times; i++) { 67 | final long startCpuTime = System.nanoTime(); 68 | test.run(); 69 | final long endCpuTime = System.nanoTime(); 70 | results.add((endCpuTime - startCpuTime) / 1e6); 71 | } 72 | 73 | return getMedian(results); 74 | } 75 | 76 | private static String getTestName(Runnable test) { 77 | return test.getClass().getName().replace("org.greenrobot.essentials.javaperf.", "").replace("$", "/"); 78 | } 79 | 80 | private static double getMedian(List values) { 81 | // sort ascending 82 | Collections.sort(values); 83 | // get median 84 | int middle = values.size() / 2; 85 | if (values.size() % 2 == 1) { 86 | return values.get(middle); 87 | } else { 88 | return (values.get(middle - 1) + values.get(middle)) / 2.0; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /java-essentials-performance/src/main/java/org/greenrobot/essentials/javaperf/LongHashMapBenchmark.java: -------------------------------------------------------------------------------- 1 | package org.greenrobot.essentials.javaperf; 2 | 3 | import org.greenrobot.essentials.collections.LongHashMap; 4 | 5 | import java.util.HashMap; 6 | import java.util.Random; 7 | 8 | public class LongHashMapBenchmark { 9 | static final int N = 100000; 10 | static final int WARM_UP_TIME_S = 5; 11 | static final int RUN_COUNT = 100; 12 | 13 | // this is only for development purposes or to run tests separately. For automated benchmarking use gradle 14 | public static void main(String[] args) { 15 | BenchmarkRunner.runWallTime(new LibImpl(), RUN_COUNT, WARM_UP_TIME_S); 16 | } 17 | 18 | private LongHashMapBenchmark() { 19 | } 20 | 21 | private static class BaseImpl { 22 | final long[] values; 23 | 24 | public BaseImpl() { 25 | values = new long[N]; 26 | final Random random = new Random(657483918); 27 | for (int i = 0; i < N; i++) { 28 | values[i] = random.nextLong(); 29 | } 30 | } 31 | } 32 | 33 | public static class LibImpl extends BaseImpl implements Runnable { 34 | private final int initialCapacity; 35 | 36 | public LibImpl(int initialCapacity) { 37 | this.initialCapacity = initialCapacity; 38 | } 39 | 40 | public LibImpl() { 41 | this(16); 42 | } 43 | 44 | @Override 45 | public void run() { 46 | final LongHashMap map = new LongHashMap<>(initialCapacity); 47 | final Object object = new Object(); 48 | 49 | final long[] values = this.values; 50 | 51 | for (int i = 0; i < N; i++) { 52 | map.put(values[i], object); 53 | } 54 | // check contains on every second key and remove it 55 | for (int i = 0; i < N; i++) { 56 | final long key = values[i] + i % 2; 57 | if (map.get(key) != null) { 58 | map.remove(key); 59 | } 60 | } 61 | // remove the rest 62 | for (int i = 0; i < N; i++) { 63 | final long key = values[i]; 64 | if (map.get(key) != null) { 65 | map.remove(key); 66 | } 67 | } 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return "LongHashMap (Dynamic)/Lib"; 73 | } 74 | } 75 | 76 | public static class StdImpl extends BaseImpl implements Runnable { 77 | private final int initialCapacity; 78 | 79 | public StdImpl(int initialCapacity) { 80 | this.initialCapacity = initialCapacity; 81 | } 82 | 83 | public StdImpl() { 84 | this(16); 85 | } 86 | 87 | @Override 88 | public void run() { 89 | final HashMap map = new HashMap<>(initialCapacity); 90 | final Object object = new Object(); 91 | 92 | final long[] values = this.values; 93 | 94 | for (int i = 0; i < N; i++) { 95 | map.put(values[i], object); 96 | } 97 | // check contains on every second key and remove it 98 | for (int i = 0; i < N; i++) { 99 | final long key = values[i] + i % 2; 100 | if (map.get(key) != null) { 101 | map.remove(key); 102 | } 103 | } 104 | // remove the rest 105 | for (int i = 0; i < N; i++) { 106 | final long key = values[i]; 107 | if (map.get(key) != null) { 108 | map.remove(key); 109 | } 110 | } 111 | } 112 | 113 | @Override 114 | public String toString() { 115 | return "LongHashMap (Dynamic)/Std"; 116 | } 117 | } 118 | 119 | public static class PreallocLibImpl extends LibImpl { 120 | public PreallocLibImpl() { 121 | super(N); 122 | } 123 | 124 | @Override 125 | public String toString() { 126 | return "LongHashMap (Prealloc)/Lib"; 127 | } 128 | } 129 | 130 | public static class PreallocStdImpl extends StdImpl { 131 | public PreallocStdImpl() { 132 | super(N); 133 | } 134 | 135 | @Override 136 | public String toString() { 137 | return "LongHashMap (Prealloc)/Std"; 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /java-essentials-performance/src/main/java/org/greenrobot/essentials/javaperf/LongHashSetBenchmark.java: -------------------------------------------------------------------------------- 1 | package org.greenrobot.essentials.javaperf; 2 | 3 | import org.greenrobot.essentials.collections.LongHashSet; 4 | 5 | import java.util.HashSet; 6 | import java.util.Random; 7 | 8 | public class LongHashSetBenchmark { 9 | static final int N = 100000; 10 | static final int WARM_UP_TIME_S = 5; 11 | static final int RUN_COUNT = 100; 12 | 13 | // this is only for development purposes or to run tests separately. For automated benchmarking use gradle 14 | public static void main(String[] args) { 15 | BenchmarkRunner.runWallTime(new LibImpl(), RUN_COUNT, WARM_UP_TIME_S); 16 | } 17 | 18 | private LongHashSetBenchmark() { 19 | } 20 | 21 | private static class BaseImpl { 22 | final long[] values; 23 | 24 | public BaseImpl() { 25 | values = new long[N]; 26 | final Random random = new Random(657483918); 27 | for (int i = 0; i < N; i++) { 28 | values[i] = random.nextLong(); 29 | } 30 | } 31 | } 32 | 33 | public static class LibImpl extends BaseImpl implements Runnable { 34 | private final int initialCapacity; 35 | 36 | public LibImpl(int initialCapacity) { 37 | this.initialCapacity = initialCapacity; 38 | } 39 | 40 | public LibImpl() { 41 | this(16); 42 | } 43 | 44 | @Override 45 | public void run() { 46 | final LongHashSet set = new LongHashSet(initialCapacity); 47 | 48 | final long[] values = this.values; 49 | 50 | for (int i = 0; i < N; i++) { 51 | set.add(values[i]); 52 | } 53 | // check contains on every second key and remove it 54 | for (int i = 0; i < N; i++) { 55 | final long key = values[i] + i % 2; 56 | if (set.contains(key)) { 57 | set.remove(key); 58 | } 59 | } 60 | // remove the rest 61 | for (int i = 0; i < N; i++) { 62 | final long key = values[i]; 63 | if (set.contains(key)) { 64 | set.remove(key); 65 | } 66 | } 67 | } 68 | 69 | @Override 70 | public String toString() { 71 | return "LongHashSet (Dynamic)/Lib"; 72 | } 73 | } 74 | 75 | public static class StdImpl extends BaseImpl implements Runnable { 76 | private final int initialCapacity; 77 | 78 | public StdImpl(int initialCapacity) { 79 | this.initialCapacity = initialCapacity; 80 | } 81 | 82 | public StdImpl() { 83 | this(16); 84 | } 85 | 86 | @Override 87 | public void run() { 88 | final HashSet set = new HashSet<>(initialCapacity); 89 | 90 | final long[] values = this.values; 91 | 92 | for (int i = 0; i < N; i++) { 93 | set.add(values[i]); 94 | } 95 | // check contains on every second key and remove it 96 | for (int i = 0; i < N; i++) { 97 | final long key = values[i] + i % 2; 98 | if (set.contains(key)) { 99 | set.remove(key); 100 | } 101 | } 102 | // remove the rest 103 | for (int i = 0; i < N; i++) { 104 | final long key = values[i]; 105 | if (set.contains(key)) { 106 | set.remove(key); 107 | } 108 | } 109 | } 110 | 111 | @Override 112 | public String toString() { 113 | return "LongHashSet (Dynamic)/Std"; 114 | } 115 | } 116 | 117 | public static class PreallocLibImpl extends LibImpl { 118 | public PreallocLibImpl() { 119 | super(N); 120 | } 121 | 122 | @Override 123 | public String toString() { 124 | return "LongHashSet (Prealloc)/Lib"; 125 | } 126 | } 127 | 128 | public static class PreallocStdImpl extends StdImpl { 129 | public PreallocStdImpl() { 130 | super(N); 131 | } 132 | 133 | @Override 134 | public String toString() { 135 | return "LongHashSet (Prealloc)/Std"; 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /java-essentials-performance/src/main/java/org/greenrobot/essentials/javaperf/PipelineStreamBenchmark.java: -------------------------------------------------------------------------------- 1 | package org.greenrobot.essentials.javaperf; 2 | 3 | import org.greenrobot.essentials.io.PipelineOutputStream; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.OutputStream; 8 | import java.io.PipedInputStream; 9 | import java.io.PipedOutputStream; 10 | import java.util.concurrent.CountDownLatch; 11 | 12 | public class PipelineStreamBenchmark { 13 | static int STREAM_LENGTH = 1 * 1024 * 1024; // 1 MB 14 | static final int[] SIZES = {8, 64, 128, 512, 1024, 2048, 4096, 8192}; 15 | 16 | // this is only for development purposes or to run tests separately. For automated benchmarking use gradle 17 | public static void main(String[] args) { 18 | BenchmarkRunner.runWallTime(new LibImpl(), 100, 10); 19 | } 20 | 21 | public static void transfer(final InputStream inputStream, final OutputStream outputStream, final int amount) { 22 | final CountDownLatch latch = new CountDownLatch(1); 23 | 24 | new Thread() { 25 | @Override 26 | public void run() { 27 | try { 28 | final int[] sizes = SIZES; 29 | final int sizesLength = sizes.length; 30 | final byte[] buffer = new byte[sizes[sizesLength - 1]]; 31 | 32 | int sizeIndex = 0; 33 | for (int position = 0; position < amount; ) { 34 | final int nextLength = Math.min(sizes[sizeIndex], STREAM_LENGTH - position); 35 | outputStream.write(buffer, 0, nextLength); 36 | position += nextLength; 37 | sizeIndex = (sizeIndex + 1) % sizes.length; 38 | } 39 | } catch (IOException e) { 40 | throw new RuntimeException(e); 41 | } finally { 42 | try { 43 | outputStream.close(); 44 | } catch (IOException e) { 45 | e.printStackTrace(); 46 | } 47 | } 48 | } 49 | }.start(); 50 | 51 | new Thread() { 52 | @Override 53 | public void run() { 54 | try { 55 | final byte[] buffer = new byte[SIZES[SIZES.length - 1]]; 56 | while (inputStream.read(buffer, 0, buffer.length) != -1); 57 | } catch (IOException e) { 58 | throw new RuntimeException(e); 59 | } finally { 60 | latch.countDown(); 61 | } 62 | } 63 | }.start(); 64 | 65 | try { 66 | latch.await(); 67 | } catch (InterruptedException e) { 68 | throw new RuntimeException(e); 69 | } 70 | } 71 | 72 | private PipelineStreamBenchmark() { 73 | } 74 | 75 | public static class LibImpl implements Runnable { 76 | private final int streamLength; 77 | 78 | public LibImpl(int streamLength) { 79 | this.streamLength = streamLength; 80 | } 81 | 82 | public LibImpl() { 83 | this(STREAM_LENGTH); 84 | } 85 | 86 | @Override 87 | public void run() { 88 | final PipelineOutputStream stream = new PipelineOutputStream(); 89 | transfer(stream.getInputStream(), stream, streamLength); 90 | } 91 | 92 | @Override 93 | public String toString() { 94 | return "PipelineStream (" + (streamLength / 1024) + "KB)/Lib"; 95 | } 96 | } 97 | 98 | public static class StdImpl implements Runnable { 99 | 100 | private final int streamLength; 101 | 102 | public StdImpl(int streamLength) { 103 | this.streamLength = streamLength; 104 | } 105 | 106 | public StdImpl() { 107 | this(STREAM_LENGTH); 108 | } 109 | 110 | @Override 111 | public void run() { 112 | try { 113 | final PipedInputStream istream = new PipedInputStream(); 114 | final PipedOutputStream ostream = new PipedOutputStream(istream); 115 | transfer(istream, ostream, streamLength); 116 | } catch (IOException e) { 117 | throw new RuntimeException(e); 118 | } 119 | } 120 | 121 | @Override 122 | public String toString() { 123 | return "PipelineStream (" + (streamLength / 1024) + "KB)/Std"; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /java-essentials-performance/src/main/java/org/greenrobot/essentials/javaperf/StringHexBenchmark.java: -------------------------------------------------------------------------------- 1 | package org.greenrobot.essentials.javaperf; 2 | 3 | import com.google.common.hash.HashCode; 4 | import org.greenrobot.essentials.StringUtils; 5 | 6 | import java.util.Random; 7 | 8 | public class StringHexBenchmark { 9 | static final int BYTES_COUNT = 1024 * 1024; // 1 MB 10 | static final byte[] bytes = generateBytes(); 11 | 12 | private static byte[] generateBytes() { 13 | final byte[] bytes = new byte[BYTES_COUNT]; 14 | Random random = new Random(409342832); 15 | random.nextBytes(bytes); 16 | return bytes; 17 | } 18 | 19 | private StringHexBenchmark() { 20 | } 21 | 22 | public static class LibImpl implements Runnable { 23 | @Override 24 | public void run() { 25 | final String hex = StringUtils.hex(bytes); 26 | // avoid optimization by HotSpot 27 | if (hex.length() != bytes.length * 2) { 28 | throw new RuntimeException(); 29 | } 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "StringHex/Lib"; 35 | } 36 | } 37 | 38 | public static class StdImpl implements Runnable { 39 | @Override 40 | public void run() { 41 | final String hex = HashCode.fromBytes(bytes).toString(); 42 | // avoid optimization by HotSpot 43 | if (hex.length() != bytes.length * 2) { 44 | throw new RuntimeException(); 45 | } 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "StringHex/Std"; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /java-essentials-performance/src/main/java/org/greenrobot/essentials/javaperf/StringSplitBenchmark.java: -------------------------------------------------------------------------------- 1 | package org.greenrobot.essentials.javaperf; 2 | 3 | import org.greenrobot.essentials.StringUtils; 4 | 5 | public class StringSplitBenchmark { 6 | static final int TINY_REPEAT_COUNT = 2000; 7 | static final int SHORT_REPEAT_COUNT = 1000; 8 | static final int LONG_WORDS_COUNT = 10000; 9 | static String TINY_STRING = "John Doe"; 10 | static String SHORT_STRING = "The quick brown fox jumps over the lazy dog"; 11 | static String LONG_STRING = generateLongString(LONG_WORDS_COUNT); 12 | static final int TINY_WORDS_COUNT = StringUtils.split(TINY_STRING, ' ').length; 13 | static final int SHORT_WORDS_COUNT = StringUtils.split(SHORT_STRING, ' ').length; 14 | 15 | public static void main(String[] args) { 16 | double result = BenchmarkRunner.run(new LongLibImpl(), 1000, 3); 17 | System.out.println("Result (median): " + result + " seconds"); 18 | } 19 | 20 | static String name(int wordsCount, int times, String impl) { 21 | if (times > 1) { 22 | return "StringSplit (" + wordsCount + " words, " + times + " times)/" + impl; 23 | } else { 24 | return "StringSplit (" + wordsCount + " words)/" + impl; 25 | } 26 | } 27 | 28 | private StringSplitBenchmark() { 29 | } 30 | 31 | private static String generateLongString(int wordsCount) { 32 | StringBuilder builder = new StringBuilder(); 33 | String[] words = StringUtils.split(SHORT_STRING, ' '); 34 | for (int i = 0; i < wordsCount; i++) { 35 | builder.append(words[i % words.length]).append(' '); 36 | } 37 | return builder.toString(); 38 | } 39 | 40 | public static class ShortLibImpl implements Runnable { 41 | @Override 42 | public void run() { 43 | int count = 0; 44 | for (int i = 0; i < SHORT_REPEAT_COUNT; i++) { 45 | final String[] strings = StringUtils.split(SHORT_STRING, ' '); 46 | count += strings.length; 47 | } 48 | if (count != 9 * SHORT_REPEAT_COUNT) { 49 | throw new RuntimeException("Check test condition"); 50 | } 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return name(SHORT_WORDS_COUNT, SHORT_REPEAT_COUNT, "Lib"); 56 | } 57 | } 58 | 59 | public static class ShortStdImpl implements Runnable { 60 | @Override 61 | public void run() { 62 | int count = 0; 63 | for (int i = 0; i < SHORT_REPEAT_COUNT; i++) { 64 | final String[] strings = SHORT_STRING.split(" "); 65 | count += strings.length; 66 | } 67 | if (count != 9 * SHORT_REPEAT_COUNT) { 68 | throw new RuntimeException("Check test condition"); 69 | } 70 | } 71 | 72 | @Override 73 | public String toString() { 74 | return name(SHORT_WORDS_COUNT, SHORT_REPEAT_COUNT, "Std"); 75 | } 76 | } 77 | 78 | public static class TinyLibImpl implements Runnable { 79 | @Override 80 | public void run() { 81 | int count = 0; 82 | for (int i = 0; i < TINY_REPEAT_COUNT; i++) { 83 | final String[] strings = StringUtils.split(TINY_STRING, ' '); 84 | count += strings.length; 85 | } 86 | if (count != 2 * TINY_REPEAT_COUNT) { 87 | throw new RuntimeException("Check test condition"); 88 | } 89 | } 90 | 91 | @Override 92 | public String toString() { 93 | return name(TINY_WORDS_COUNT, TINY_REPEAT_COUNT, "Lib"); 94 | } 95 | } 96 | 97 | public static class TinyStdImpl implements Runnable { 98 | @Override 99 | public void run() { 100 | int count = 0; 101 | for (int i = 0; i < TINY_REPEAT_COUNT; i++) { 102 | final String[] strings = TINY_STRING.split(" "); 103 | count += strings.length; 104 | } 105 | if (count != 2 * TINY_REPEAT_COUNT) { 106 | throw new RuntimeException("Check test condition"); 107 | } 108 | } 109 | 110 | @Override 111 | public String toString() { 112 | return name(TINY_WORDS_COUNT, TINY_REPEAT_COUNT, "Std"); 113 | } 114 | } 115 | 116 | public static class LongLibImpl implements Runnable { 117 | @Override 118 | public void run() { 119 | final String[] strings = StringUtils.split(LONG_STRING, ' '); 120 | if (strings.length != LONG_WORDS_COUNT + 1) { // "+ 1" for the last closing space 121 | throw new RuntimeException("Check test condition"); 122 | } 123 | } 124 | 125 | @Override 126 | public String toString() { 127 | return name(LONG_WORDS_COUNT, 1, "Lib"); 128 | } 129 | } 130 | 131 | public static class LongStdImpl implements Runnable { 132 | @Override 133 | public void run() { 134 | final String[] strings = LONG_STRING.split(" "); 135 | if (strings.length != LONG_WORDS_COUNT) { 136 | throw new RuntimeException("Check test condition"); 137 | } 138 | } 139 | 140 | @Override 141 | public String toString() { 142 | return name(LONG_WORDS_COUNT, 1, "Std"); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /java-essentials/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.github.spotbugs" version "4.5.1" 3 | } 4 | 5 | apply plugin: 'java' 6 | apply plugin: 'maven' 7 | apply plugin: 'signing' 8 | apply plugin: 'checkstyle' 9 | 10 | archivesBaseName = 'essentials' 11 | group = 'org.greenrobot' 12 | version = '3.1.0' 13 | 14 | sourceCompatibility = 1.7 15 | targetCompatibility = '1.7' 16 | 17 | def isSnapshot = version.endsWith('-SNAPSHOT') 18 | def sonatypeRepositoryUrl 19 | if(isSnapshot) { 20 | sonatypeRepositoryUrl = "https://oss.sonatype.org/content/repositories/snapshots/" 21 | } else { 22 | sonatypeRepositoryUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 23 | } 24 | 25 | repositories { 26 | mavenCentral() 27 | } 28 | 29 | configurations { 30 | deployerJars 31 | // include test classes and their dependencies 32 | tests.extendsFrom testRuntime 33 | } 34 | 35 | dependencies { 36 | testImplementation 'junit:junit:4.13.1' 37 | testImplementation "com.google.guava:guava:$guavaVersion-jre" // Murmur3 (& hex) 38 | testImplementation 'net.jpountz.lz4:lz4:1.2.0' // xxHash 39 | 40 | deployerJars 'org.apache.maven.wagon:wagon-webdav:1.0-beta-2' 41 | } 42 | 43 | javadoc { 44 | failOnError = false 45 | title = "Essentials ${version} API" 46 | options.bottom = 'Available under the Apache License, Version 2.0 - Copyright © 2012-2020 greenrobot.org. All Rights Reserved.' 47 | doLast { 48 | copy { 49 | from '../javadoc-style' 50 | into "build/docs/javadoc/" 51 | } 52 | } 53 | } 54 | 55 | task javadocJar(type: Jar, dependsOn: javadoc) { 56 | classifier = 'javadoc' 57 | from 'build/docs/javadoc' 58 | } 59 | 60 | task sourcesJar(type: Jar) { 61 | from sourceSets.main.allSource 62 | classifier = 'sources' 63 | } 64 | 65 | // build a jar with all test classes to allow usage as dependency in android test project 66 | // https://discuss.gradle.org/t/how-do-i-declare-a-dependency-on-a-modules-test-code/7172/9 67 | task testJar(type: Jar) { 68 | classifier "test" 69 | from sourceSets.test.output 70 | // exclude tests which can not run on Android 71 | exclude '**/*Jvm*' 72 | } 73 | 74 | artifacts { 75 | archives jar 76 | archives javadocJar 77 | archives sourcesJar 78 | tests testJar 79 | } 80 | 81 | signing { 82 | if(project.hasProperty('signing.keyId') && project.hasProperty('signing.password') && 83 | project.hasProperty('signing.secretKeyRingFile')) { 84 | sign configurations.archives 85 | } else { 86 | println "Signing information missing/incomplete for ${project.name}" 87 | } 88 | } 89 | 90 | checkstyle { 91 | configFile = file("${rootProject.projectDir}/config/checkstyle/checkstyle-greenrobot.xml") 92 | configProperties.checkstyleSuppressionsFile = file("checkstyle-suppressions.xml") 93 | sourceSets = [sourceSets.main] 94 | } 95 | 96 | /* Todo port settings to spotbugs 97 | findbugs { 98 | toolVersion = '3.0.1' 99 | excludeFilter = file("findbugs-exclude.xml") 100 | effort = 'max' 101 | // use reportLevel='medium' for more issues 102 | reportLevel = 'high' 103 | sourceSets = [sourceSets.main] 104 | } 105 | 106 | */ 107 | 108 | spotbugsMain { 109 | ignoreFailures = true 110 | reports { 111 | // switch to html report 112 | xml.enabled = false 113 | html.enabled = true 114 | } 115 | } 116 | 117 | spotbugsTest { 118 | ignoreFailures = true 119 | reports { 120 | html { 121 | enabled = true 122 | } 123 | } 124 | } 125 | 126 | uploadArchives { 127 | repositories { 128 | mavenDeployer { 129 | if(project.hasProperty('preferedRepo') && project.hasProperty('preferedUsername') 130 | && project.hasProperty('preferedPassword')) { 131 | configuration = configurations.deployerJars 132 | repository(url: preferedRepo) { 133 | authentication(userName: preferedUsername, password: preferedPassword) 134 | } 135 | } else if(project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePassword')) { 136 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 137 | repository(url: sonatypeRepositoryUrl) { 138 | authentication(userName: sonatypeUsername, password: sonatypePassword) 139 | } 140 | } else { 141 | println "Settings sonatypeUsername/sonatypePassword missing/incomplete for ${project.name}" 142 | } 143 | pom.project { 144 | name 'greenrobot-essentials' 145 | packaging 'jar' 146 | description 'Essentials for high-performance Android and Java projects' 147 | url 'https://greenrobot.org/essentials/' 148 | 149 | scm { 150 | url 'https://github.com/greenrobot/essentials' 151 | connection 'scm:git@github.com:greenrobot/essentials.git' 152 | developerConnection 'scm:git@github.com:greenrobot/essentials.git' 153 | } 154 | 155 | licenses { 156 | license { 157 | name 'The Apache Software License, Version 2.0' 158 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 159 | distribution 'repo' 160 | } 161 | } 162 | 163 | developers { 164 | developer { 165 | id 'greenrobot' 166 | name 'greenrobot' 167 | } 168 | } 169 | 170 | issueManagement { 171 | system 'GitHub Issues' 172 | url 'https://github.com/greenrobot/essentials/issues' 173 | } 174 | 175 | organization { 176 | name 'greenrobot' 177 | url 'https://greenrobot.org' 178 | } 179 | } 180 | } 181 | } 182 | } -------------------------------------------------------------------------------- /java-essentials/checkstyle-suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /java-essentials/findbugs-exclude.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /java-essentials/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | essentials 6 | 3.0.0-SNAPSHOT 7 | 8 | 9 | org.greenrobot 10 | parent-pom-with-checks 11 | 3.0.0-SNAPSHOT 12 | ../build-common/parent-pom-with-checks.xml 13 | 14 | 15 | 16 | 17 | junit 18 | junit 19 | test 20 | 21 | 22 | 23 | com.google.guava 24 | guava 25 | 19.0 26 | test 27 | 28 | 29 | 30 | net.jpountz.lz4 31 | lz4 32 | 1.2.0 33 | test 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-checkstyle-plugin 42 | 43 | ${basedir}/checkstyle-suppressions.xml 44 | 45 | 46 | 47 | 48 | org.codehaus.mojo 49 | findbugs-maven-plugin 50 | 51 | ${basedir}/findbugs-exclude.xml 52 | 53 | 54 | 55 | 56 | org.apache.maven.plugins 57 | maven-source-plugin 58 | 59 | 60 | attach-sources 61 | 62 | jar 63 | 64 | 65 | 66 | 67 | 68 | 69 | org.apache.maven.plugins 70 | maven-javadoc-plugin 71 | 72 | 73 | attach-javadocs 74 | 75 | jar 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | deploy-to-mvn-central 91 | 92 | 93 | ossrh 94 | https://oss.sonatype.org/content/repositories/snapshots 95 | 96 | 97 | ossrh 98 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 99 | 100 | 101 | 102 | 103 | 104 | org.apache.maven.plugins 105 | maven-gpg-plugin 106 | 1.6 107 | 108 | 109 | sign-artifacts 110 | verify 111 | 112 | sign 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | greenrobot-essentials 123 | Essentials for high-performance Android and Java projects 124 | https://github.com/greenrobot/greenrobot-common 125 | 126 | 127 | The Apache License, Version 2.0 128 | http://www.apache.org/licenses/LICENSE-2.0.txt 129 | 130 | 131 | 132 | 133 | greenrobot 134 | greenrobot 135 | http://greenrobot.de 136 | 137 | 138 | 139 | scm:git@github.com:greenrobot/greenrobot-common.git 140 | scm:git@github.com:greenrobot/greenrobot-common.git 141 | https://github.com/greenrobot/greenrobot-common 142 | 143 | -------------------------------------------------------------------------------- /java-essentials/src/main/java/org/greenrobot/essentials/DateUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2016 Markus Junginger, greenrobot (http://greenrobot.org) 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 | package org.greenrobot.essentials; 18 | 19 | import java.util.Calendar; 20 | 21 | /** 22 | * Simple Date and time utils. 23 | */ 24 | public class DateUtils { 25 | /** 26 | * Calendar objects are rather expensive: for heavy usage it's a good idea to use a single instance per thread 27 | * instead of calling Calendar.getInstance() multiple times. Calendar.getInstance() creates a new instance each 28 | * time. 29 | */ 30 | public static final class DefaultCalendarThreadLocal extends ThreadLocal { 31 | @Override 32 | protected Calendar initialValue() { 33 | return Calendar.getInstance(); 34 | } 35 | } 36 | 37 | private static ThreadLocal calendarThreadLocal = new DefaultCalendarThreadLocal(); 38 | 39 | public static long getTimeForDay(int year, int month, int day) { 40 | return getTimeForDay(calendarThreadLocal.get(), year, month, day); 41 | } 42 | 43 | /** @param calendar helper object needed for conversion */ 44 | public static long getTimeForDay(Calendar calendar, int year, int month, int day) { 45 | calendar.clear(); 46 | calendar.set(year, month - 1, day); 47 | return calendar.getTimeInMillis(); 48 | } 49 | 50 | /** Sets hour, minutes, seconds and milliseconds to the given values. Leaves date info untouched. */ 51 | public static void setTime(Calendar calendar, int hourOfDay, int minute, int second, int millisecond) { 52 | calendar.set(Calendar.HOUR_OF_DAY, hourOfDay); 53 | calendar.set(Calendar.MINUTE, minute); 54 | calendar.set(Calendar.SECOND, second); 55 | calendar.set(Calendar.MILLISECOND, millisecond); 56 | } 57 | 58 | /** Readable yyyyMMdd int representation of a day, which is also sortable. */ 59 | public static int getDayAsReadableInt(long time) { 60 | Calendar cal = calendarThreadLocal.get(); 61 | cal.setTimeInMillis(time); 62 | return getDayAsReadableInt(cal); 63 | } 64 | 65 | /** Readable yyyyMMdd representation of a day, which is also sortable. */ 66 | public static int getDayAsReadableInt(Calendar calendar) { 67 | int day = calendar.get(Calendar.DAY_OF_MONTH); 68 | int month = calendar.get(Calendar.MONTH) + 1; 69 | int year = calendar.get(Calendar.YEAR); 70 | return year * 10000 + month * 100 + day; 71 | } 72 | 73 | /** Returns midnight of the given day. */ 74 | public static long getTimeFromDayReadableInt(int day) { 75 | return getTimeFromDayReadableInt(calendarThreadLocal.get(), day, 0); 76 | } 77 | 78 | /** @param calendar helper object needed for conversion */ 79 | public static long getTimeFromDayReadableInt(Calendar calendar, int readableDay, int hour) { 80 | int day = readableDay % 100; 81 | int month = readableDay / 100 % 100; 82 | int year = readableDay / 10000; 83 | 84 | calendar.clear(); // We don't set all fields, so we should clear the calendar first 85 | calendar.set(Calendar.HOUR_OF_DAY, hour); 86 | calendar.set(Calendar.DAY_OF_MONTH, day); 87 | calendar.set(Calendar.MONTH, month - 1); 88 | calendar.set(Calendar.YEAR, year); 89 | 90 | return calendar.getTimeInMillis(); 91 | } 92 | 93 | public static int getDayDifferenceOfReadableInts(int dayOfBroadcast1, int dayOfBroadcast2) { 94 | long time1 = getTimeFromDayReadableInt(dayOfBroadcast1); 95 | long time2 = getTimeFromDayReadableInt(dayOfBroadcast2); 96 | 97 | // Don't use getDayDifference(time1, time2) here, it's wrong for some days. 98 | // Do float calculation and rounding at the end to cover daylight saving stuff etc. 99 | float daysFloat = (time2 - time1) / 1000 / 60 / 60 / 24f; 100 | return Math.round(daysFloat); 101 | } 102 | 103 | public static int getDayDifference(long time1, long time2) { 104 | return (int) ((time2 - time1) / 1000 / 60 / 60 / 24); 105 | } 106 | 107 | public static long addDays(long time, int days) { 108 | Calendar calendar = calendarThreadLocal.get(); 109 | calendar.setTimeInMillis(time); 110 | calendar.add(Calendar.DAY_OF_YEAR, days); 111 | return calendar.getTimeInMillis(); 112 | } 113 | 114 | public static void addDays(Calendar calendar, int days) { 115 | calendar.add(Calendar.DAY_OF_YEAR, days); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /java-essentials/src/main/java/org/greenrobot/essentials/collections/AbstractMultimap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2016 Markus Junginger, greenrobot (http://greenrobot.org) 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 | package org.greenrobot.essentials.collections; 18 | 19 | import java.util.Collection; 20 | import java.util.Map; 21 | import java.util.Set; 22 | 23 | /** 24 | * Combines a Map with List values to provide simple way to store multiple values for a key (multimap). 25 | *

26 | * Threading note: All methods are synchronized 27 | */ 28 | public abstract class AbstractMultimap> implements Map { 29 | 30 | protected Map map; 31 | 32 | protected AbstractMultimap(Map map) { 33 | this.map = map; 34 | } 35 | 36 | abstract protected C createNewCollection(); 37 | 38 | @Override 39 | public void putAll(Map m) { 40 | map.putAll(m); 41 | } 42 | 43 | @Override 44 | public synchronized int size() { 45 | return map.size(); 46 | } 47 | 48 | @Override 49 | public synchronized boolean isEmpty() { 50 | return map.isEmpty(); 51 | } 52 | 53 | @Override 54 | public synchronized boolean containsKey(Object key) { 55 | return map.containsKey(key); 56 | } 57 | 58 | @Override 59 | public synchronized boolean containsValue(Object value) { 60 | return map.containsValue(value); 61 | } 62 | 63 | @Override 64 | public synchronized C get(Object key) { 65 | return map.get(key); 66 | } 67 | 68 | @Override 69 | public synchronized C remove(Object key) { 70 | return map.remove(key); 71 | } 72 | 73 | @Override 74 | public synchronized void clear() { 75 | map.clear(); 76 | } 77 | 78 | @Override 79 | public synchronized Set keySet() { 80 | return map.keySet(); 81 | } 82 | 83 | @Override 84 | public synchronized Collection values() { 85 | return map.values(); 86 | } 87 | 88 | @Override 89 | public synchronized boolean equals(Object o) { 90 | return map.equals(o); 91 | } 92 | 93 | @Override 94 | public synchronized int hashCode() { 95 | return map.hashCode(); 96 | } 97 | 98 | /** 99 | * @return number of elements stored for given key after storing the given value. 100 | */ 101 | public synchronized int putElement(K key, V value) { 102 | C collection = map.get(key); 103 | if (collection == null) { 104 | collection = createNewCollection(); 105 | map.put(key, collection); 106 | } 107 | collection.add(value); 108 | return collection.size(); 109 | } 110 | 111 | @Override 112 | public synchronized C put(K key, C value) { 113 | return map.put(key, value); 114 | } 115 | 116 | @Override 117 | public synchronized Set> entrySet() { 118 | return map.entrySet(); 119 | } 120 | 121 | /** @return true if the collection was changed. */ 122 | public synchronized boolean putElements(K key, Collection values) { 123 | C collection = map.get(key); 124 | if (collection == null) { 125 | collection = createNewCollection(); 126 | map.put(key, collection); 127 | } 128 | return collection.addAll(values); 129 | } 130 | 131 | /** @return true if the given element was removed. */ 132 | public synchronized boolean removeElement(K key, V value) { 133 | C collection = map.get(key); 134 | if (collection == null) { 135 | return false; 136 | } else { 137 | boolean removed = collection.remove(value); 138 | if (collection.isEmpty()) { 139 | map.remove(key); 140 | } 141 | return removed; 142 | } 143 | } 144 | 145 | public synchronized int countElements(K key) { 146 | C collection = map.get(key); 147 | if (collection == null) { 148 | return 0; 149 | } else { 150 | return collection.size(); 151 | } 152 | } 153 | 154 | public synchronized int countElements() { 155 | int count = 0; 156 | for (C collection : map.values()) { 157 | count += collection.size(); 158 | } 159 | return count; 160 | } 161 | 162 | public synchronized boolean containsElement(K key, V value) { 163 | C collection = map.get(key); 164 | if (collection == null) { 165 | return false; 166 | } else { 167 | return collection.contains(value); 168 | } 169 | } 170 | 171 | public synchronized boolean containsElement(V value) { 172 | for (C collection : map.values()) { 173 | if (collection.contains(value)) { 174 | return true; 175 | } 176 | } 177 | return false; 178 | } 179 | 180 | public synchronized C valuesElements() { 181 | C all = createNewCollection(); 182 | for (C collection : map.values()) { 183 | all.addAll(collection); 184 | } 185 | return all; 186 | } 187 | 188 | } 189 | -------------------------------------------------------------------------------- /java-essentials/src/main/java/org/greenrobot/essentials/collections/Multimap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2016 Markus Junginger, greenrobot (http://greenrobot.org) 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 | package org.greenrobot.essentials.collections; 18 | 19 | import java.util.ArrayList; 20 | import java.util.HashMap; 21 | import java.util.LinkedList; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.concurrent.CopyOnWriteArrayList; 25 | 26 | /** 27 | * Combines a Map with List values to provide simple way to store multiple values for a key. 28 | */ 29 | // Top level class to get rid of 3rd generic collection parameter for more convenient usage. 30 | public class Multimap extends AbstractMultimap> { 31 | private final ListType listType; 32 | 33 | public enum ListType { 34 | /** Aka ArrayList */ 35 | REGULAR, 36 | 37 | /** Aka CopyOnWriteArrayList */ 38 | THREAD_SAFE, 39 | 40 | /** Aka LinkedList */ 41 | LINKED 42 | } 43 | 44 | public static Multimap create() { 45 | return create(ListType.REGULAR); 46 | } 47 | 48 | public static Multimap create(ListType listType) { 49 | return new Multimap<>(new HashMap>(), listType); 50 | } 51 | 52 | protected Multimap(Map> map, ListType listType) { 53 | super(map); 54 | this.listType = listType; 55 | if (listType == null) { 56 | throw new IllegalArgumentException("List type may not be null"); 57 | } 58 | } 59 | 60 | protected List createNewCollection() { 61 | switch (listType) { 62 | case REGULAR: 63 | return new ArrayList<>(); 64 | case THREAD_SAFE: 65 | return new CopyOnWriteArrayList<>(); 66 | case LINKED: 67 | return new LinkedList<>(); 68 | default: 69 | throw new IllegalStateException("Unknown list type: " + listType); 70 | } 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /java-essentials/src/main/java/org/greenrobot/essentials/collections/MultimapSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2016 Markus Junginger, greenrobot (http://greenrobot.org) 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 | package org.greenrobot.essentials.collections; 18 | 19 | import java.util.HashMap; 20 | import java.util.HashSet; 21 | import java.util.Map; 22 | import java.util.Set; 23 | import java.util.concurrent.CopyOnWriteArraySet; 24 | 25 | /** 26 | * Combines a Map with Set values to provide simple way to store multiple values for a key. 27 | * Like {@link org.greenrobot.essentials.collections.Multimap}, but element values are stored in Sets. 28 | */ 29 | // Top level class to get rid of 3rd generic collection parameter for more convenient usage. 30 | public class MultimapSet extends AbstractMultimap> { 31 | private final SetType setType; 32 | 33 | public enum SetType { 34 | /** Aka HashSet */ 35 | REGULAR, 36 | 37 | /** Aka CopyOnWriteArraySet */ 38 | THREAD_SAFE 39 | } 40 | 41 | public static MultimapSet create() { 42 | return create(SetType.REGULAR); 43 | } 44 | 45 | public static MultimapSet create(SetType setType) { 46 | return new MultimapSet<>(new HashMap>(), setType); 47 | } 48 | 49 | protected MultimapSet(Map> map, SetType setType) { 50 | super(map); 51 | this.setType = setType; 52 | } 53 | 54 | protected Set createNewCollection() { 55 | switch (setType) { 56 | case REGULAR: 57 | return new HashSet<>(); 58 | case THREAD_SAFE: 59 | return new CopyOnWriteArraySet<>(); 60 | default: 61 | throw new IllegalStateException("Unknown set type: " + setType); 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /java-essentials/src/main/java/org/greenrobot/essentials/hash/Checksum128.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2016 Markus Junginger, greenrobot (http://greenrobot.org) 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 | package org.greenrobot.essentials.hash; 18 | 19 | import java.math.BigInteger; 20 | import java.util.zip.Checksum; 21 | 22 | /** Checksum interface to access 128 bit in various ways. */ 23 | public interface Checksum128 extends Checksum { 24 | /** Returns the higher 64 bits of the 128 bit hash. */ 25 | long getValueHigh(); 26 | 27 | /** Positive value. */ 28 | BigInteger getValueBigInteger(); 29 | 30 | /** Padded with leading 0s to ensure length of 32. */ 31 | String getValueHexString(); 32 | 33 | /** Big endian is the default in Java / network byte order. */ 34 | byte[] getValueBytesBigEndian(); 35 | 36 | /** Big endian is used by most machines natively. */ 37 | byte[] getValueBytesLittleEndian(); 38 | } 39 | -------------------------------------------------------------------------------- /java-essentials/src/main/java/org/greenrobot/essentials/hash/FNV32.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2016 Markus Junginger, greenrobot (http://greenrobot.org) 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 | package org.greenrobot.essentials.hash; 18 | 19 | import java.util.zip.Checksum; 20 | 21 | /** Hash function FNV-1a (http://www.isthe.com/chongo/tech/comp/fnv). */ 22 | public class FNV32 implements Checksum { 23 | private final static int INITIAL_VALUE = 0x811C9DC5; 24 | private final static int MULTIPLIER = 16777619; 25 | 26 | private int hash = INITIAL_VALUE; 27 | 28 | @Override 29 | public void update(int b) { 30 | hash ^= 0xff & b; 31 | hash *= MULTIPLIER; 32 | } 33 | 34 | @Override 35 | public void update(byte[] b, int off, int len) { 36 | int stop = off + len; 37 | for (int i = off; i < stop; i++) { 38 | hash ^= 0xff & b[i]; 39 | hash *= MULTIPLIER; 40 | 41 | // This optimization of the multiplication might work in C, but not in Java (~2 times slower) 42 | // hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24); 43 | } 44 | } 45 | 46 | @Override 47 | public long getValue() { 48 | return hash & 0xffffffffL; 49 | } 50 | 51 | @Override 52 | public void reset() { 53 | hash = INITIAL_VALUE; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /java-essentials/src/main/java/org/greenrobot/essentials/hash/FNV64.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2016 Markus Junginger, greenrobot (http://greenrobot.org) 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 | package org.greenrobot.essentials.hash; 18 | 19 | import java.util.zip.Checksum; 20 | 21 | /** Hash function FNV-1a (http://www.isthe.com/chongo/tech/comp/fnv). */ 22 | public class FNV64 implements Checksum { 23 | private final static long INITIAL_VALUE = 0xcbf29ce484222325L; 24 | private final static long MULTIPLIER = 0x100000001b3L; 25 | 26 | private long hash = INITIAL_VALUE; 27 | 28 | @Override 29 | public void update(int b) { 30 | hash ^= 0xffL & b; 31 | hash *= MULTIPLIER; 32 | } 33 | 34 | @Override 35 | public void update(byte[] b, int off, int len) { 36 | int stop = off + len; 37 | for (int i = off; i < stop; i++) { 38 | hash ^= 0xffL & b[i]; 39 | hash *= MULTIPLIER; 40 | } 41 | } 42 | 43 | @Override 44 | /** Note: Java's long is signed. */ 45 | public long getValue() { 46 | return hash; 47 | } 48 | 49 | @Override 50 | public void reset() { 51 | hash = INITIAL_VALUE; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /java-essentials/src/main/java/org/greenrobot/essentials/hash/PrimitiveDataChecksum.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2016 Markus Junginger, greenrobot (http://greenrobot.org) 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 | package org.greenrobot.essentials.hash; 18 | 19 | import java.io.UnsupportedEncodingException; 20 | import java.util.zip.Checksum; 21 | 22 | /** Wrapper for Checksum that accepts all kind of primitive data to update the hash. */ 23 | public class PrimitiveDataChecksum implements Checksum { 24 | private final Checksum checksum; 25 | 26 | public PrimitiveDataChecksum(Checksum checksum) { 27 | this.checksum = checksum; 28 | } 29 | 30 | @Override 31 | public void update(int b) { 32 | checksum.update(b); 33 | } 34 | 35 | @Override 36 | public void update(byte[] b, int off, int len) { 37 | checksum.update(b, off, len); 38 | } 39 | 40 | @Override 41 | public long getValue() { 42 | return checksum.getValue(); 43 | } 44 | 45 | @Override 46 | public void reset() { 47 | checksum.reset(); 48 | } 49 | 50 | /** Note: leaves the checksum untouched if given value is null (provide a special value for stronger hashing). */ 51 | public void updateUtf8(String string) { 52 | if (string != null) { 53 | byte[] bytes; 54 | try { 55 | bytes = string.getBytes("UTF-8"); 56 | } catch (UnsupportedEncodingException e) { 57 | throw new RuntimeException(e); 58 | } 59 | update(bytes, 0, bytes.length); 60 | } 61 | } 62 | 63 | /** Note: leaves the checksum untouched if given value is null (provide a special value for stronger hashing). */ 64 | public void updateUtf8(String[] strings) { 65 | if (strings != null) { 66 | for (String string : strings) { 67 | updateUtf8(string); 68 | } 69 | } 70 | } 71 | 72 | /** updates a byte with 0 for false and 1 for true */ 73 | public void updateBoolean(boolean value) { 74 | update(value ? 1 : 0); 75 | } 76 | 77 | public void updateShort(short number) { 78 | update((number >>> 8) & 0xff); 79 | update(number & 0xff); 80 | } 81 | 82 | public void updateInt(int number) { 83 | update((number >>> 24) & 0xff); 84 | update((number >>> 16) & 0xff); 85 | update((number >>> 8) & 0xff); 86 | update(number & 0xff); 87 | } 88 | 89 | public void updateLong(long number) { 90 | update((int) (number >>> 56) & 0xff); 91 | update((int) (number >>> 48) & 0xff); 92 | update((int) (number >>> 40) & 0xff); 93 | update((int) (number >>> 32) & 0xff); 94 | update((int) (number >>> 24) & 0xff); 95 | update((int) (number >>> 16) & 0xff); 96 | update((int) (number >>> 8) & 0xff); 97 | update((int) (number & 0xff)); 98 | } 99 | 100 | public void updateFloat(float number) { 101 | updateInt(Float.floatToIntBits(number)); 102 | } 103 | 104 | public void updateDouble(double number) { 105 | updateLong(Double.doubleToLongBits(number)); 106 | } 107 | 108 | /** Note: leaves the checksum untouched if given value is null (provide a special value for stronger hashing). */ 109 | public void update(byte[] numbers) { 110 | if (numbers != null) { 111 | update(numbers, 0, numbers.length); 112 | } 113 | } 114 | 115 | /** Note: leaves the checksum untouched if given value is null (provide a special value for stronger hashing). */ 116 | public void update(short[] numbers) { 117 | if (numbers != null) { 118 | for (short number : numbers) { 119 | updateShort(number); 120 | } 121 | } 122 | } 123 | 124 | /** Note: leaves the checksum untouched if given value is null (provide a special value for stronger hashing). */ 125 | public void update(int[] numbers) { 126 | if (numbers != null) { 127 | for (int number : numbers) { 128 | updateInt(number); 129 | } 130 | } 131 | } 132 | 133 | /** Note: leaves the checksum untouched if given value is null (provide a special value for stronger hashing). */ 134 | public void update(long[] numbers) { 135 | if (numbers != null) { 136 | for (long number : numbers) { 137 | updateLong(number); 138 | } 139 | } 140 | } 141 | 142 | /** Note: leaves the checksum untouched if given value is null (provide a special value for stronger hashing). */ 143 | public void update(float[] numbers) { 144 | if (numbers != null) { 145 | for (float number : numbers) { 146 | updateFloat(number); 147 | } 148 | } 149 | } 150 | 151 | /** Note: leaves the checksum untouched if given value is null (provide a special value for stronger hashing). */ 152 | public void update(double[] numbers) { 153 | if (numbers != null) { 154 | for (double number : numbers) { 155 | updateDouble(number); 156 | } 157 | } 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /java-essentials/src/main/java/org/greenrobot/essentials/io/IoUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2016 Markus Junginger, greenrobot (http://greenrobot.org) 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 | package org.greenrobot.essentials.io; 18 | 19 | import org.greenrobot.essentials.StringUtils; 20 | 21 | import java.io.ByteArrayOutputStream; 22 | import java.io.Closeable; 23 | import java.io.IOException; 24 | import java.io.InputStream; 25 | import java.io.OutputStream; 26 | import java.io.Reader; 27 | import java.io.Writer; 28 | import java.security.MessageDigest; 29 | import java.security.NoSuchAlgorithmException; 30 | import java.util.zip.Checksum; 31 | 32 | /** 33 | * Utils for dealing with IO (streams, readers, ...). 34 | * 35 | * @author Markus 36 | */ 37 | public class IoUtils { 38 | private static final int BUFFER_SIZE = 8192; 39 | 40 | public static byte[] readAllBytes(InputStream in) throws IOException { 41 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 42 | copyAllBytes(in, out); 43 | return out.toByteArray(); 44 | } 45 | 46 | public static byte[] readAllBytesAndClose(InputStream in) throws IOException { 47 | try { 48 | return readAllBytes(in); 49 | } finally { 50 | safeClose(in); 51 | } 52 | } 53 | 54 | public static String readAllChars(Reader reader) throws IOException { 55 | char[] buffer = new char[BUFFER_SIZE / 2]; 56 | StringBuilder builder = new StringBuilder(); 57 | while (true) { 58 | int read = reader.read(buffer); 59 | if (read == -1) { 60 | break; 61 | } 62 | builder.append(buffer, 0, read); 63 | } 64 | return builder.toString(); 65 | } 66 | 67 | public static String readAllCharsAndClose(Reader reader) throws IOException { 68 | try { 69 | return readAllChars(reader); 70 | } finally { 71 | safeClose(reader); 72 | } 73 | } 74 | 75 | public static void writeAllCharsAndClose(Writer writer, CharSequence text) throws IOException { 76 | try { 77 | writer.append(text); 78 | } finally { 79 | safeClose(writer); 80 | } 81 | } 82 | 83 | public static void updateChecksum(InputStream in, Checksum checksum) throws IOException { 84 | byte[] buffer = new byte[BUFFER_SIZE]; 85 | while (true) { 86 | int read = in.read(buffer); 87 | if (read == -1) { 88 | break; 89 | } 90 | checksum.update(buffer, 0, read); 91 | } 92 | } 93 | 94 | /** @return MD5 digest (32 hex characters). */ 95 | public static String getMd5(InputStream in) throws IOException { 96 | byte[] digest = getDigest(in, "MD5"); 97 | return StringUtils.hex(digest); 98 | } 99 | 100 | /** @return SHA-1 digest (40 hex characters). */ 101 | public static String getSha1(InputStream in) throws IOException { 102 | byte[] digest = getDigest(in, "SHA-1"); 103 | return StringUtils.hex(digest); 104 | } 105 | 106 | /** @return SHA-256 digest (64 hex characters). */ 107 | public static String getSha256(InputStream in) throws IOException { 108 | byte[] digest = getDigest(in, "SHA-256"); 109 | return StringUtils.hex(digest); 110 | } 111 | 112 | public static byte[] getDigest(InputStream in, String digestAlgo) throws IOException { 113 | MessageDigest digester; 114 | try { 115 | digester = MessageDigest.getInstance(digestAlgo); 116 | } catch (NoSuchAlgorithmException nsae) { 117 | throw new RuntimeException(nsae); 118 | } 119 | 120 | byte[] buffer = new byte[BUFFER_SIZE]; 121 | while (true) { 122 | int read = in.read(buffer); 123 | if (read == -1) { 124 | break; 125 | } 126 | digester.update(buffer, 0, read); 127 | } 128 | return digester.digest(); 129 | } 130 | 131 | /** 132 | * Copies all available data from in to out without closing any stream. 133 | * 134 | * @return number of bytes copied 135 | */ 136 | public static int copyAllBytes(InputStream in, OutputStream out) throws IOException { 137 | int byteCount = 0; 138 | byte[] buffer = new byte[BUFFER_SIZE]; 139 | while (true) { 140 | int read = in.read(buffer); 141 | if (read == -1) { 142 | break; 143 | } 144 | out.write(buffer, 0, read); 145 | byteCount += read; 146 | } 147 | return byteCount; 148 | } 149 | 150 | 151 | /** 152 | * Closes the given resource (e.g. stream, reader, writer, etc.) inside a try/catch. 153 | * Does nothing if stream is null. 154 | */ 155 | public static void safeClose(Closeable closeable) { 156 | if (closeable != null) { 157 | try { 158 | closeable.close(); 159 | } catch (IOException e) { 160 | // Silent 161 | } 162 | } 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /java-essentials/src/main/java/org/greenrobot/essentials/io/LimitedInputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2016 Markus Junginger, greenrobot (http://greenrobot.org) 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 | package org.greenrobot.essentials.io; 18 | 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.util.zip.GZIPInputStream; 22 | 23 | /** 24 | * Wraps around an InputStream and limits the amount of bytes that can be read from it. Use it if you operate on an 25 | * InputStream that consists of chunks of a know size, each to be processed using a buffered input stream (e.g. 26 | * GZIPInputStream). Normal buffered input streams would read beyond the limit. The LimitedInputStream never closes the 27 | * inside InputStream(close does nothing). 28 | * 29 | * @author Markus 30 | */ 31 | public class LimitedInputStream extends InputStream { 32 | 33 | public static GZIPInputStream createGZIPInputStream(InputStream in, int maxBytes) throws IOException { 34 | LimitedInputStream limitedInputStream = new LimitedInputStream(in, maxBytes); 35 | return new GZIPInputStream(limitedInputStream); 36 | } 37 | 38 | private int bytesLeft; 39 | private final InputStream in; 40 | 41 | public LimitedInputStream(InputStream in, int maxBytes) { 42 | this.in = in; 43 | bytesLeft = maxBytes; 44 | } 45 | 46 | @Override 47 | public int available() throws IOException { 48 | int available = in.available(); 49 | return Math.min(available, bytesLeft); 50 | } 51 | 52 | @Override 53 | public int read() throws IOException { 54 | if (bytesLeft <= 0) { 55 | return -1; 56 | } 57 | int read = in.read(); 58 | if (read != -1) { 59 | bytesLeft--; 60 | } 61 | return read; 62 | } 63 | 64 | @Override 65 | public int read(byte[] buffer, int offset, int count) throws IOException { 66 | if (bytesLeft <= 0) { 67 | return -1; 68 | } 69 | int countToRead = Math.min(bytesLeft, count); 70 | int read = in.read(buffer, offset, countToRead); 71 | if (read > 0) { 72 | bytesLeft -= read; 73 | } 74 | return read; 75 | } 76 | 77 | @Override 78 | public int read(byte[] buffer) throws IOException { 79 | return read(buffer, 0, buffer.length); 80 | } 81 | 82 | @Override 83 | public long skip(long byteCount) throws IOException { 84 | if (byteCount <= 0) { 85 | return 0; 86 | } 87 | long countToSkip = Math.min(bytesLeft, byteCount); 88 | long skipped = in.skip(countToSkip); 89 | if (skipped > 0) { 90 | bytesLeft -= skipped; 91 | } 92 | return skipped; 93 | } 94 | 95 | @Override 96 | /** Does nothing, because the inner stream is intended to be left open and closed separately (by design).*/ 97 | public void close() throws IOException { 98 | } 99 | 100 | public int getBytesLeft() { 101 | return bytesLeft; 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /java-essentials/src/main/java/org/greenrobot/essentials/io/PipelineOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2016 Markus Junginger, greenrobot (http://greenrobot.org) 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 | package org.greenrobot.essentials.io; 18 | 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.io.OutputStream; 22 | 23 | /** 24 | * Replacement for Java's PipedOutputStream: all data written to this stream will get available in the integrated 25 | * InputStream (see {@link #getInputStream()}). 26 | *

27 | * Note: Usually, you will have exactly two threads: one to write and one to read. If you use a single thread, avoid 28 | * reading more bytes than previously written or writing more bytes than the internal buffer can handle. 29 | */ 30 | public class PipelineOutputStream extends OutputStream { 31 | private final PipelineInputStream inputStream; 32 | final CircularByteBuffer buffer; 33 | boolean closedOut; 34 | boolean closedIn; 35 | 36 | public PipelineOutputStream() { 37 | this(8192); 38 | } 39 | 40 | public PipelineOutputStream(int bufferCapacity) { 41 | this.buffer = new CircularByteBuffer(bufferCapacity); 42 | inputStream = new PipelineInputStream(); 43 | } 44 | 45 | public InputStream getInputStream() { 46 | return inputStream; 47 | } 48 | 49 | @Override 50 | public synchronized void write(byte[] data, int off, int len) throws IOException { 51 | int done = 0; 52 | while (done != len) { 53 | checkPipelineInput(); 54 | int count = buffer.put(data, off + done, len - done); 55 | if (count > 0) { 56 | done += count; 57 | notifyBuffer(); 58 | } else { 59 | waitForBuffer(); 60 | } 61 | } 62 | } 63 | 64 | private void checkPipelineInput() throws IOException { 65 | if (closedIn) { 66 | throw new IOException("PipelineInputStream was closed (broken pipeline)"); 67 | } 68 | } 69 | 70 | @Override 71 | public synchronized void write(int b) throws IOException { 72 | checkPipelineInput(); 73 | while (!buffer.put((byte) b)) { 74 | waitForBuffer(); 75 | checkPipelineInput(); 76 | } 77 | notifyBuffer(); 78 | } 79 | 80 | @Override 81 | public synchronized void close() throws IOException { 82 | closedOut = true; 83 | notifyBuffer(); 84 | } 85 | 86 | void waitForBuffer() throws IOException { 87 | try { 88 | wait(); 89 | } catch (InterruptedException e) { 90 | throw new IOException(e); 91 | } 92 | } 93 | 94 | void notifyBuffer() { 95 | notifyAll(); 96 | } 97 | 98 | protected class PipelineInputStream extends InputStream { 99 | 100 | @Override 101 | public int read(byte[] b, int off, int len) throws IOException { 102 | if (len == 0) { 103 | return closedOut ? -1 : 0; 104 | } 105 | int read; 106 | synchronized (PipelineOutputStream.this) { 107 | do { 108 | read = buffer.get(b, off, len); 109 | if (read == 0) { 110 | if (closedOut) { 111 | return -1; 112 | } 113 | waitForBuffer(); 114 | } 115 | } while (read == 0); 116 | notifyBuffer(); 117 | } 118 | 119 | return read; 120 | } 121 | 122 | @Override 123 | public int read() throws IOException { 124 | synchronized (PipelineOutputStream.this) { 125 | int value = buffer.get(); 126 | while (value == -1) { 127 | if (closedOut) { 128 | return -1; 129 | } 130 | waitForBuffer(); 131 | value = buffer.get(); 132 | } 133 | notifyBuffer(); 134 | return value; 135 | } 136 | } 137 | 138 | @Override 139 | public int available() throws IOException { 140 | return buffer.available(); 141 | } 142 | 143 | @Override 144 | public long skip(long n) throws IOException { 145 | int len = (int) Math.min(n, Integer.MAX_VALUE); 146 | int total = 0; 147 | synchronized (PipelineOutputStream.this) { 148 | while (total < len) { 149 | int skipped = buffer.skip(len - total); 150 | if (skipped == 0) { 151 | if (closedOut) { 152 | return total; 153 | } 154 | waitForBuffer(); 155 | } else { 156 | total += skipped; 157 | notifyBuffer(); 158 | } 159 | } 160 | return total; 161 | } 162 | } 163 | 164 | @Override 165 | public void close() throws IOException { 166 | closedIn = true; 167 | } 168 | } 169 | 170 | } 171 | -------------------------------------------------------------------------------- /java-essentials/src/main/java/org/greenrobot/essentials/io/RepeaterInputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2016 Markus Junginger, greenrobot (http://greenrobot.org) 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 | package org.greenrobot.essentials.io; 18 | 19 | import java.io.FilterInputStream; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.io.OutputStream; 23 | 24 | /** 25 | * Repeats an input stream to an additional OutputStream. The data of the InputStream becomes available for two 26 | * purposes, e.g. reading data and storing for caching. 27 | *

28 | * Note: OutputStream is not closed when close() is called. 29 | * 30 | * @author Markus 31 | */ 32 | public class RepeaterInputStream extends FilterInputStream { 33 | private final OutputStream out; 34 | 35 | public RepeaterInputStream(InputStream in, OutputStream out) { 36 | super(in); 37 | this.out = out; 38 | } 39 | 40 | @Override 41 | public int read() throws IOException { 42 | int read = in.read(); 43 | if (read > 0) { 44 | out.write(read); 45 | } 46 | return read; 47 | } 48 | 49 | @Override 50 | public int read(byte[] b, int off, int len) throws IOException { 51 | int read = in.read(b, off, len); 52 | if (read > 0) { 53 | out.write(b, off, read); 54 | } 55 | return read; 56 | } 57 | 58 | public int read(byte[] b) throws IOException { 59 | return read(b, 0, b.length); 60 | } 61 | 62 | @Override 63 | /** Unsupported. */ 64 | public boolean markSupported() { 65 | return false; 66 | } 67 | 68 | @Override 69 | /** Unsupported. */ 70 | public void mark(int readlimit) { 71 | } 72 | 73 | @Override 74 | /** Unsupported. */ 75 | public void reset() throws IOException { 76 | throw new IOException("Unsupported"); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/DateUtilsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | 22 | import java.util.Calendar; 23 | 24 | public class DateUtilsTest { 25 | 26 | @Test 27 | public void testGetDayDifferenceOfReadableIntsPlusMinusNDays() { 28 | testGetDayDifferenceOfReadableIntsPlusMinusNDays(1); 29 | testGetDayDifferenceOfReadableIntsPlusMinusNDays(-1); 30 | } 31 | 32 | private void testGetDayDifferenceOfReadableIntsPlusMinusNDays(int sign) { 33 | Calendar calendar = Calendar.getInstance(); 34 | DateUtils.setTime(calendar, 12, 0, 0, 0); 35 | int today = DateUtils.getDayAsReadableInt(calendar); 36 | for (int i = 1; i <= 5000; i++) { 37 | DateUtils.addDays(calendar, sign); 38 | int day = DateUtils.getDayAsReadableInt(calendar); 39 | int diff = DateUtils.getDayDifferenceOfReadableInts(today, day); 40 | Assert.assertEquals(sign * i, diff); 41 | } 42 | } 43 | 44 | @Test 45 | public void testGetDayDifferenceOfReadableInts() { 46 | checkDayDifference(20110101, 20110101, 0); 47 | checkDayDifference(20110101, 20110102, 1); 48 | checkDayDifference(20110101, 20110103, 2); 49 | checkDayDifference(20110101, 20110201, 31); 50 | checkDayDifference(20110101, 20110301, 59); 51 | checkDayDifference(20110102, 20110101, -1); 52 | checkDayDifference(20110103, 20110101, -2); 53 | checkDayDifference(20110201, 20110101, -31); 54 | checkDayDifference(20110301, 20110101, -59); 55 | checkDayDifference(20111231, 20120101, 1); 56 | } 57 | 58 | private void checkDayDifference(int day1, int day2, int expectedDifference) { 59 | int actualDifference = DateUtils.getDayDifferenceOfReadableInts(day1, day2); 60 | Assert.assertEquals(expectedDifference, actualDifference); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/LimitedInputStreamTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials; 18 | 19 | import org.greenrobot.essentials.io.LimitedInputStream; 20 | import org.junit.Test; 21 | 22 | import java.io.BufferedInputStream; 23 | import java.io.ByteArrayInputStream; 24 | import java.io.IOException; 25 | 26 | import static org.junit.Assert.assertArrayEquals; 27 | import static org.junit.Assert.assertEquals; 28 | 29 | public class LimitedInputStreamTest { 30 | @Test 31 | public void testsBasics() throws IOException { 32 | ByteArrayInputStream in = new ByteArrayInputStream(new byte[]{1, 2, 3, 4}); 33 | LimitedInputStream limited = new LimitedInputStream(in, 2); 34 | BufferedInputStream buffered = new BufferedInputStream(limited); 35 | 36 | byte[] readBuffer = new byte[4]; 37 | assertEquals(2, buffered.read(readBuffer)); 38 | assertArrayEquals(new byte[]{1, 2, 0, 0}, readBuffer); 39 | assertEquals(-1, buffered.read()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/ObjectCacheTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials; 18 | 19 | import org.junit.Test; 20 | 21 | import static org.junit.Assert.*; 22 | 23 | public class ObjectCacheTest { 24 | @Test 25 | public void testBasics() { 26 | doTestBasics(ObjectCache.ReferenceType.SOFT); 27 | doTestBasics(ObjectCache.ReferenceType.STRONG); 28 | doTestBasics(ObjectCache.ReferenceType.WEAK); 29 | } 30 | 31 | private void doTestBasics(ObjectCache.ReferenceType referenceType) { 32 | ObjectCache cache = new ObjectCache(referenceType, 10, 0); 33 | String value = "foo"; 34 | String value2 = "bar"; 35 | String key = "mykey"; 36 | assertNull(cache.get(key)); 37 | assertNull(cache.put(key, value)); 38 | assertTrue(cache.containsKey(key)); 39 | assertTrue(cache.containsKeyWithValue(key)); 40 | assertEquals(value, cache.get(key)); 41 | assertEquals(value, cache.put(key, value2)); 42 | assertEquals(value2, cache.get(key)); 43 | assertEquals(value2, cache.remove(key)); 44 | assertNull(value2, cache.get(key)); 45 | assertFalse(cache.containsKey(key)); 46 | assertFalse(cache.containsKeyWithValue(key)); 47 | } 48 | 49 | 50 | @Test 51 | public void testMaxSize() { 52 | ObjectCache cache = createCacheWith4Entries(0); 53 | cache.put("5", "e"); 54 | assertEquals(4, cache.size()); 55 | assertNull(cache.get("1")); 56 | assertEquals(cache.get("5"), "e"); 57 | assertEquals(1, cache.getCountEvicted()); 58 | } 59 | 60 | @Test 61 | public void testEvictToTargetSize() { 62 | ObjectCache cache = createCacheWith4Entries(0); 63 | cache.evictToTargetSize(2); 64 | assertEquals(2, cache.size()); 65 | assertEquals(cache.get("3"), "c"); 66 | assertEquals(cache.get("4"), "d"); 67 | 68 | cache.evictToTargetSize(0); 69 | assertEquals(0, cache.size()); 70 | } 71 | 72 | @Test 73 | public void testExpired() throws InterruptedException { 74 | ObjectCache cache = new ObjectCache(ObjectCache.ReferenceType.STRONG, 4, 1); 75 | cache.put("1", "a"); 76 | Thread.sleep(3); 77 | assertNull(cache.get("1")); 78 | assertEquals(0, cache.size()); 79 | assertEquals(1, cache.getCountExpired()); 80 | } 81 | 82 | @Test 83 | public void testCleanUpObsoleteEntries() throws InterruptedException { 84 | // Use more than one entry to detect ConcurrentModificationException 85 | ObjectCache cache = createCacheWith4Entries(1); 86 | Thread.sleep(3); 87 | cache.checkCleanUpObsoleteEntries(); 88 | assertEquals(0, cache.size()); 89 | assertEquals(4, cache.getCountExpired()); 90 | } 91 | 92 | @Test 93 | public void testNotExpired() throws InterruptedException { 94 | ObjectCache cache = new ObjectCache(ObjectCache.ReferenceType.STRONG, 4, 1000); 95 | cache.put("1", "a"); 96 | Thread.sleep(3); 97 | assertEquals(cache.get("1"), "a"); 98 | assertEquals(0, cache.getCountExpired()); 99 | } 100 | 101 | private ObjectCache createCacheWith4Entries(int expirationMillis) { 102 | ObjectCache cache = new ObjectCache(ObjectCache.ReferenceType.STRONG, 4, expirationMillis); 103 | cache.put("1", "a"); 104 | cache.put("2", "b"); 105 | cache.put("3", "c"); 106 | cache.put("4", "d"); 107 | assertEquals(4, cache.size()); 108 | return cache; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/PrimitiveArrayUtilsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Before; 21 | import org.junit.BeforeClass; 22 | import org.junit.Test; 23 | 24 | import java.nio.ByteBuffer; 25 | import java.nio.ByteOrder; 26 | import java.util.Random; 27 | 28 | public class PrimitiveArrayUtilsTest { 29 | private byte[] bytes; 30 | private ByteBuffer byteBufferLE; 31 | private ByteBuffer byteBufferBE; 32 | 33 | static PrimitiveArrayUtils primitiveArrayUtilsPotentiallyUnsafe; 34 | PrimitiveArrayUtils primitiveArrayUtilsSafe = PrimitiveArrayUtils.getInstanceSafe(); 35 | 36 | @BeforeClass 37 | public static void setupBytes() { 38 | boolean hasUnsafe = PrimitiveArrayUtils.initUnsafeInstance(); 39 | System.out.println("Unsafe available: " + hasUnsafe); 40 | primitiveArrayUtilsPotentiallyUnsafe = PrimitiveArrayUtils.getInstance(); 41 | } 42 | 43 | @Before 44 | public void setUp() { 45 | bytes = new byte[102400]; 46 | new Random(42).nextBytes(bytes); 47 | 48 | byteBufferLE = ByteBuffer.wrap(bytes); 49 | byteBufferLE.order(ByteOrder.LITTLE_ENDIAN); 50 | byteBufferBE = ByteBuffer.wrap(bytes); 51 | } 52 | 53 | @Test 54 | public void testGetIntLE() { 55 | for (int i = 0; i < bytes.length - 3; i++) { 56 | int expected = byteBufferLE.getInt(i); 57 | int value = primitiveArrayUtilsPotentiallyUnsafe.getIntLE(bytes, i); 58 | Assert.assertEquals(expected, value); 59 | } 60 | } 61 | 62 | @Test 63 | public void testGetIntLEPlainJava() { 64 | for (int i = 0; i < bytes.length - 3; i++) { 65 | int expected = byteBufferLE.getInt(i); 66 | int value = primitiveArrayUtilsSafe.getIntLE(bytes, i); 67 | Assert.assertEquals(expected, value); 68 | } 69 | } 70 | 71 | @Test 72 | public void testGetLongLE() { 73 | for (int i = 0; i < bytes.length - 7; i++) { 74 | long expected = byteBufferLE.getLong(i); 75 | long value = primitiveArrayUtilsPotentiallyUnsafe.getLongLE(bytes, i); 76 | Assert.assertEquals(expected, value); 77 | } 78 | } 79 | 80 | @Test 81 | public void testGetLongLEPlainJava() { 82 | for (int i = 0; i < bytes.length - 7; i++) { 83 | long expected = byteBufferLE.getLong(i); 84 | long value = primitiveArrayUtilsSafe.getLongLE(bytes, i); 85 | Assert.assertEquals(expected, value); 86 | } 87 | } 88 | 89 | @Test 90 | public void testGetIntBE() { 91 | for (int i = 0; i < bytes.length - 3; i++) { 92 | int expected = byteBufferBE.getInt(i); 93 | int value = primitiveArrayUtilsPotentiallyUnsafe.getIntBE(bytes, i); 94 | Assert.assertEquals(expected, value); 95 | } 96 | } 97 | 98 | @Test 99 | public void testGetIntBEPlainJava() { 100 | for (int i = 0; i < bytes.length - 3; i++) { 101 | int expected = byteBufferBE.getInt(i); 102 | int value = primitiveArrayUtilsSafe.getIntBE(bytes, i); 103 | Assert.assertEquals(expected, value); 104 | } 105 | } 106 | 107 | @Test 108 | public void testGetLongBE() { 109 | for (int i = 0; i < bytes.length - 7; i++) { 110 | long expected = byteBufferBE.getLong(i); 111 | long value = primitiveArrayUtilsPotentiallyUnsafe.getLongBE(bytes, i); 112 | Assert.assertEquals(expected, value); 113 | } 114 | } 115 | 116 | @Test 117 | public void testGetLongBEPlainJava() { 118 | for (int i = 0; i < bytes.length - 7; i++) { 119 | long expected = byteBufferBE.getLong(i); 120 | long value = primitiveArrayUtilsSafe.getLongBE(bytes, i); 121 | Assert.assertEquals(expected, value); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/StringUtilsJvmTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials; 18 | 19 | import com.google.common.hash.HashCode; 20 | import org.junit.Test; 21 | 22 | import static org.junit.Assert.assertEquals; 23 | 24 | public class StringUtilsJvmTest { 25 | @Test 26 | public void testHexBig() { 27 | for (int i = 0; i < 256 * 256; i++) { 28 | byte[] bytes = {(byte) (i >> 8), (byte) i}; 29 | 30 | String hexExpected = HashCode.fromBytes(bytes).toString().toUpperCase(); 31 | String hex = StringUtils.hex(bytes); 32 | assertEquals(hexExpected, hex); 33 | 34 | byte[] bytes2 = StringUtils.parseHex(hex); 35 | assertEquals(bytes[0], bytes2[0]); 36 | assertEquals(bytes[1], bytes2[1]); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/StringUtilsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials; 18 | 19 | import org.junit.Test; 20 | 21 | import java.util.ArrayList; 22 | import java.util.Arrays; 23 | import java.util.List; 24 | 25 | import static org.junit.Assert.assertArrayEquals; 26 | import static org.junit.Assert.assertEquals; 27 | 28 | public class StringUtilsTest { 29 | private final static String LINES = "Line 1\nLine 2\n\nLine 4\r\nLine 5\r\n\r\nLine 7"; 30 | 31 | @Test 32 | public void testSplitLines() { 33 | String[] lines = StringUtils.splitLines(LINES, false); 34 | assertEquals(7, lines.length); 35 | 36 | assertEquals("Line 1", lines[0]); 37 | assertEquals("Line 2", lines[1]); 38 | assertEquals("", lines[2]); 39 | assertEquals("Line 4", lines[3]); 40 | assertEquals("Line 5", lines[4]); 41 | assertEquals("", lines[5]); 42 | assertEquals("Line 7", lines[6]); 43 | } 44 | 45 | @Test 46 | public void testSplitLinesSkipEmptyLines() { 47 | String[] lines = StringUtils.splitLines(LINES, true); 48 | assertEquals(5, lines.length); 49 | 50 | assertEquals("Line 1", lines[0]); 51 | assertEquals("Line 2", lines[1]); 52 | assertEquals("Line 4", lines[2]); 53 | assertEquals("Line 5", lines[3]); 54 | assertEquals("Line 7", lines[4]); 55 | } 56 | 57 | @Test 58 | public void testSplit() throws Exception { 59 | assertArrayEquals(ss("John", "Doe"), StringUtils.split("John Doe", ' ')); 60 | assertArrayEquals(ss("John", "", "Doe", ""), StringUtils.split("John Doe ", ' ')); 61 | assertArrayEquals(ss("", "John", "Doe", ""), StringUtils.split(" John Doe ", ' ')); 62 | assertArrayEquals(ss("John", "Christoph", "Doe"), StringUtils.split("John Christoph Doe", ' ')); 63 | assertArrayEquals(ss("John", "", "", "Doe"), StringUtils.split("John,,,Doe", ',')); 64 | assertArrayEquals(ss("John", "Doe", ""), StringUtils.split("John Doe ", ' ')); 65 | assertArrayEquals(ss("John", "", "", ""), StringUtils.split("John,,,", ',')); 66 | } 67 | 68 | private String[] ss(String... values) { 69 | return values; 70 | } 71 | 72 | @Test 73 | public void testFindLinesContaining() { 74 | String text = "LiXXXne 1\nLine 2\n\nLXXXine 4\r\nLine 5\r\nXXX\r\nLine 7"; 75 | List lines = StringUtils.findLinesContaining(text, "XXX"); 76 | assertEquals(3, lines.size()); 77 | 78 | assertEquals("LiXXXne 1", lines.get(0)); 79 | assertEquals("LXXXine 4", lines.get(1)); 80 | assertEquals("XXX", lines.get(2)); 81 | } 82 | 83 | @Test 84 | public void testConcatLines() { 85 | String[] lines = StringUtils.splitLines(LINES, false); 86 | ArrayList list = new ArrayList(); 87 | for (String line : lines) { 88 | list.add(line); 89 | } 90 | String concated = StringUtils.join(list, "\n"); 91 | assertEquals("Line 1\nLine 2\n\nLine 4\nLine 5\n\nLine 7", concated); 92 | } 93 | 94 | @Test 95 | public void testJoinIterable() { 96 | assertEquals("", StringUtils.join((Iterable) null, "blub")); 97 | List fooBarList = Arrays.asList("foo", "bar"); 98 | assertEquals("foo,bar", StringUtils.join(fooBarList, ",")); 99 | assertEquals("foo, bar", StringUtils.join(fooBarList, ", ")); 100 | } 101 | 102 | @Test 103 | public void testJoinIntArray() { 104 | assertEquals("", StringUtils.join((int[]) null, "blub")); 105 | int[] ints = {42, 23}; 106 | assertEquals("42,23", StringUtils.join(ints, ",")); 107 | assertEquals("42, 23", StringUtils.join(ints, ", ")); 108 | } 109 | 110 | @Test 111 | public void testJoinStringArray() { 112 | assertEquals("", StringUtils.join((String[]) null, "blub")); 113 | String[] fooBar = {"foo", "bar"}; 114 | assertEquals("foo,bar", StringUtils.join(fooBar, ",")); 115 | assertEquals("foo, bar", StringUtils.join(fooBar, ", ")); 116 | } 117 | 118 | @Test 119 | public void testEllipsize() { 120 | assertEquals("He...", StringUtils.ellipsize("Hello world", 5)); 121 | assertEquals("Hell>", StringUtils.ellipsize("Hello world", 5, ">")); 122 | } 123 | 124 | @Test 125 | public void testHex() { 126 | assertArrayEquals(new byte[] {0, 0x66, -1}, StringUtils.parseHex("0066FF")); 127 | } 128 | 129 | @Test 130 | public void testDigests() { 131 | String text = "The quick brown fox jumps over the lazy dog"; 132 | assertEquals("9E107D9D372BB6826BD81D3542A419D6", StringUtils.md5(text)); 133 | assertEquals("2FD4E1C67A2D28FCED849EE1BB76E7391B93EB12", StringUtils.sha1(text)); 134 | assertEquals("D7A8FBB307D7809469CA9ABCB0082E4F8D5651E46D3CDB762D02D0BF37C9E592", 135 | StringUtils.sha256(text)); 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/collections/LongHashMapTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.collections; 18 | 19 | import org.junit.Test; 20 | 21 | import java.util.Arrays; 22 | import java.util.Random; 23 | 24 | import static org.junit.Assert.*; 25 | 26 | public class LongHashMapTest { 27 | 28 | Random random; 29 | private String traceName; 30 | private long start; 31 | 32 | public LongHashMapTest() { 33 | this.random = new Random(); 34 | } 35 | 36 | @Test 37 | public void testLongHashMapSimple() { 38 | LongHashMap map = new LongHashMap<>(); 39 | 40 | map.put(1l << 33, "OK"); 41 | assertNull(map.get(0)); 42 | assertEquals("OK", map.get(1l << 33)); 43 | 44 | long keyLong = 0x7fffffffl << 33l + 14; 45 | assertNull(map.remove(keyLong)); 46 | map.put(keyLong, "OK"); 47 | assertTrue(map.containsKey(keyLong)); 48 | assertEquals("OK", map.remove(keyLong)); 49 | 50 | keyLong = Long.MAX_VALUE; 51 | map.put(keyLong, "OK"); 52 | assertTrue(map.containsKey(keyLong)); 53 | 54 | keyLong = 8064216579113853113l; 55 | map.put(keyLong, "OK"); 56 | assertTrue(map.containsKey(keyLong)); 57 | } 58 | 59 | @Test 60 | public void testLongHashMapRandom() { 61 | LongHashMap map = new LongHashMap<>(); 62 | testLongHashMapRandom(map); 63 | } 64 | 65 | @Test 66 | public void testLongHashMapRandom_Synchronized() { 67 | LongHashMap map = LongHashMap.createSynchronized(); 68 | testLongHashMapRandom(map); 69 | } 70 | 71 | private void testLongHashMapRandom(LongHashMap map) { 72 | for (int i = 0; i < 5000; i++) { 73 | long key = random.nextLong(); 74 | String value = "Value-" + key; 75 | map.put(key, value); 76 | assertTrue("" + key, map.containsKey(key)); 77 | 78 | int keyInt = (int) key; 79 | String valueInt = "Value-" + keyInt; 80 | map.put(keyInt, valueInt); 81 | assertTrue(map.containsKey(keyInt)); 82 | 83 | assertEquals(value, map.get(key)); 84 | assertEquals(valueInt, map.get(keyInt)); 85 | 86 | assertEquals(value, map.remove(key)); 87 | assertEquals(valueInt, map.remove(keyInt)); 88 | 89 | assertNull(map.get(key)); 90 | assertNull(map.get(keyInt)); 91 | } 92 | } 93 | 94 | @Test 95 | public void testKeys() { 96 | LongHashMap map = new LongHashMap(); 97 | map.put(0, "a"); 98 | map.put(-98, "b"); 99 | map.put(666, "c"); 100 | map.put(Long.MAX_VALUE, "d"); 101 | map.remove(666); 102 | 103 | long[] keys = map.keys(); 104 | assertEquals(3, keys.length); 105 | Arrays.sort(keys); 106 | assertEquals(-98, keys[0]); 107 | assertEquals(0, keys[1]); 108 | assertEquals(Long.MAX_VALUE, keys[2]); 109 | } 110 | 111 | @Test 112 | public void testEntries() { 113 | LongHashMap map = new LongHashMap(); 114 | map.put(0, "a"); 115 | map.put(-98, "b"); 116 | map.put(666, "c"); 117 | map.put(Long.MAX_VALUE, "d"); 118 | map.remove(666); 119 | 120 | LongHashMap.Entry[] entries = map.entries(); 121 | assertEquals(3, entries.length); 122 | 123 | String all = ""; 124 | for (LongHashMap.Entry entry : entries) { 125 | all += "(" + entry.key + "=" + entry.value + ")"; 126 | } 127 | 128 | assertTrue(all, all.contains("(0=a)")); 129 | assertTrue(all, all.contains("(-98=b)")); 130 | assertTrue(all, all.contains("(" + Long.MAX_VALUE + "=d)")); 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/collections/LongHashSetTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.collections; 18 | 19 | import org.junit.Test; 20 | 21 | import java.util.Arrays; 22 | import java.util.Random; 23 | 24 | import static org.junit.Assert.assertEquals; 25 | import static org.junit.Assert.assertFalse; 26 | import static org.junit.Assert.assertTrue; 27 | 28 | public class LongHashSetTest { 29 | 30 | Random random; 31 | private String traceName; 32 | private long start; 33 | 34 | public LongHashSetTest() { 35 | this.random = new Random(); 36 | } 37 | 38 | @Test 39 | public void testLongHashSetSimple() { 40 | LongHashSet set = new LongHashSet(); 41 | 42 | set.add(1l << 33); 43 | assertFalse(set.contains(0)); 44 | assertTrue(set.contains(1l << 33)); 45 | 46 | long keyLong = 0x7fffffffl << 33l + 14; 47 | assertFalse(set.remove(keyLong)); 48 | set.add(keyLong); 49 | assertTrue(set.contains(keyLong)); 50 | assertTrue(set.remove(keyLong)); 51 | assertFalse(set.remove(keyLong)); 52 | 53 | keyLong = Long.MAX_VALUE; 54 | set.add(keyLong); 55 | assertTrue(set.contains(keyLong)); 56 | 57 | keyLong = 8064216579113853113l; 58 | set.add(keyLong); 59 | assertTrue(set.contains(keyLong)); 60 | } 61 | 62 | @Test 63 | public void testLongHashMapRandom() { 64 | LongHashSet set = new LongHashSet(); 65 | for (int i = 0; i < 5000; i++) { 66 | long key = random.nextLong(); 67 | set.add(key); 68 | assertTrue(set.contains(key)); 69 | 70 | int keyInt = (int) key; 71 | set.add(keyInt); 72 | assertTrue(set.contains(keyInt)); 73 | 74 | assertTrue(set.remove(key)); 75 | if(key!=keyInt) { 76 | assertTrue(set.remove(keyInt)); 77 | } 78 | 79 | assertFalse(set.remove(key)); 80 | assertFalse(set.remove(keyInt)); 81 | } 82 | } 83 | 84 | @Test 85 | public void testKeys() { 86 | LongHashSet set = new LongHashSet(); 87 | set.add(0); 88 | set.add(-98); 89 | set.add(666); 90 | set.add(Long.MAX_VALUE); 91 | set.remove(666); 92 | 93 | long[] keys = set.keys(); 94 | assertEquals(3, keys.length); 95 | Arrays.sort(keys); 96 | assertEquals(-98, keys[0]); 97 | assertEquals(0, keys[1]); 98 | assertEquals(Long.MAX_VALUE, keys[2]); 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/collections/MultimapTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2016 Markus Junginger, greenrobot (http://greenrobot.org) 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 | package org.greenrobot.essentials.collections; 18 | 19 | import org.greenrobot.essentials.collections.Multimap.ListType; 20 | import org.greenrobot.essentials.collections.MultimapSet.SetType; 21 | import org.junit.Before; 22 | import org.junit.Test; 23 | import org.junit.runner.RunWith; 24 | import org.junit.runners.Parameterized; 25 | 26 | import java.util.Arrays; 27 | import java.util.Collection; 28 | import java.util.HashSet; 29 | import java.util.List; 30 | 31 | import static org.junit.Assert.*; 32 | 33 | @RunWith(Parameterized.class) 34 | public class MultimapTest { 35 | 36 | @Parameterized.Parameters 37 | public static Collection data() { 38 | return Arrays.asList( 39 | new Object[]{Multimap.create()}, 40 | new Object[]{Multimap.create(ListType.THREAD_SAFE)}, 41 | new Object[]{Multimap.create(ListType.LINKED)}, 42 | new Object[]{MultimapSet.create()}, 43 | new Object[]{MultimapSet.create(SetType.THREAD_SAFE)} 44 | ); 45 | } 46 | 47 | @Parameterized.Parameter 48 | public AbstractMultimap> multimap; 49 | 50 | @Before 51 | public void setup() { 52 | multimap.clear(); 53 | multimap.putElement("a", "1"); 54 | multimap.putElement("a", "2"); 55 | multimap.putElement("a", "3"); 56 | } 57 | 58 | @Test 59 | public void testPutElementAndGet() { 60 | Collection collection = multimap.get("a"); 61 | assertEquals(3, collection.size()); 62 | if (collection instanceof List) { 63 | List list = (List) collection; 64 | assertEquals("1", list.get(0)); 65 | assertEquals("2", list.get(1)); 66 | assertEquals("3", list.get(2)); 67 | } 68 | } 69 | 70 | @Test 71 | public void testContains() { 72 | assertTrue(multimap.containsElement("1")); 73 | assertFalse(multimap.containsElement("4")); 74 | 75 | assertTrue(multimap.containsElement("a", "1")); 76 | assertFalse(multimap.containsElement("a", "4")); 77 | } 78 | 79 | @Test 80 | public void testRemove() { 81 | assertTrue(multimap.removeElement("a", "2")); 82 | assertFalse(multimap.removeElement("a", "2")); 83 | 84 | assertTrue(multimap.removeElement("a", "1")); 85 | assertTrue(multimap.containsKey("a")); 86 | assertTrue(multimap.removeElement("a", "3")); 87 | assertFalse(multimap.containsKey("a")); 88 | } 89 | 90 | @Test 91 | public void testPutElements() { 92 | Collection collection = new HashSet<>(); 93 | collection.add("4"); 94 | collection.add("5"); 95 | assertTrue(multimap.putElements("a", collection)); 96 | assertEquals(5, multimap.get("a").size()); 97 | assertTrue(multimap.containsElement("a", "4")); 98 | assertTrue(multimap.containsElement("a", "5")); 99 | } 100 | 101 | @Test 102 | public void testValuesElements() { 103 | multimap.putElement("b", "10"); 104 | multimap.putElement("b", "11"); 105 | 106 | Collection allStrings = multimap.valuesElements(); 107 | assertEquals(5, allStrings.size()); 108 | assertTrue(allStrings.contains("1")); 109 | assertTrue(allStrings.contains("10")); 110 | } 111 | 112 | @Test 113 | public void testCountElements() { 114 | multimap.putElement("b", "10"); 115 | multimap.putElement("b", "11"); 116 | 117 | assertEquals(5, multimap.countElements()); 118 | assertEquals(3, multimap.countElements("a")); 119 | assertEquals(2, multimap.countElements("b")); 120 | assertEquals(0, multimap.countElements("c")); 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/hash/AbstractAllChecksumTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.hash; 18 | 19 | import org.junit.runners.Parameterized; 20 | 21 | import java.util.Arrays; 22 | import java.util.Collection; 23 | import java.util.zip.Adler32; 24 | import java.util.zip.Checksum; 25 | 26 | public abstract class AbstractAllChecksumTest { 27 | @Parameterized.Parameter 28 | public Checksum checksum; 29 | 30 | @Parameterized.Parameters(name = "{0}") 31 | public static Collection alignments() { 32 | return Arrays.asList(new Object[][]{ 33 | {new Adler32()}, 34 | {new FNV32()}, 35 | {new FNV64()}, 36 | {new Murmur3A()}, 37 | {new Murmur3F()}}); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/hash/AbstractChecksumTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.hash; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Before; 21 | import org.junit.Test; 22 | 23 | import java.util.Random; 24 | import java.util.zip.Checksum; 25 | 26 | public abstract class AbstractChecksumTest { 27 | protected final static byte[] INPUT4 = {(byte) 0xcc, 0x24, 0x31, (byte) 0xc4}; 28 | protected final static byte[] INPUT16 = {(byte) 0xe0, 0x4d, (byte) 0x9f, (byte) 0xcb, (byte) 0xd5, 0x6b, 29 | (byte) 0xb9, 0x53, 0x42, (byte) 0x87, 0x08, 0x36, 0x77, 0x23, 0x01, 0}; 30 | 31 | protected Checksum checksum; 32 | 33 | protected AbstractChecksumTest(Checksum checksum) { 34 | this.checksum = checksum; 35 | } 36 | 37 | @Before 38 | public void setUp() { 39 | checksum.reset(); 40 | } 41 | 42 | @Test 43 | public void testBasics() { 44 | long initialHash = checksum.getValue(); 45 | 46 | for (int b : INPUT4) { 47 | checksum.update(b); 48 | Assert.assertNotEquals(initialHash, checksum.getValue()); 49 | } 50 | long hash = checksum.getValue(); 51 | 52 | checksum.reset(); 53 | Assert.assertEquals(initialHash, checksum.getValue()); 54 | 55 | checksum.update(INPUT4, 0, INPUT4.length); 56 | Assert.assertEquals(hash, checksum.getValue()); 57 | } 58 | 59 | @Test 60 | public void testGetValueStable() { 61 | checksum.update(INPUT16, 0, INPUT16.length); 62 | long hash = checksum.getValue(); 63 | // Calling checksum.getValue() twice should not change hash 64 | Assert.assertEquals(hash, checksum.getValue()); 65 | } 66 | 67 | public void testExpectedHash(long expectedFor0, long expectedForInput4, long expectedForInput16) { 68 | checksum.update(0); 69 | checksum.update(0); 70 | checksum.update(0); 71 | checksum.update(0); 72 | Assert.assertEquals("0 (int)", expectedFor0, checksum.getValue()); 73 | 74 | checksum.reset(); 75 | checksum.update(INPUT4, 0, INPUT4.length); 76 | Assert.assertEquals("I4", expectedForInput4, checksum.getValue()); 77 | 78 | checksum.reset(); 79 | checksum.update(INPUT16, 0, INPUT16.length); 80 | Assert.assertEquals("I16", expectedForInput16, checksum.getValue()); 81 | } 82 | 83 | @Test 84 | public void testRestUnaligned() { 85 | checksum.update(42); 86 | long hash = checksum.getValue(); 87 | checksum.reset(); 88 | checksum.update(42); 89 | Assert.assertEquals(hash, checksum.getValue()); 90 | } 91 | 92 | @Test 93 | public void testMixedUnaligned() { 94 | checksum.update(INPUT16, 0, INPUT16.length); 95 | long hash = checksum.getValue(); 96 | 97 | checksum.reset(); 98 | checksum.update(INPUT16, 0, 2); 99 | checksum.update(INPUT16[2]); 100 | checksum.update(INPUT16, 3, 11); 101 | checksum.update(INPUT16[14]); 102 | checksum.update(INPUT16[15]); 103 | Assert.assertEquals(hash, checksum.getValue()); 104 | } 105 | 106 | @Test 107 | public void testTrailingZero() { 108 | long lastHash = checksum.getValue(); 109 | Assert.assertEquals(0, INPUT16[INPUT16.length - 1]); 110 | for (int b : INPUT16) { 111 | checksum.update(b); 112 | long hash = checksum.getValue(); 113 | Assert.assertNotEquals(lastHash, hash); 114 | lastHash = hash; 115 | } 116 | } 117 | 118 | @Test 119 | public void testComparePerByteVsByteArray() { 120 | byte[] bytes = new byte[1024]; 121 | new Random(42).nextBytes(bytes); 122 | 123 | for (int i = 0; i <= bytes.length; i++) { 124 | checksum.reset(); 125 | for (int j = 0; j < i; j++) { 126 | checksum.update(bytes[j]); 127 | } 128 | long expected = checksum.getValue(); 129 | 130 | checksum.reset(); 131 | checksum.update(bytes, 0, i); 132 | Assert.assertEquals("Iteration " + i, expected, checksum.getValue()); 133 | } 134 | 135 | for (int i = 0; i <= bytes.length; i++) { 136 | checksum.reset(); 137 | for (int j = i; j < bytes.length ; j++) { 138 | checksum.update(bytes[j]); 139 | } 140 | long expected = checksum.getValue(); 141 | 142 | checksum.reset(); 143 | checksum.update(bytes, i, bytes.length - i); 144 | Assert.assertEquals("Iteration " + i + " (" + (bytes.length - i) + ")", expected, checksum.getValue()); 145 | } 146 | 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/hash/ChecksumStreamTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.hash; 18 | 19 | import org.greenrobot.essentials.io.IoUtils; 20 | import org.junit.Assert; 21 | import org.junit.Test; 22 | import org.junit.runner.RunWith; 23 | import org.junit.runners.Parameterized; 24 | 25 | import java.io.ByteArrayInputStream; 26 | import java.io.ByteArrayOutputStream; 27 | import java.io.IOException; 28 | import java.util.Random; 29 | import java.util.zip.CheckedInputStream; 30 | import java.util.zip.CheckedOutputStream; 31 | 32 | /** 33 | * Tests compatibility with CheckedOutputStream and CheckedInputStream. 34 | */ 35 | @RunWith(Parameterized.class) 36 | public class ChecksumStreamTest extends AbstractAllChecksumTest { 37 | @Test 38 | public void testChecksumStreams() throws IOException { 39 | byte[] content = new byte[33333]; 40 | new Random().nextBytes(content); 41 | 42 | Murmur3F murmur3F = new Murmur3F(); 43 | murmur3F.update(content); 44 | String hash = murmur3F.getValueHexString(); 45 | 46 | murmur3F.reset(); 47 | CheckedOutputStream out = new CheckedOutputStream(new ByteArrayOutputStream(), murmur3F); 48 | out.write(content); 49 | Assert.assertEquals(hash, murmur3F.getValueHexString()); 50 | 51 | murmur3F.reset(); 52 | CheckedInputStream in = new CheckedInputStream(new ByteArrayInputStream(content), murmur3F); 53 | IoUtils.readAllBytes(in); 54 | Assert.assertEquals(hash, murmur3F.getValueHexString()); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/hash/FNV32Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.hash; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | 22 | public class FNV32Test extends AbstractChecksumTest { 23 | private final static byte[] INPUT32_ZERO1 = {(byte) 0xcc, 0x24, 0x31, (byte) 0xc4}; 24 | private final static byte[] INPUT32_ZERO2 = {(byte) 0xe0, 0x4d, (byte) 0x9f, (byte) 0xcb}; 25 | 26 | public FNV32Test() { 27 | super(new FNV32()); 28 | } 29 | 30 | @Test 31 | public void testFnv32UpdateZeroHash() { 32 | for (int b : INPUT32_ZERO1) { 33 | checksum.update(b); 34 | } 35 | Assert.assertEquals(0, checksum.getValue()); 36 | 37 | checksum.reset(); 38 | for (int b : INPUT32_ZERO2) { 39 | checksum.update(b); 40 | } 41 | Assert.assertEquals(0, checksum.getValue()); 42 | } 43 | 44 | @Test 45 | public void testFnv32UpdateBytesZeroHash() { 46 | checksum.update(INPUT32_ZERO1, 0, INPUT32_ZERO1.length); 47 | Assert.assertEquals(0, checksum.getValue()); 48 | 49 | checksum.reset(); 50 | checksum.update(INPUT32_ZERO2, 0, INPUT32_ZERO1.length); 51 | Assert.assertEquals(0, checksum.getValue()); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/hash/FNV64Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.hash; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | 22 | public class FNV64Test extends AbstractChecksumTest { 23 | private final static byte[] INPUT64_ZERO = {(byte) 0xd5, 0x6b, (byte) 0xb9, 0x53, 0x42, (byte) 0x87, 0x08, 0x36}; 24 | 25 | public FNV64Test() { 26 | super(new FNV64()); 27 | } 28 | 29 | @Test 30 | public void testFnv64UpdateZeroHash() { 31 | for (int b : INPUT64_ZERO) { 32 | checksum.update(b); 33 | } 34 | Assert.assertEquals(0, checksum.getValue()); 35 | } 36 | 37 | @Test 38 | public void testFnv64UpdateBytesZeroHash() { 39 | checksum.update(INPUT64_ZERO, 0, INPUT64_ZERO.length); 40 | Assert.assertEquals(0, checksum.getValue()); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/hash/Murmur3ASpeedTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.hash; 18 | 19 | import org.junit.Ignore; 20 | import org.junit.Test; 21 | import org.junit.runner.RunWith; 22 | import org.junit.runners.Parameterized; 23 | 24 | import java.util.Arrays; 25 | import java.util.Collection; 26 | import java.util.Random; 27 | 28 | @RunWith(Parameterized.class) 29 | @Ignore 30 | public class Murmur3ASpeedTest { 31 | public static final int ITERATIONS = 1000; 32 | 33 | @Parameterized.Parameter 34 | public int alignment; 35 | 36 | @Parameterized.Parameters(name = "{0}-aligned") 37 | public static Collection alignments() { 38 | return Arrays.asList(new Object[][]{{0}, {1}, {2}, {3}}); 39 | } 40 | 41 | @Test 42 | public void measureByteArrayPerformance() { 43 | System.out.println("ByteArray align=" + alignment + "\t----------------------------------------------------"); 44 | Murmur3A checksum = new Murmur3A(); 45 | byte[] data = new byte[1024 * 1024]; // 1MB 46 | new Random(23).nextBytes(data); 47 | 48 | // Warm up a bit 49 | checksum.update(data); 50 | 51 | long hash; 52 | long totalTime = 0; 53 | for (int i = 0; i < ITERATIONS; i++) { 54 | prepareChecksum(checksum); 55 | long start = System.nanoTime(); 56 | checksum.update(data); 57 | hash = checksum.getValue(); 58 | totalTime += System.nanoTime() - start; 59 | if ((i + 1) % (ITERATIONS / 10) == 0) { 60 | printStats(i + 1, data.length, totalTime, hash); 61 | } 62 | } 63 | } 64 | 65 | @Test 66 | public void measureShortArrayPerformance() { 67 | System.out.println("ShortArray align=" + alignment + "\t----------------------------------------------------"); 68 | Murmur3A checksum = new Murmur3A(); 69 | short[] data = new short[512 * 1024]; // 1MB 70 | Random random = new Random(23); 71 | for (int i = 0; i < data.length; i++) { 72 | data[i] = (short) random.nextInt(); 73 | } 74 | 75 | // Warm up a bit 76 | checksum.updateShort(data); 77 | 78 | long hash; 79 | long totalTime = 0; 80 | for (int i = 0; i < ITERATIONS; i++) { 81 | prepareChecksum(checksum); 82 | long start = System.nanoTime(); 83 | checksum.updateShort(data); 84 | hash = checksum.getValue(); 85 | totalTime += System.nanoTime() - start; 86 | if ((i + 1) % (ITERATIONS / 10) == 0) { 87 | printStats(i + 1, data.length * 2, totalTime, hash); 88 | } 89 | } 90 | } 91 | 92 | @Test 93 | public void measureIntArrayPerformance() { 94 | System.out.println("IntArray align=" + alignment + "\t----------------------------------------------------"); 95 | Murmur3A checksum = new Murmur3A(); 96 | int[] data = new int[256 * 1024]; // 1MB 97 | Random random = new Random(23); 98 | for (int i = 0; i < data.length; i++) { 99 | data[i] = random.nextInt(); 100 | } 101 | 102 | // Warm up a bit 103 | checksum.updateInt(data); 104 | 105 | long hash; 106 | long totalTime = 0; 107 | for (int i = 0; i < ITERATIONS; i++) { 108 | prepareChecksum(checksum); 109 | long start = System.nanoTime(); 110 | checksum.updateInt(data); 111 | hash = checksum.getValue(); 112 | totalTime += System.nanoTime() - start; 113 | if ((i + 1) % (ITERATIONS / 10) == 0) { 114 | printStats(i + 1, data.length * 4, totalTime, hash); 115 | } 116 | } 117 | } 118 | 119 | @Test 120 | public void measureLongArrayPerformance() { 121 | System.out.println("LongArray align=" + alignment + "\t----------------------------------------------------"); 122 | Murmur3A checksum = new Murmur3A(); 123 | long[] data = new long[128 * 1024]; // 1MB 124 | Random random = new Random(23); 125 | for (int i = 0; i < data.length; i++) { 126 | data[i] = random.nextLong(); 127 | } 128 | 129 | // Warm up a bit 130 | checksum.updateLong(data); 131 | 132 | long hash; 133 | long totalTime = 0; 134 | for (int i = 0; i < ITERATIONS; i++) { 135 | prepareChecksum(checksum); 136 | long start = System.nanoTime(); 137 | checksum.updateLong(data); 138 | hash = checksum.getValue(); 139 | totalTime += System.nanoTime() - start; 140 | if ((i + 1) % (ITERATIONS / 10) == 0) { 141 | printStats(i + 1, data.length * 4, totalTime, hash); 142 | } 143 | } 144 | } 145 | 146 | private void prepareChecksum(Murmur3A checksum) { 147 | checksum.reset(); 148 | for (int j = 0; j < alignment; j++) { 149 | checksum.update(0); 150 | } 151 | } 152 | 153 | private void printStats(int iterations, int bytesPerIteration, long totalTime, long hash) { 154 | long ms = totalTime / 1000000; 155 | double mb = ((double) iterations) * bytesPerIteration / 1024 / 1024; 156 | int mbs = (int) (mb / (totalTime / 1000000000d) + 0.5f); 157 | 158 | System.out.println(iterations + ":\t\tms: " + ms + "\t\tMB: " + mb + "\t\tMB/s: " + mbs + "\t\thash: " + hash); 159 | } 160 | 161 | } 162 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/hash/PrimitiveDataChecksumTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.hash; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Before; 21 | import org.junit.Test; 22 | import org.junit.runner.RunWith; 23 | import org.junit.runners.Parameterized; 24 | 25 | import java.io.ByteArrayOutputStream; 26 | import java.io.DataOutputStream; 27 | import java.util.zip.Adler32; 28 | 29 | @RunWith(Parameterized.class) 30 | public class PrimitiveDataChecksumTest extends AbstractAllChecksumTest { 31 | 32 | private PrimitiveDataChecksum primitiveDataChecksum; 33 | 34 | @Before 35 | public void setUp() { 36 | primitiveDataChecksum = new PrimitiveDataChecksum(checksum); 37 | } 38 | 39 | @Test 40 | public void testUpdateInt() throws Exception { 41 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 42 | new DataOutputStream(out).writeInt(1234567890); 43 | long expected = getHashAndReset(out); 44 | primitiveDataChecksum.updateInt(1234567890); 45 | Assert.assertEquals(expected, primitiveDataChecksum.getValue()); 46 | } 47 | 48 | @Test 49 | public void testUpdateBoolean() throws Exception { 50 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 51 | new DataOutputStream(out).writeBoolean(true); 52 | long expected = getHashAndReset(out); 53 | primitiveDataChecksum.updateBoolean(true); 54 | Assert.assertEquals(expected, primitiveDataChecksum.getValue()); 55 | } 56 | 57 | @Test 58 | public void testUpdateShort() throws Exception { 59 | short input = Short.MIN_VALUE + 12345; 60 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 61 | new DataOutputStream(out).writeShort(input); 62 | long expected = getHashAndReset(out); 63 | primitiveDataChecksum.updateShort(input); 64 | Assert.assertEquals(expected, primitiveDataChecksum.getValue()); 65 | } 66 | 67 | @Test 68 | public void testUpdateLong() throws Exception { 69 | long input = Long.MIN_VALUE + 123456789; 70 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 71 | new DataOutputStream(out).writeLong(input); 72 | long expected = getHashAndReset(out); 73 | primitiveDataChecksum.updateLong(input); 74 | Assert.assertEquals(expected, primitiveDataChecksum.getValue()); 75 | } 76 | 77 | @Test 78 | public void testUpdateFloat() throws Exception { 79 | float input = (float) -Math.PI; 80 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 81 | new DataOutputStream(out).writeFloat(input); 82 | long expected = getHashAndReset(out); 83 | primitiveDataChecksum.updateFloat(input); 84 | Assert.assertEquals(expected, primitiveDataChecksum.getValue()); 85 | } 86 | 87 | @Test 88 | public void testUpdateDouble() throws Exception { 89 | double input = -Math.PI; 90 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 91 | new DataOutputStream(out).writeDouble(input); 92 | long expected = getHashAndReset(out); 93 | primitiveDataChecksum.updateDouble(input); 94 | Assert.assertEquals(expected, primitiveDataChecksum.getValue()); 95 | } 96 | 97 | @Test 98 | public void testNullValues() throws Exception { 99 | PrimitiveDataChecksum checksum = new PrimitiveDataChecksum(new Adler32()); 100 | long before = checksum.getValue(); 101 | checksum.update((byte[]) null); 102 | checksum.update((int[]) null); 103 | checksum.update((short[]) null); 104 | checksum.update((long[]) null); 105 | checksum.update((float[]) null); 106 | checksum.update((double[]) null); 107 | checksum.updateUtf8((String) null); 108 | checksum.updateUtf8((String[]) null); 109 | Assert.assertEquals(before, checksum.getValue()); 110 | } 111 | 112 | private long getHashAndReset(ByteArrayOutputStream out) { 113 | primitiveDataChecksum.reset(); 114 | byte[] bytes = out.toByteArray(); 115 | primitiveDataChecksum.update(bytes, 0, bytes.length); 116 | long value = primitiveDataChecksum.getValue(); 117 | primitiveDataChecksum.reset(); 118 | return value; 119 | } 120 | 121 | 122 | } 123 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/hash/otherhashes/MessageDigestChecksum.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.hash.otherhashes; 18 | 19 | import org.greenrobot.essentials.PrimitiveArrayUtils; 20 | 21 | import java.security.MessageDigest; 22 | import java.security.NoSuchAlgorithmException; 23 | import java.util.zip.Checksum; 24 | 25 | /** Warpper for MessageDigest. */ 26 | public class MessageDigestChecksum implements Checksum { 27 | private final MessageDigest digest; 28 | private PrimitiveArrayUtils primitiveArrayUtils = PrimitiveArrayUtils.getInstance(); 29 | 30 | public MessageDigestChecksum(MessageDigest digest) { 31 | this.digest = digest; 32 | } 33 | 34 | public MessageDigestChecksum(String algo) { 35 | try { 36 | digest = (MessageDigest.getInstance(algo)); 37 | } catch (NoSuchAlgorithmException e) { 38 | throw new RuntimeException(e); 39 | } 40 | } 41 | 42 | @Override 43 | public void update(int b) { 44 | throw new RuntimeException("Not implemented"); 45 | } 46 | 47 | @Override 48 | public void update(byte[] b, int off, int len) { 49 | digest.update(b, off, len); 50 | } 51 | 52 | @Override 53 | public long getValue() { 54 | return primitiveArrayUtils.getLongLE(digest.digest(), 0); 55 | } 56 | 57 | @Override 58 | public void reset() { 59 | digest.reset(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/hash/otherhashes/Murmur2Checksum.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.hash.otherhashes; 18 | 19 | import java.util.zip.Checksum; 20 | 21 | /** TODO */ 22 | public class Murmur2Checksum implements Checksum { 23 | Long hash; 24 | 25 | @Override 26 | public void update(int b) { 27 | throw new RuntimeException("Not implemented"); 28 | } 29 | 30 | @Override 31 | public void update(byte[] b, int off, int len) { 32 | if (hash != null) { 33 | throw new RuntimeException("No hash building available"); 34 | } 35 | hash = 0xffffffffL & MurmurHash2.hash(b, 0x9747b28c); 36 | } 37 | 38 | @Override 39 | public long getValue() { 40 | return hash; 41 | } 42 | 43 | @Override 44 | public void reset() { 45 | hash = null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/hash/otherhashes/Murmur2bChecksum.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.hash.otherhashes; 18 | 19 | import java.util.zip.Checksum; 20 | 21 | /** TODO */ 22 | public class Murmur2bChecksum implements Checksum { 23 | Long hash; 24 | 25 | @Override 26 | public void update(int b) { 27 | throw new RuntimeException("Not implemented"); 28 | } 29 | 30 | @Override 31 | public void update(byte[] b, int off, int len) { 32 | if (hash != null) { 33 | throw new RuntimeException("No hash building available"); 34 | } 35 | hash = 0xffffffffL & MurmurHash2b.hash32(b, off, len); 36 | } 37 | 38 | @Override 39 | public long getValue() { 40 | return hash; 41 | } 42 | 43 | @Override 44 | public void reset() { 45 | hash = null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/hash/otherhashes/Murmur3aGuavaChecksum.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.hash.otherhashes; 18 | 19 | import com.google.common.hash.Hasher; 20 | import com.google.common.hash.Hashing; 21 | 22 | import java.util.zip.Checksum; 23 | 24 | /** TODO */ 25 | public class Murmur3aGuavaChecksum implements Checksum { 26 | Hasher hasher = Hashing.murmur3_32().newHasher(); 27 | 28 | @Override 29 | public void update(int b) { 30 | hasher.putByte((byte) b); 31 | } 32 | 33 | @Override 34 | public void update(byte[] b, int off, int len) { 35 | hasher.putBytes(b, off, len); 36 | } 37 | 38 | @Override 39 | public long getValue() { 40 | return 0xffffffffL & hasher.hash().asInt(); 41 | } 42 | 43 | @Override 44 | public void reset() { 45 | hasher = Hashing.murmur3_32().newHasher(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/hash/otherhashes/Murmur3fGuavaChecksum.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.hash.otherhashes; 18 | 19 | import com.google.common.hash.Hasher; 20 | import com.google.common.hash.Hashing; 21 | 22 | import java.util.zip.Checksum; 23 | 24 | /** TODO */ 25 | public class Murmur3fGuavaChecksum implements Checksum { 26 | Hasher hasher= Hashing.murmur3_128().newHasher(); 27 | 28 | @Override 29 | public void update(int b) { 30 | hasher.putByte((byte)b); 31 | } 32 | 33 | @Override 34 | public void update(byte[] b, int off, int len) { 35 | hasher.putBytes(b,off,len); 36 | } 37 | 38 | @Override 39 | public long getValue() { 40 | return hasher.hash().asLong(); 41 | } 42 | 43 | @Override 44 | public void reset() { 45 | hasher= Hashing.murmur3_128().newHasher(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/hash/otherhashes/MurmurHash2.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.hash.otherhashes; 18 | 19 | // Ported by Derek Young from the C version (specifically the endian-neutral 20 | // version) from: 21 | // http://murmurhash.googlepages.com/ 22 | // 23 | // released to the public domain - dmy999@gmail.com 24 | public class MurmurHash2 { 25 | 26 | @SuppressWarnings("fallthrough") 27 | public static int hash(byte[] data, int seed) { 28 | // 'm' and 'r' are mixing constants generated offline. 29 | // They're not really 'magic', they just happen to work well. 30 | int m = 0x5bd1e995; 31 | int r = 24; 32 | 33 | // Initialize the hash to a 'random' value 34 | int len = data.length; 35 | int h = seed ^ len; 36 | 37 | int i = 0; 38 | while (len >= 4) { 39 | int k = data[i + 0] & 0xFF; 40 | k |= (data[i + 1] & 0xFF) << 8; 41 | k |= (data[i + 2] & 0xFF) << 16; 42 | k |= (data[i + 3] & 0xFF) << 24; 43 | 44 | k *= m; 45 | k ^= k >>> r; 46 | k *= m; 47 | 48 | h *= m; 49 | h ^= k; 50 | 51 | i += 4; 52 | len -= 4; 53 | } 54 | 55 | switch (len) { 56 | case 3: h ^= (data[i + 2] & 0xFF) << 16; 57 | case 2: h ^= (data[i + 1] & 0xFF) << 8; 58 | case 1: h ^= (data[i + 0] & 0xFF); 59 | h *= m; 60 | } 61 | 62 | h ^= h >>> 13; 63 | h *= m; 64 | h ^= h >>> 15; 65 | 66 | return h; 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/hash/otherhashes/MurmurHash2b.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.hash.otherhashes; 18 | /** 19 | * This is a very fast, non-cryptographic hash suitable for general hash-based 20 | * lookup. See http://murmurhash.googlepages.com/ for more details. 21 | *

22 | * The C version of MurmurHash 2.0 found at that site was ported to Java by 23 | * Andrzej Bialecki (ab at getopt org). 24 | *

25 | *

26 | * The code from getopt.org was adapted by Mark Harwood in the form here as one of a pluggable choice of 27 | * hashing functions as the core function had to be adapted to work with BytesRefs with offsets and lengths 28 | * rather than raw byte arrays. 29 | *

30 | * @lucene.experimental 31 | */ 32 | public final class MurmurHash2b { 33 | 34 | public static final MurmurHash2 INSTANCE = new MurmurHash2(); 35 | 36 | private MurmurHash2b() {} 37 | 38 | public static int hash(byte[] data, int seed, int offset, int len) { 39 | int m = 0x5bd1e995; 40 | int r = 24; 41 | int h = seed ^ len; 42 | int len_4 = len >> 2; 43 | for (int i = 0; i < len_4; i++) { 44 | int i_4 = offset + (i << 2); 45 | int k = data[i_4 + 3]; 46 | k = k << 8; 47 | k = k | (data[i_4 + 2] & 0xff); 48 | k = k << 8; 49 | k = k | (data[i_4 + 1] & 0xff); 50 | k = k << 8; 51 | k = k | (data[i_4 + 0] & 0xff); 52 | k *= m; 53 | k ^= k >>> r; 54 | k *= m; 55 | h *= m; 56 | h ^= k; 57 | } 58 | int len_m = len_4 << 2; 59 | int left = len - len_m; 60 | if (left != 0) { 61 | if (left >= 3) { 62 | h ^= data[offset + len - 3] << 16; 63 | } 64 | if (left >= 2) { 65 | h ^= data[offset + len - 2] << 8; 66 | } 67 | if (left >= 1) { 68 | h ^= data[offset + len - 1]; 69 | } 70 | h *= m; 71 | } 72 | h ^= h >>> 13; 73 | h *= m; 74 | h ^= h >>> 15; 75 | return h; 76 | } 77 | 78 | /** 79 | * Generates 32 bit hash from byte array with default seed value. 80 | * 81 | * @param data 82 | * byte array to hash 83 | * @param offset 84 | * the start position in the array to hash 85 | * @param len 86 | * length of the array elements to hash 87 | * @return 32 bit hash of the given array 88 | */ 89 | public static final int hash32(final byte[] data, int offset, int len) { 90 | return MurmurHash2b.hash(data, 0x9747b28c, offset, len); 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/hash/otherhashes/MurmurHash3Yonik.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.hash.otherhashes; 18 | 19 | public class MurmurHash3Yonik { 20 | 21 | /** Returns the MurmurHash3_x86_32 hash. */ 22 | public static int murmurhash3_x86_32(byte[] data, int offset, int len, int seed) { 23 | 24 | final int c1 = 0xcc9e2d51; 25 | final int c2 = 0x1b873593; 26 | 27 | int h1 = seed; 28 | int roundedEnd = offset + (len & 0xfffffffc); // round down to 4 byte block 29 | 30 | for (int i=offset; i>> 17); // ROTL32(k1,15); 35 | k1 *= c2; 36 | 37 | h1 ^= k1; 38 | h1 = (h1 << 13) | (h1 >>> 19); // ROTL32(h1,13); 39 | h1 = h1*5+0xe6546b64; 40 | } 41 | 42 | // tail 43 | int k1 = 0; 44 | 45 | switch(len & 0x03) { 46 | case 3: 47 | k1 = (data[roundedEnd + 2] & 0xff) << 16; 48 | // fallthrough 49 | case 2: 50 | k1 |= (data[roundedEnd + 1] & 0xff) << 8; 51 | // fallthrough 52 | case 1: 53 | k1 |= (data[roundedEnd] & 0xff); 54 | k1 *= c1; 55 | k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15); 56 | k1 *= c2; 57 | h1 ^= k1; 58 | } 59 | 60 | // finalization 61 | h1 ^= len; 62 | 63 | // fmix(h1); 64 | h1 ^= h1 >>> 16; 65 | h1 *= 0x85ebca6b; 66 | h1 ^= h1 >>> 13; 67 | h1 *= 0xc2b2ae35; 68 | h1 ^= h1 >>> 16; 69 | 70 | return h1; 71 | } 72 | 73 | 74 | /** Returns the MurmurHash3_x86_32 hash of the UTF-8 bytes of the String without actually encoding 75 | * the string to a temporary buffer. This is more than 2x faster than hashing the result 76 | * of String.getBytes(). 77 | */ 78 | public static int murmurhash3_x86_32(CharSequence data, int offset, int len, int seed) { 79 | 80 | final int c1 = 0xcc9e2d51; 81 | final int c2 = 0x1b873593; 82 | 83 | int h1 = seed; 84 | 85 | int pos = offset; 86 | int end = offset + len; 87 | int k1 = 0; 88 | int k2 = 0; 89 | int shift = 0; 90 | int bits = 0; 91 | int nBytes = 0; // length in UTF8 bytes 92 | 93 | 94 | while (pos < end) { 95 | int code = data.charAt(pos++); 96 | if (code < 0x80) { 97 | k2 = code; 98 | bits = 8; 99 | 100 | /*** 101 | // optimized ascii implementation (currently slower!!! code size?) 102 | if (shift == 24) { 103 | k1 = k1 | (code << 24); 104 | 105 | k1 *= c1; 106 | k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15); 107 | k1 *= c2; 108 | 109 | h1 ^= k1; 110 | h1 = (h1 << 13) | (h1 >>> 19); // ROTL32(h1,13); 111 | h1 = h1*5+0xe6546b64; 112 | 113 | shift = 0; 114 | nBytes += 4; 115 | k1 = 0; 116 | } else { 117 | k1 |= code << shift; 118 | shift += 8; 119 | } 120 | continue; 121 | ***/ 122 | 123 | } 124 | else if (code < 0x800) { 125 | k2 = (0xC0 | (code >> 6)) 126 | | ((0x80 | (code & 0x3F)) << 8); 127 | bits = 16; 128 | } 129 | else if (code < 0xD800 || code > 0xDFFF || pos>=end) { 130 | // we check for pos>=end to encode an unpaired surrogate as 3 bytes. 131 | k2 = (0xE0 | (code >> 12)) 132 | | ((0x80 | ((code >> 6) & 0x3F)) << 8) 133 | | ((0x80 | (code & 0x3F)) << 16); 134 | bits = 24; 135 | } else { 136 | // surrogate pair 137 | // int utf32 = pos < end ? (int) data.charAt(pos++) : 0; 138 | int utf32 = (int) data.charAt(pos++); 139 | utf32 = ((code - 0xD7C0) << 10) + (utf32 & 0x3FF); 140 | k2 = (0xff & (0xF0 | (utf32 >> 18))) 141 | | ((0x80 | ((utf32 >> 12) & 0x3F))) << 8 142 | | ((0x80 | ((utf32 >> 6) & 0x3F))) << 16 143 | | (0x80 | (utf32 & 0x3F)) << 24; 144 | bits = 32; 145 | } 146 | 147 | 148 | k1 |= k2 << shift; 149 | 150 | // int used_bits = 32 - shift; // how many bits of k2 were used in k1. 151 | // int unused_bits = bits - used_bits; // (bits-(32-shift)) == bits+shift-32 == bits-newshift 152 | 153 | shift += bits; 154 | if (shift >= 32) { 155 | // mix after we have a complete word 156 | 157 | k1 *= c1; 158 | k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15); 159 | k1 *= c2; 160 | 161 | h1 ^= k1; 162 | h1 = (h1 << 13) | (h1 >>> 19); // ROTL32(h1,13); 163 | h1 = h1*5+0xe6546b64; 164 | 165 | shift -= 32; 166 | // unfortunately, java won't let you shift 32 bits off, so we need to check for 0 167 | if (shift != 0) { 168 | k1 = k2 >>> (bits-shift); // bits used == bits - newshift 169 | } else { 170 | k1 = 0; 171 | } 172 | nBytes += 4; 173 | } 174 | 175 | } // inner 176 | 177 | // handle tail 178 | if (shift > 0) { 179 | nBytes += shift >> 3; 180 | k1 *= c1; 181 | k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15); 182 | k1 *= c2; 183 | h1 ^= k1; 184 | } 185 | 186 | // finalization 187 | h1 ^= nBytes; 188 | 189 | // fmix(h1); 190 | h1 ^= h1 >>> 16; 191 | h1 *= 0x85ebca6b; 192 | h1 ^= h1 >>> 13; 193 | h1 *= 0xc2b2ae35; 194 | h1 ^= h1 >>> 16; 195 | 196 | return h1; 197 | } 198 | 199 | } -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/hash/otherhashes/MurmurHash3YonikChecksum.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.hash.otherhashes; 18 | 19 | import java.util.zip.Checksum; 20 | 21 | /** TODO */ 22 | public class MurmurHash3YonikChecksum implements Checksum { 23 | Long hash; 24 | 25 | @Override 26 | public void update(int b) { 27 | throw new RuntimeException("Not implemented"); 28 | } 29 | 30 | @Override 31 | public void update(byte[] b, int off, int len) { 32 | if (hash != null) { 33 | throw new RuntimeException("No hash building available"); 34 | } 35 | hash = 0xffffffffL & MurmurHash3Yonik.murmurhash3_x86_32(b, off, len, 0); 36 | } 37 | 38 | @Override 39 | public long getValue() { 40 | return hash; 41 | } 42 | 43 | @Override 44 | public void reset() { 45 | hash = null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /java-essentials/src/test/java/org/greenrobot/essentials/io/FileUtilsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 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 | package org.greenrobot.essentials.io; 18 | 19 | import org.greenrobot.essentials.hash.Murmur3F; 20 | import org.junit.Assert; 21 | import org.junit.Before; 22 | import org.junit.Test; 23 | 24 | import java.io.File; 25 | import java.io.IOException; 26 | import java.util.ArrayList; 27 | import java.util.Random; 28 | 29 | import static org.junit.Assert.assertFalse; 30 | import static org.junit.Assert.assertTrue; 31 | 32 | /** Implicitly tests some of IoUtils. */ 33 | public class FileUtilsTest { 34 | 35 | private File file; 36 | 37 | @Before 38 | public void setUp() throws IOException { 39 | file = File.createTempFile("file-utils-test", ".txt"); 40 | file.deleteOnExit(); 41 | } 42 | 43 | @Test 44 | public void testWriteAndReadUtf8() throws IOException { 45 | String text = "Hello, let's put in some Umlauts: öäüÖÄÜ €"; 46 | FileUtils.writeUtf8(file, text); 47 | Assert.assertEquals(text, FileUtils.readUtf8(file)); 48 | } 49 | 50 | @Test 51 | public void testAppendUtf8() throws IOException { 52 | String text = "Hello"; 53 | FileUtils.writeUtf8(file, text); 54 | FileUtils.appendUtf8(file, " world"); 55 | Assert.assertEquals("Hello world", FileUtils.readUtf8(file)); 56 | } 57 | 58 | @Test 59 | public void testWriteAndReadObject() throws Exception { 60 | String text = "Hello, let's put in some Umlauts: öäüÖÄÜ €"; 61 | String text2 = "And one more"; 62 | ArrayList strings = new ArrayList<>(); 63 | strings.add(text); 64 | strings.add(text2); 65 | FileUtils.writeObject(file, strings); 66 | ArrayList strings2 = FileUtils.readObject(file); 67 | Assert.assertEquals(strings.size(), strings2.size()); 68 | Assert.assertEquals(text, strings2.get(0)); 69 | Assert.assertEquals(text2, strings2.get(1)); 70 | } 71 | 72 | @Test 73 | public void testDigestMd5AndSha1() throws IOException { 74 | byte[] content = new byte[33333]; 75 | new Random(42).nextBytes(content); 76 | FileUtils.writeBytes(file, content); 77 | 78 | Assert.assertEquals("E4DB2A1C03CA891DDDCE45150570ABEB", FileUtils.getMd5(file)); 79 | Assert.assertEquals("5123C97498170FFA46056190D9439DA203E5234C", FileUtils.getSha1(file)); 80 | Assert.assertEquals("B098145996F6AF622C8B4373A590039A94890E79482B6E97D800C49943915975", 81 | FileUtils.getSha256(file)); 82 | } 83 | 84 | @Test 85 | public void testUpdateChecksumAndCopy() throws IOException { 86 | byte[] content = new byte[33333]; 87 | new Random().nextBytes(content); 88 | 89 | Murmur3F murmur3F = new Murmur3F(); 90 | murmur3F.update(content); 91 | String hash = murmur3F.getValueHexString(); 92 | 93 | FileUtils.writeBytes(file, content); 94 | 95 | File file2 = File.createTempFile("file-utils-test", ".txt"); 96 | file2.deleteOnExit(); 97 | FileUtils.copyFile(file, file2); 98 | 99 | murmur3F.reset(); 100 | FileUtils.updateChecksum(file, murmur3F); 101 | Assert.assertEquals(hash, murmur3F.getValueHexString()); 102 | } 103 | 104 | @Test 105 | public void testDeleteDir() throws IOException { 106 | testDeleteDir(true); 107 | testDeleteDir(false); 108 | } 109 | 110 | private void testDeleteDir(boolean failFast) throws IOException { 111 | file.delete(); 112 | assertTrue(file.mkdir()); 113 | File subDir = new File(file, "mysub"); 114 | assertTrue(subDir.mkdir()); 115 | assertTrue(File.createTempFile("foo", "bar", subDir).exists()); 116 | if (failFast) { 117 | FileUtils.deleteDirRecursive(file); 118 | } else { 119 | assertTrue(FileUtils.deleteDirRecursiveBestEffort(file)); 120 | } 121 | assertFalse(subDir.exists()); 122 | assertFalse(file.exists()); 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /javadoc-style/background.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greenrobot/essentials/a129506eb62ea6d68d248b02e9617d925e510b76/javadoc-style/background.gif -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | de.greenrobot 6 | essentials-modules 7 | 3.0.0-SNAPSHOT 8 | pom 9 | 10 | 11 | build-common 12 | java-essentials 13 | 14 | 15 | 16 | 17 | 18 | org.apache.maven.plugins 19 | maven-deploy-plugin 20 | 2.8.2 21 | 22 | true 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':java-essentials' 2 | include ':java-essentials-performance' 3 | include ':android-test' 4 | include ':android-performance' 5 | -------------------------------------------------------------------------------- /web-resources/hash-functions-benchmark.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greenrobot/essentials/a129506eb62ea6d68d248b02e9617d925e510b76/web-resources/hash-functions-benchmark.pdf -------------------------------------------------------------------------------- /web-resources/hash-functions-bit-distribution-quality.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greenrobot/essentials/a129506eb62ea6d68d248b02e9617d925e510b76/web-resources/hash-functions-bit-distribution-quality.png -------------------------------------------------------------------------------- /web-resources/hash-functions-collisions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greenrobot/essentials/a129506eb62ea6d68d248b02e9617d925e510b76/web-resources/hash-functions-collisions.png -------------------------------------------------------------------------------- /web-resources/hash-functions-performance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greenrobot/essentials/a129506eb62ea6d68d248b02e9617d925e510b76/web-resources/hash-functions-performance.png --------------------------------------------------------------------------------