Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ jobs:
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_IN_MEMORY_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.SIGNING_IN_MEMORY_KEY_ID }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_IN_MEMORY_KEY_PASSWORD }}
run: ./gradlew :mapsmith-core:check :mapsmith-core:publishAndReleaseToMavenCentral
run: ./gradlew :mapsmith-core:check :mapsmith-core:javadoc :mapsmith-core:publishAndReleaseToMavenCentral
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.idea/
.gradle
.jqwik-database
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
Expand Down Expand Up @@ -41,4 +42,4 @@ bin/
.vscode/

### Mac OS ###
.DS_Store
.DS_Store
19 changes: 19 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import net.ltgt.gradle.errorprone.errorprone
import org.gradle.external.javadoc.StandardJavadocDocletOptions

plugins {
alias(libs.plugins.spotless)
alias(libs.plugins.spotbugs) apply false
Expand Down Expand Up @@ -51,6 +54,14 @@ subprojects {

extensions.configure<com.diffplug.gradle.spotless.SpotlessExtension> {
java {
// google-java-format only recognizes block tags at the start of a Javadoc line, so
// normalize
// hand-written Javadoc before formatting it.
replaceRegex(
"separate Javadoc block tags",
"(?<!\\*) (?=@(?:author|deprecated|exception|param|return|see|serial|serialData|serialField|since|throws|version)\\b)",
"\n * ",
)
googleJavaFormat()
removeUnusedImports()
trimTrailingWhitespace()
Expand All @@ -70,11 +81,19 @@ subprojects {
add("testImplementation", platform(libs.junit.bom))
add("testImplementation", libs.junit.jupiter)
add("testImplementation", libs.assertj.core)
add("testImplementation", libs.jqwik)
add("testRuntimeOnly", libs.junit.platform.launcher)
}

tasks.withType<JavaCompile>().configureEach {
options.compilerArgs.add("-Xlint:all")
options.compilerArgs.add("-XDaddTypeAnnotationsToSymbol=true")
options.errorprone.disable("ThreadPriorityCheck")
}

tasks.withType<Javadoc>().configureEach {
(options as StandardJavadocDocletOptions).addBooleanOption("Werror", true)
(options as StandardJavadocDocletOptions).addBooleanOption("Xdoclint:all,-missing", true)
}

tasks.withType<Test>().configureEach {
Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
org.gradle.jvmargs=-Xmx1g -Dfile.encoding=UTF-8
org.gradle.parallel=true
org.gradle.configuration-cache=true
3 changes: 3 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ errorprone = "2.50.0"
errorprone-plugin = "5.1.0"
jacoco = "0.8.15"
jmh = "1.37"
# Keep this version fixed pending an explicit security review of newer jqwik releases.
jqwik = { strictly = "1.9.3" }
junit = "6.0.0"
ktfmt = "0.61"
maven-publish = "0.36.0"
Expand All @@ -16,6 +18,7 @@ assertj-core = { module = "org.assertj:assertj-core", version.ref = "assertj" }
errorprone-core = { module = "com.google.errorprone:error_prone_core", version.ref = "errorprone" }
jmh-core = { module = "org.openjdk.jmh:jmh-core", version.ref = "jmh" }
jmh-generator-annprocess = { module = "org.openjdk.jmh:jmh-generator-annprocess", version.ref = "jmh" }
jqwik = { module = "net.jqwik:jqwik", version.ref = "jqwik" }
junit-bom = { module = "org.junit:junit-bom", version.ref = "junit" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter" }
junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher" }
Expand Down
25 changes: 25 additions & 0 deletions mapsmith-core/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,8 +1,33 @@
import java.math.BigDecimal
import org.gradle.testing.jacoco.tasks.JacocoCoverageVerification

plugins {
`java-library`
alias(libs.plugins.maven.publish)
}

tasks.named<JacocoCoverageVerification>("jacocoTestCoverageVerification") {
dependsOn(tasks.named("test"))
executionData(layout.buildDirectory.file("jacoco/test.exec"))

violationRules {
rule {
limit {
counter = "LINE"
value = "MISSEDCOUNT"
maximum = BigDecimal.ZERO
}
limit {
counter = "BRANCH"
value = "MISSEDCOUNT"
maximum = BigDecimal.ZERO
}
}
}
}

tasks.named("check") { dependsOn(tasks.named("jacocoTestCoverageVerification")) }

mavenPublishing {
coordinates(project.group.toString(), "mapsmith-core", project.version.toString())

Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,65 @@
package name.mrkandreev.mapsmith;

/** A map from primitive {@code long} keys to primitive {@code long} values. */
public interface LongLongMap {
/**
* Returns the number of entries.
*
* @return entry count
*/
int size();

/**
* Returns whether this map has no entries.
*
* @return whether empty
*/
default boolean isEmpty() {
return size() == 0;
}

/**
* Returns whether {@code key} is present.
*
* @param key key to look up
* @return whether present
*/
boolean containsKey(long key);

/**
* Returns the value for {@code key}.
*
* @param key key to look up
* @return mapped value
*/
long get(long key);

/**
* Returns the value for {@code key}, or {@code defaultValue}.
*
* @param key key to look up
* @param defaultValue value returned when absent
* @return mapped or default value
*/
long getOrDefault(long key, long defaultValue);

/**
* Associates {@code value} with {@code key}.
*
* @param key key to store
* @param value value to store
* @return previous value
*/
long put(long key, long value);

/**
* Removes {@code key}.
*
* @param key key to remove
* @return previous value
*/
long remove(long key);

/** Removes all entries. */
void clear();
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,65 @@
package name.mrkandreev.mapsmith;

/** A map from primitive {@code long} keys to object values. */
public interface LongObjectMap<T> {
/**
* Returns the number of entries.
*
* @return entry count
*/
int size();

/**
* Returns whether this map has no entries.
*
* @return whether empty
*/
default boolean isEmpty() {
return size() == 0;
}

/**
* Returns whether {@code key} is present.
*
* @param key key to look up
* @return whether present
*/
boolean containsKey(long key);

/**
* Returns the value for {@code key}.
*
* @param key key to look up
* @return mapped value
*/
T get(long key);

/**
* Returns the value for {@code key}, or {@code defaultValue}.
*
* @param key key to look up
* @param defaultValue value returned when absent
* @return mapped or default value
*/
T getOrDefault(long key, T defaultValue);

/**
* Associates {@code value} with {@code key}.
*
* @param key key to store
* @param value value to store
* @return previous value
*/
T put(long key, T value);

/**
* Removes {@code key}.
*
* @param key key to remove
* @return previous value
*/
T remove(long key);

/** Removes all entries. */
void clear();
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package name.mrkandreev.mapsmith.openaddressing;

/** Hash functions for primitive {@code long} values. */
public enum LongHashing {
/** MurmurHash3 finalizer. */
MURMUR3_FINALIZER {
@Override
public long hash(long value) {
Expand All @@ -13,12 +15,14 @@ public long hash(long value) {
return result;
}
},
/** Fibonacci hashing. */
FIBONACCI {
@Override
public long hash(long value) {
return value * 0x9e3779b97f4a7c15L;
}
},
/** Xor-shift hashing. */
XOR_SHIFT {
@Override
public long hash(long value) {
Expand All @@ -29,12 +33,19 @@ public long hash(long value) {
return result;
}
},
/** Identity hashing. */
IDENTITY {
@Override
public long hash(long value) {
return value;
}
};

/**
* Hashes {@code value}.
*
* @param value value to hash
* @return hash value
*/
public abstract long hash(long value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,53 @@
import java.util.Objects;
import name.mrkandreev.mapsmith.LongLongMap;

/** Factory for primitive long-to-long maps. */
public final class LongLongMapFactory {
private LongLongMapFactory() {}

/**
* Creates a map using the default hash function.
*
* @param specialization implementation to use
* @return new map
*/
public static LongLongMap create(MapSpecialization specialization) {
return create(specialization, LongHashing.MURMUR3_FINALIZER);
}

/**
* Creates a map using the default hash function.
*
* @param specialization implementation to use
* @param expectedSize expected entry count
* @return new map
*/
public static LongLongMap create(MapSpecialization specialization, int expectedSize) {
return create(specialization, expectedSize, LongHashing.MURMUR3_FINALIZER);
}

/**
* Creates a map.
*
* @param specialization implementation to use
* @param hashing hash function
* @return new map
*/
public static LongLongMap create(MapSpecialization specialization, LongHashing hashing) {
Objects.requireNonNull(specialization, "specialization must not be null");
Objects.requireNonNull(hashing, "hashing must not be null");

return new LongLongOpenAddressMap(specialization.strategy(), hashing);
}

/**
* Creates a map.
*
* @param specialization implementation to use
* @param expectedSize expected entry count
* @param hashing hash function
* @return new map
*/
public static LongLongMap create(
MapSpecialization specialization, int expectedSize, LongHashing hashing) {
Objects.requireNonNull(specialization, "specialization must not be null");
Expand Down
Loading