Skip to content

Latest commit

 

History

History
218 lines (170 loc) · 8.17 KB

File metadata and controls

218 lines (170 loc) · 8.17 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Zero-Allocation Hashing is a Java library providing fast, non-cryptographic hash functions that allocate zero objects during hash computation. It implements multiple algorithms (CityHash, FarmHash, MurmurHash3, xxHash, XXH3, wyHash, MetroHash) for hashing byte sequences from various sources (arrays, buffers, CharSequence, raw memory). The corresponding Java class for MurmurHash3 is MurmurHash_3.

Target: Java 8+ (supports JDK 8, 11, 17, 21+)

Build & Test Commands

# Run full build with tests
mvn verify

# Run tests only
mvn test

# Run specific test class
mvn test -Dtest=LongHashFunctionTest

# Run specific test method
mvn test -Dtest=LongHashFunctionTest#testHashBytes

# Clean build
mvn clean install

# Generate javadoc
mvn javadoc:javadoc

# Run quality profile (Checkstyle + SpotBugs); requires JDK 11+
mvn -Pquality verify

Architecture & Key Concepts

Core Abstractions

LongHashFunction (src/main/java/net/openhft/hashing/LongHashFunction.java)

  • Primary facade for 64-bit hashes
  • Factory methods: city_1_1(), farmNa(), farmUo(), murmur_3(), xx(), xx3(), xx128low(), wy_3(), metro()
  • All instances are immutable and thread-safe
  • Seeds are baked into instances at construction time

LongTupleHashFunction (src/main/java/net/openhft/hashing/LongTupleHashFunction.java)

  • Multi-word hash results (128-bit and beyond)
  • Uses reusable long[] buffers to maintain zero-allocation guarantee
  • Caller must manage/reuse result arrays

Access<T> (src/main/java/net/openhft/hashing/Access.java)

  • Strategy pattern abstracting byte sequence reading from different sources
  • Implementations: UnsafeAccess (heap arrays), ByteBufferAccess, CharSequenceAccess, CompactLatin1CharSequenceAccess
  • Handles byte-order normalisation via Access.byteOrder(input, desiredOrder)
  • Algorithms are written once against Access interface, work with all input types

Byte Order Handling

  • All algorithms normalise to Little-Endian internally (ADR-002)
  • Ensures cross-platform deterministic results (x86 vs s390x)
  • Performance penalty on Big-Endian platforms due to byte-swapping

Memory Access

UnsafeAccess (src/main/java/net/openhft/hashing/UnsafeAccess.java)

  • Wraps sun.misc.Unsafe for zero-copy memory reads
  • Enables "type punning" (reading bytes as longs) efficiently
  • Requires --add-opens java.base/jdk.internal.misc=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED on Java 9+

String Hashing Runtime Adaptation (src/main/java/net/openhft/hashing/Util.java)

  • VALID_STRING_HASH selects correct strategy at JVM initialisation
  • Handles: HotSpot (pre-compact, compact strings), OpenJ9, Zing, unknown VMs
  • Uses reflection to access internal String.value field (ADR-004)
  • Fallback: UnknownJvmStringHash for unrecognized JVMs

Algorithm Implementations

All live in package-private classes with factory methods exposed via LongHashFunction:

  • CityAndFarmHash_1_1 - CityHash64 v1.1, FarmHash NA/UO
  • MurmurHash_3 - 64-bit and 128-bit variants
  • XxHash - XXH64
  • XXH3 - XXH3 64-bit and 128-bit
  • WyHash - wyHash v3
  • MetroHash - metrohash64_2

Project-Specific Guidelines

Language & Character Set (CRITICAL)

  • Use British English: "organisation", "licence", "optimisation", "normalise", "initialise" (NOT "organization", "license", "optimization")
  • Technical US spellings allowed: "synchronized", "byte"
  • ISO-8859-1 only (code-points 0-255) except in string literals
  • NO smart quotes, non-breaking spaces, accented characters in code/docs
  • Use textual forms: "micro-second", ">=", ":alpha:" instead of Unicode

Javadoc Standards

DO:

  • Document behavioural contracts, edge-cases, thread-safety, units, performance characteristics
  • Explain constraints via @param, conditions via @throws
  • Keep first sentence short (becomes summary line)

DON'T:

  • Restate the obvious ("Gets the value", "Sets the name")
  • Duplicate parameter names/types without additional explanation
  • Leave autogenerated Javadoc for trivial getters/setters

Code Conventions

  • Zero-allocation in steady state (one-time allocation during class loading permitted)
  • Validate array offsets/lengths via Util.checkArrayOffs to prevent crashes
  • Assume raw memory addresses are pre-validated by caller
  • No ThreadLocal usage (containerisation requirement)
  • Immutable, stateless hash function instances

Documentation Workflow (IMPORTANT)

  • Keep AsciiDoc files synchronized with code/tests
  • Update src/main/docs/*.adoc when changing features, requirements, or implementations
  • See AGENTS.md for company-wide documentation standards
  • Reference decisions in commit messages (e.g., "Implements ADR-003")

Testing Requirements

  • Thoroughly tested on LTS JDKs: 8, 11, 17, 21
  • Test both Little-Endian and Big-Endian platforms
  • JDK 9+ runs tests twice: with and without -XX:-CompactStrings (see pom.xml profiles)
  • All tests use JUnit 4 (supports Java 7+)

Commit & PR Guidelines

  • Subject line <= 72 chars, imperative mood: "Fix roll-cycle offset in ExcerptAppender"
  • Body format: root cause -> fix -> measurable impact
  • Run mvn verify before committing
  • Reference JIRA/GitHub issues
  • Use ASCII bullet points in commit bodies
  • Main branch for PRs: ea (not master)

File Structure

src/main/java/net/openhft/hashing/
  Access.java                  - Core abstraction for byte sequence reading
  LongHashFunction.java        - Primary 64-bit hash facade
  LongTupleHashFunction.java   - Multi-word hash facade
  DualHashFunction.java        - Bridges 128-bit -> 64-bit views
  UnsafeAccess.java           - sun.misc.Unsafe wrapper

  [Algorithm Implementations]
  CityAndFarmHash_1_1.java
  MurmurHash_3.java
  XxHash.java
  XXH3.java
  WyHash.java
  MetroHash.java

  [Memory Access Strategies]
  ByteBufferAccess.java
  CharSequenceAccess.java
  CompactLatin1CharSequenceAccess.java

  [String Hashing Adapters]
  StringHash.java
  ModernHotSpotStringHash.java
  ModernCompactStringHash.java
  HotSpotPrior7u6StringHash.java
  UnknownJvmStringHash.java

  [Utilities]
  Util.java                    - Runtime detection, validation
  Primitives.java              - Byte-order normalization
  Maths.java                   - Low-level arithmetic helpers

src/main/docs/
  project-requirements.adoc    - Functional/non-functional requirements
  decision-log.adoc            - Architecture Decision Records (ADRs)
  architecture-overview.adoc   - System architecture details
  algorithm-profiles.adoc      - Per-algorithm characteristics
  testing-strategy.adoc        - Test coverage approach

Common Development Patterns

Adding a New Hash Algorithm

  1. Create package-private class implementing the algorithm
  2. Extend LongHashFunction or LongTupleHashFunction
  3. Use Access<T> abstraction for all memory reads
  4. Normalize to Little-Endian via Access.byteOrder(input, LITTLE_ENDIAN)
  5. Add factory methods to LongHashFunction facade
  6. Add tests validating against reference implementation
  7. Update algorithm-profiles.adoc with characteristics

Implementing Custom Access Strategy

// Example from Access.java documentation
class Pair {
    long first, second;

    static final long pairDataOffset =
        theUnsafe.objectFieldOffset(Pair.class.getDeclaredField("first"));

    static long hashPair(Pair pair, LongHashFunction hashFunction) {
        return hashFunction.hash(pair, Access.unsafe(), pairDataOffset, 16L);
    }
}

Running on Java 9+

Requires JVM flags for internal API access:

--add-opens java.base/jdk.internal.misc=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED

Key Decisions

See src/main/docs/decision-log.adoc for the canonical ADR list (ADR-001 through ADR-006). Reference ADR IDs in commit messages where relevant.

References