diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..4635bba --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,169 @@ +# Guidance for AI agents, bots, and humans contributing to Chronicle Software's OpenHFT projects + +LLM-based agents can accelerate development only if they respect our house rules. This file tells you: + +* how to run and verify the build; +* what *not* to comment; +* when to open pull requests. + +## Language & character-set policy + +| Requirement | Rationale | +|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------| +| **British English** spelling (`organisation`, `licence`, *not* `organization`, `license`) except technical US spellings like `synchronized` | Keeps wording consistent with Chronicle's London HQ and existing docs. See the University of Oxford style guide for reference. | +| **ISO-8859-1** (code-points 0-255), except in string literals. Avoid smart quotes, non-breaking spaces and accented characters. | ISO-8859-1 survives every toolchain Chronicle uses, incl. low-latency binary wire formats that expect the 8th bit to be 0. | +| If a symbol is not available in ISO-8859-1, use a textual form such as `micro-second`, `>=`, `:alpha:`, `:yes:`. This is the preferred approach and Unicode must not be inserted. | Extended or '8-bit ASCII' variants are *not* portable and are therefore disallowed. | + +## Javadoc guidelines + +**Goal:** Every Javadoc block should add information you cannot glean from the method signature alone. Anything else is +noise and slows readers down. + +| Do | Don't | +|----|-------| +| State *behavioural contracts*, edge-cases, thread-safety guarantees, units, performance characteristics and checked exceptions. | Restate the obvious ("Gets the value", "Sets the name"). | +| Keep the first sentence short; it becomes the summary line in aggregated docs. | Duplicate parameter names/ types unless more explanation is needed. | +| Prefer `@param` for *constraints* and `@throws` for *conditions*, following Oracle's style guide. | Pad comments to reach a line-length target. | +| Remove or rewrite autogenerated Javadoc for trivial getters/setters. | Leave stale comments that now contradict the code. | + +The principle that Javadoc should only explain what is *not* manifest from the signature is well-established in the +wider Java community. + +## Build & test commands + +Agents must verify that the project still compiles and all unit tests pass before opening a PR: + +```bash +# From repo root +mvn -q verify +``` + +## Commit-message & PR etiquette + +1. **Subject line <= 72 chars**, imperative mood: Fix roll-cycle offset in `ExcerptAppender`. +2. Reference the JIRA/GitHub issue if it exists. +3. In *body*: *root cause -> fix -> measurable impact* (latency, allocation, etc.). Use ASCII bullet points. +4. **Run `mvn verify`** again after rebasing. + +## What to ask the reviewers + +* *Is this AsciiDoc documentation precise enough for a clean-room re-implementation?* +* Does the Javadoc explain the code's *why* and *how* that a junior developer would not be expected to work out? +* Are the documentation, tests and code updated together so the change is clear? +* Does the commit point back to the relevant requirement or decision tag? +* Would an example or small diagram help future maintainers? + +## Project requirements + +See the [Decision Log](src/main/docs/decision-log.adoc) for the latest project decisions. +See the [Project Requirements](src/main/docs/project-requirements.adoc) for details on project requirements. + +## Elevating the Workflow with Real-Time Documentation + +Building upon our existing Iterative Workflow, the newest recommendation is to emphasise *real-time updates* to documentation. +Ensure the relevant `.adoc` files are updated when features, requirements, implementation details, or tests change. +This tight loop informs the AI accurately and creates immediate clarity for all team members. + +### Benefits of Real-Time Documentation + +* **Confidence in documentation**: Accurate docs prevent miscommunications that derail real-world outcomes. +* **Reduced drift**: Real-time updates keep requirements, tests and code aligned. +* **Faster feedback**: AI can quickly highlight inconsistencies when everything is in sync. +* **Better quality**: Frequent checks align the implementation with the specified behaviour. +* **Smoother onboarding**: Up-to-date AsciiDoc clarifies the system for new developers. + +### Best Practices + +* **Maintain Sync**: Keep documentation (AsciiDoc), tests, and code synchronised in version control. Changes in one area should prompt reviews and potential updates in the others. +* **Doc-First for New Work**: For *new* features or requirements, aim to update documentation first, then use AI to help produce or refine corresponding code and tests. For refactoring or initial bootstrapping, updates might flow from code/tests back to documentation, which should then be reviewed and finalised. +* **Small Commits**: Each commit should ideally relate to a single requirement or coherent change, making reviews easier for humans and AI analysis tools. +* **Team Buy-In**: Encourage everyone to review AI outputs critically and contribute to maintaining the synchronicity of all artefacts. + +## AI Agent Guidelines + +When using AI agents to assist with development, please adhere to the following guidelines: + +* **Respect the Language & Character-set Policy**: Ensure all AI-generated content follows the British English and ISO-8859-1 guidelines outlined above. +* **Focus on Clarity**: AI-generated documentation should be clear and concise and add value beyond what is already present in the code or existing documentation. +* **Avoid Redundancy**: Do not generate content that duplicates existing documentation or code comments unless it provides additional context or clarification. +* **Review AI Outputs**: Always review AI-generated content for accuracy, relevance, and adherence to the project's documentation standards before committing it to the repository. + +## Company-Wide Tagging + +This section records **company-wide** decisions that apply to *all* Chronicle projects. All identifiers use the `--xxx` prefix. The `xxx` digits are unique within the same Scope even if the tags are different. Component-specific decisions live in their xxx-decision-log.adoc files. + +### Tag Taxonomy (Nine-Box Framework) + +To improve traceability, we adopt the Nine-Box taxonomy for requirement and decision identifiers. These tags are used in addition to the existing ALL prefix, which remains reserved for global decisions across every project. + +.Adopt a Nine-Box Requirement Taxonomy + +|Tag | Scope | Typical examples | +|----|-------|------------------| +|FN |Functional user-visible behaviour | Message routing, business rules | +|NF-P |Non-functional - Performance | Latency budgets, throughput targets | +|NF-S |Non-functional - Security | Authentication method, TLS version | +|NF-O |Non-functional - Operability | Logging, monitoring, health checks | +|TEST |Test / QA obligations | Chaos scenarios, benchmarking rigs | +|DOC |Documentation obligations | Sequence diagrams, user guides | +|OPS |Operational / DevOps concerns | Helm values, deployment checklist | +|UX |Operator or end-user experience | CLI ergonomics, dashboard layouts | +|RISK |Compliance / risk controls | GDPR retention, audit trail | + +`ALL-*` stays global, case-exact tags. Pick one primary tag if multiple apply. + +### Decision Record Template + +```asciidoc +=== [Identifier] Title of Decision + +Date:: YYYY-MM-DD +Context:: +* What is the issue that this decision addresses? +* What are the driving forces, constraints, and requirements? +Decision Statement:: +* What is the change that is being proposed or was decided? +Alternatives Considered:: +* [Alternative 1 Name/Type]: +** *Description:* Brief description of the alternative. +** *Pros:* ... +** *Cons:* ... +* [Alternative 2 Name/Type]: +** *Description:* Brief description of the alternative. +** *Pros:* ... +** *Cons:* ... +Rationale for Decision:: +* Why was the chosen decision selected? +* How does it address the context and outweigh the cons of alternatives? +Impact & Consequences:: +* What are the positive and negative consequences of this decision? +* How does this decision affect the system, developers, users, or operations? +- What are the trade-offs made? +Notes/Links:: +** (Optional: Links to relevant issues, discussions, documentation, proof-of-concepts) +``` + +## Asciidoc formatting guidelines + +### List Indentation + +Do not rely on indentation for list items in AsciiDoc documents. Use the following pattern instead: + +```asciidoc +section:: Top Level Section +* first level + ** nested level +``` + +### Emphasis and Bold Text + +In AsciiDoc, an underscore `_` is _emphasis_; `*text*` is *bold*. + +## Zero-Allocation Hashing module specifics + +- Follow repository `AGENTS.md` as the base rules; this section adds module notes. Durable docs live in `src/main/docs/` with the landing page at `README.adoc`. +- Module purpose: zero-allocation hashing primitives and utilities for bytes/arrays/buffers with stable, cross-platform output. +- Build commands: full build `mvn -q clean verify`; quality gates `mvn -Pquality verify` (requires JDK 11+). +- Quality gates: keep Checkstyle/SpotBugs clean; preserve deterministic hash outputs across platforms; avoid hidden allocations in hot paths. +- Documentation: maintain Nine-Box IDs in `src/main/docs/specifications.adoc`/`project-requirements` if added, and link decisions/tests accordingly; British English, ASCII/ISO-8859-1, `:source-highlighter: rouge`. +- Guardrails: changes to hashing outputs are breaking; document any algorithm/version bumps; call out platform-specific behaviour in `unsafe-and-platform-notes.adoc`. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..9b27fac --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,218 @@ +# 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 + +```bash +# 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`** (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` 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 +```java +// 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: +```bash +--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 + +- Javadoc: https://javadoc.io/doc/net.openhft/zero-allocation-hashing/latest +- GitHub: https://github.com/OpenHFT/Zero-Allocation-Hashing +- Issues: https://github.com/OpenHFT/Zero-Allocation-Hashing/issues +- Release Notes: https://chronicle.software/release-notes/ +- Company Guidelines: See `AGENTS.md` for Chronicle Software standards \ No newline at end of file diff --git a/README.adoc b/README.adoc index 0a8458d..138b622 100644 --- a/README.adoc +++ b/README.adoc @@ -1,5 +1,8 @@ -== Zero-Allocation Hashing += Zero-Allocation Hashing +:toc: :pp: ++ +:lang: en-GB +:source-highlighter: rouge Chronicle Software diff --git a/pom.xml b/pom.xml index 714398b..72085fe 100644 --- a/pom.xml +++ b/pom.xml @@ -215,7 +215,6 @@ - maven-release-plugin 3.0.0-M4 @@ -334,9 +333,9 @@ - jdk21-profile + jdk17-profile - [21,) + [17,) 8 @@ -366,5 +365,77 @@ + + quality + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.6.0 + + + validate + validate + + check + + + + + ${checkstyle.config.location} + true + true + true + ${checkstyle.violationSeverity} + + + + com.puppycrawl.tools + checkstyle + 10.26.1 + + + net.openhft + chronicle-quality-rules + 1.27.0-SNAPSHOT + + + + + com.github.spotbugs + spotbugs-maven-plugin + + 4.9.8.1 + + Max + Low + true + true + + net/openhft/quality/spotbugs27/chronicle-spotbugs-include.xml + net/openhft/quality/spotbugs27/chronicle-spotbugs-exclude.xml + + + + net.openhft + chronicle-quality-rules + 1.27.0-SNAPSHOT + + + + + spotbugs-main + + process-test-classes + + check + + + + + + + diff --git a/src/main/docs/algorithm-profiles.adoc b/src/main/docs/algorithm-profiles.adoc index f62b785..e5c9078 100644 --- a/src/main/docs/algorithm-profiles.adoc +++ b/src/main/docs/algorithm-profiles.adoc @@ -1,18 +1,19 @@ -== Algorithm Profiles += Algorithm Profiles +:toc: :pp: ++ +:lang: en-GB +:source-highlighter: rouge Chronicle Software -toc::[] - === CityHash 1.1 Factories :: `LongHashFunction.city_1_1()`, `.city_1_1(long)`, `.city_1_1(long, long)` (`LongHashFunction.java:53-115`). Implementation :: -`net.openhft.hashing.CityAndFarmHash_1_1` ports Google’s CityHash64 v1.1 (`CityAndFarmHash_1_1.java`). +`net.openhft.hashing.CityAndFarmHash_1_1` ports Google's CityHash64 v1.1 (`CityAndFarmHash_1_1.java`). Key traits :: -* Normalises inputs to little-endian and forwards short-length cases to specialised mix routines (1–3, 4–7, 8–16 byte fast paths). +* Normalises inputs to little-endian and forwards short-length cases to specialised mix routines (1-3, 4-7, 8-16 byte fast paths). * Produces identical output across host endianness; big-endian incurs the expected byte swapping cost. * Provides seedless, single-seed, and dual-seed variants mirroring the upstream API. @@ -31,17 +32,17 @@ Key traits :: Factories :: `LongHashFunction.farmUo()`, `.farmUo(long)`, `.farmUo(long, long)` (`LongHashFunction.java:181-243`). Implementation :: -Also hosted in `CityAndFarmHash_1_1`, which covers the 1.1 update’s longer pipelines. +Also hosted in `CityAndFarmHash_1_1`, which covers the 1.1 update's longer pipelines. Key traits :: -* Maintains parity with Google’s C{pp} release for test vectors. -* Endianness neutral: always routes through an `Access` view that matches the algorithm’s little-endian assumptions. +* Maintains parity with Google's C{pp} release for test vectors. +* Endianness neutral: always routes through an `Access` view that matches the algorithm's little-endian assumptions. === MurmurHash3 Factories :: `LongHashFunction.murmur_3()`, `.murmur_3(long)` for 64-bit (`LongHashFunction.java:245-268`); `LongTupleHashFunction.murmur_3()`, `.murmur_3(long)` for 128-bit (`LongTupleHashFunction.java:35-69`). Implementation :: -`net.openhft.hashing.MurmurHash_3` adapts Austin Appleby’s x64 variants. +`net.openhft.hashing.MurmurHash_3` adapts Austin Appleby's x64 variants. It extends `DualHashFunction` so the 128-bit engine also exposes the low 64 bits through `LongHashFunction`. Key traits :: * Little-endian canonicalisation via `Access.byteOrder`. @@ -54,7 +55,7 @@ Factories :: Implementation :: `net.openhft.hashing.XxHash` ports the official XXH64 reference and keeps the unsigned prime constants as signed Java longs. Key traits :: -* Uses four-lane accumulation for ≥32 byte inputs, matching upstream behaviour bit-for-bit. +* Uses four-lane accumulation for >=32 byte inputs, matching upstream behaviour bit-for-bit. * Applies the canonical avalanche round in `XxHash.finalize` for all lengths. * Seeded and seedless instances differ only by the stored `seed()` override; serialisation preserves both forms. @@ -67,7 +68,7 @@ Implementation :: `net.openhft.hashing.XXH3` keeps the FARSH-derived 192 byte secret and streaming logic. It defines distinct entry points for 64-bit, 128-bit, and low-64-bit projections. Key traits :: -* Optimises for short messages with dedicated 1–3, 4–8, 9–16, 17–128, and 129–240 byte paths. +* Optimises for short messages with dedicated 1-3, 4-8, 9-16, 17-128, and 129-240 byte paths. * Uses `UnsafeAccess.INSTANCE.byteOrder(null, LITTLE_ENDIAN)` once to avoid per-call adapter allocation. * The 128-bit variant reuses the same mixing core; exposing the low 64 bits avoids extra copies for callers that only need a single `long`. @@ -76,10 +77,10 @@ Key traits :: Factories :: `LongHashFunction.wy_3()`, `.wy_3(long)` (`LongHashFunction.java:343-369`). Implementation :: -`net.openhft.hashing.WyHash` mirrors Wang Yi’s version 3 reference, including the `_wymum` 128-bit multiply-fold helper built on `Maths.unsignedLongMulXorFold`. +`net.openhft.hashing.WyHash` mirrors Wang Yi's version 3 reference, including the `_wymum` 128-bit multiply-fold helper built on `Maths.unsignedLongMulXorFold`. Key traits :: * Supports streaming chunks up to 256 bytes per loop iteration; beyond that it accumulates in 32 byte strides. -* Handles ≤3, ≤8, ≤16, ≤24, ≤32 byte inputs with the same branching as the C code. +* Handles <=3, <=8, <=16, <=24, <=32 byte inputs with the same branching as the C code. * Maintains deterministic output across architectures while acknowledging the performance hit on big-endian systems. === MetroHash (metrohash64_2) @@ -87,8 +88,8 @@ Key traits :: Factories :: `LongHashFunction.metro()`, `.metro(long)` (`LongHashFunction.java:371-389`). Implementation :: -`net.openhft.hashing.MetroHash` implements the 64-bit metrohash variant with the `_2` initialisation vector, matching the original author’s reference. +`net.openhft.hashing.MetroHash` implements the 64-bit metrohash variant with the `_2` initialisation vector, matching the original author's reference. Key traits :: -* Performs four-lane unrolled mixing for ≥32 byte inputs and cascades down to 16, 8, 4, 2, and 1 byte tails. +* Performs four-lane unrolled mixing for >=32 byte inputs and cascades down to 16, 8, 4, 2, and 1 byte tails. * Uses deterministic finalisation (`MetroHash.finalize`) shared by scalar and streaming paths. * Seeded instances override `seed()` and cache the pre-hashed `hashVoid()` constant to avoid re-computation. diff --git a/src/main/docs/architecture-overview.adoc b/src/main/docs/architecture-overview.adoc index 72775ef..44dce67 100644 --- a/src/main/docs/architecture-overview.adoc +++ b/src/main/docs/architecture-overview.adoc @@ -1,10 +1,11 @@ -== Zero-Allocation Hashing Architecture Overview += Zero-Allocation Hashing Architecture Overview +:toc: :pp: ++ +:lang: en-GB +:source-highlighter: rouge Chronicle Software -toc::[] - === Entry Points * `net.openhft.hashing.LongHashFunction` is the primary façade for 64-bit hashes. @@ -15,7 +16,7 @@ It currently delivers 128-bit MurmurHash3 and XXH3 outputs and mirrors the singl === Memory Access Abstractions -* All hashing flows rely on `net.openhft.hashing.Access` to read primitive values from arrays, direct buffers, off-heap memory, or custom structures. `Access.byteOrder(input, desiredOrder)` returns a view that matches the algorithm’s expected endianness (`Access.java:273-308`). +* All hashing flows rely on `net.openhft.hashing.Access` to read primitive values from arrays, direct buffers, off-heap memory, or custom structures. `Access.byteOrder(input, desiredOrder)` returns a view that matches the algorithm's expected endianness (`Access.java:273-308`). * Concrete strategies cover heap arrays (`UnsafeAccess.INSTANCE`), `ByteBuffer` (`ByteBufferAccess`), `CharSequence` in native or explicit byte order (`CharSequenceAccess`), and compact Latin-1 backed strings (`CompactLatin1CharSequenceAccess`). * `UnsafeAccess` wraps `sun.misc.Unsafe` for zero-copy reads, falling back to legacy helpers when `getByte` or `getShort` are absent (e.g., pre-Nougat Android) (`UnsafeAccess.java:40-118`). * Reverse-order wrappers are generated automatically through `Access.newDefaultReverseAccess`, allowing algorithms to treat every source as little-endian while still accepting big-endian buffers (`Access.java:295-344`). diff --git a/src/main/docs/change-log-template.adoc b/src/main/docs/change-log-template.adoc index da67691..52e8ae4 100644 --- a/src/main/docs/change-log-template.adoc +++ b/src/main/docs/change-log-template.adoc @@ -1,4 +1,7 @@ -== Change Log Template += Change Log Template +:toc: +:lang: en-GB +:source-highlighter: rouge Chronicle Software diff --git a/src/main/docs/decision-log.adoc b/src/main/docs/decision-log.adoc new file mode 100644 index 0000000..c47c412 --- /dev/null +++ b/src/main/docs/decision-log.adoc @@ -0,0 +1,84 @@ += Decision Log +:toc: +:lang: en-GB +:source-highlighter: rouge + +Chronicle Software + +== ADR-001: Usage of `sun.misc.Unsafe` for Memory Access + +[cols="1,3"] +|=== +| Status | Accepted +| Date | 2025-11-01 +| Context | To achieve the "Zero-Allocation" goal and maximise throughput, standard Java array access checks and byte-by-byte reading overhead are prohibitive. +| Decision | Use `sun.misc.Unsafe` (`UnsafeAccess.java`) to perform raw memory reads (getLong, getInt) directly from heap arrays and off-heap addresses. +| Consequences | +* **Positive:** Maximal performance; enables "type punning" (reading bytes as longs) efficiently. +* **Negative:** Depends on internal JDK APIs. Requires specific `--add-opens` flags on modularised JDKs (Java 9+). +|=== + +== ADR-002: Little-Endian Canonicalisation + +[cols="1,3"] +|=== +| Status | Accepted +| Date | 2025-11-01 +| Context | Different hardware architectures store multibyte primitives in different orders (Little-Endian vs Big-Endian). Hashing algorithms (often ported from C++) usually assume a specific order (mostly LE). +| Decision | All implementations must normalise input to Little-Endian before processing (`Primitives.nativeToLittleEndian`, `Access.byteOrder`). The `Access` abstraction handles the detection and necessary byte swapping. +| Consequences | +* **Positive:** Guarantees identical hash results for the same byte sequence regardless of the hardware platform (x86 vs s390x). +* **Negative:** Incurs a performance penalty on Big-Endian platforms due to the overhead of byte-swapping during reads. +|=== + +== ADR-003: Abstraction via `Access` Strategy pattern + +[cols="1,3"] +|=== +| Status | Accepted +| Date | 2025-11-01 +| Context | The library needs to hash data residing in various formats: `byte[]`, `ByteBuffer`, `CharSequence`, and raw memory addresses, without duplicating the hashing logic for each source. +| Decision | Implement an `Access` strategy pattern. The hashing algorithms are written against the `Access` interface, which defines how to read 1 to 8 bytes from a given object `T` at a specific offset. +| Consequences | +* **Positive:** Eliminates code duplication; a single algorithm implementation supports all input types. Allows users to implement custom `Access` for their own POJOs. +* **Negative:** Virtual method dispatch could theoretically introduce overhead, though HotSpot inlining generally mitigates this. +|=== + +== ADR-004: Reflective String Value Access (Compact Strings) + +[cols="1,3"] +|=== +| Status | Accepted +| Date | 2025-11-01 +| Context | Java 9 introduced "Compact Strings" (JEP 254), changing the internal representation of Strings from `char[]` (UTF-16) to `byte[]` (Latin-1 or UTF-16). Standard `CharSequence` methods involve copying or decoding overhead. +| Decision | Use reflection and `Unsafe` to inspect the internal `value` field of `java.lang.String`. Detect if the JVM uses compact strings and, if the string is Latin-1, use `CompactLatin1CharSequenceAccess` to treat the backing byte array directly. +| Consequences | +* **Positive:** Zero-copy, zero-allocation hashing for Strings on modern JVMs. +* **Negative:** Extremely brittle; depends on private implementation details of `java.lang.String`. Requires defensive fallbacks (`UnknownJvmStringHash`) for non-HotSpot or future JVMs. +|=== + +== ADR-005: Stateless Immutable Hash Functions + +[cols="1,3"] +|=== +| Status | Accepted +| Date | 2025-11-01 +| Context | Hash functions are often used in concurrent environments (e.g., web servers, high-frequency trading). +| Decision | `LongHashFunction` instances are fully stateless and immutable. Seeds are baked in at construction time. +| Consequences | +* **Positive:** Instances are inherently thread-safe and can be stored in `static final` fields or singletons. No need for synchronization or `ThreadLocal`. +* **Negative:** To change a seed, a new object must be allocated (though this is typically a setup-time cost, not a runtime cost). +|=== + +== ADR-006: Handling 128-bit Hashes via `LongTupleHashFunction` + +[cols="1,3"] +|=== +| Status | Accepted +| Date | 2025-11-01 +| Context | Java primitives are limited to 64 bits (`long`). Algorithms like MurmurHash3 and XXH128 produce 128-bit output. +| Decision | Introduce `LongTupleHashFunction` which accepts a `long[]` buffer to write the results into. Provide `DualHashFunction` to allow viewing the 128-bit function as a 64-bit function (returning the lower 64 bits). +| Consequences | +* **Positive:** Supports >64-bit hashes without allocating an Object wrapper (like `BigInteger` or a custom class). +* **Negative:** The `long[]` result array must be managed/reused by the caller to maintain the "zero-allocation" promise. +|=== diff --git a/src/main/docs/invariants-and-contracts.adoc b/src/main/docs/invariants-and-contracts.adoc index 57697e9..ae5624b 100644 --- a/src/main/docs/invariants-and-contracts.adoc +++ b/src/main/docs/invariants-and-contracts.adoc @@ -1,12 +1,13 @@ -== Invariants and Contracts += Invariants and Contracts +:toc: +:lang: en-GB +:source-highlighter: rouge Chronicle Software -toc::[] - === Hash Interface Guarantees -* Every `LongHashFunction` and `LongTupleHashFunction` implementation treats primitives as if they were written to memory using the platform’s native byte order; the API therefore guarantees that `hashLong(v)` equals `hashLongs(new long[] {v})` and similar array forms (`LongHashFunction.java`, `LongTupleHashFunction.java`). +* Every `LongHashFunction` and `LongTupleHashFunction` implementation treats primitives as if they were written to memory using the platform's native byte order; the API therefore guarantees that `hashLong(v)` equals `hashLongs(new long[] {v})` and similar array forms (`LongHashFunction.java`, `LongTupleHashFunction.java`). * All bundled algorithms normalise multi-byte reads to little-endian before mixing, so the same input bytes produce identical hashes on big- and little-endian machines. Performance may differ, but results must not (`CityAndFarmHash_1_1.java`, `XxHash.java`, `XXH3.java`, `WyHash.java`, `MetroHash.java`, `MurmurHash_3.java`). * `hash(Object, Access, long off, long len)` assumes the addressed region is contiguous and valid for the requested byte count. @@ -27,7 +28,7 @@ Alternative `Access` implementations should document whether they permit null ba === Result Buffer Handling -* `LongTupleHashFunction.hash*(…, long[] result)` requires a pre-sized buffer created via `newResultArray()`. +* `LongTupleHashFunction.hash*(..., long[] result)` requires a pre-sized buffer created via `newResultArray()`. The method throws `NullPointerException` for null buffers and `IllegalArgumentException` for undersized buffers; the helper checks are centralised in `DualHashFunction` (`DualHashFunction.java:12-74`). * The allocation-free path is only honoured when callers reuse buffers. The overloads that return `long[]` will always allocate exactly one new array per call by design (`LongTupleHashFunction.java:70-118`). @@ -41,7 +42,7 @@ Several implementations expose singleton seedless instances via `readResolve`, e === String Handling -* `hashChars` and `hash(CharSequence…)` delegate to `Util.VALID_STRING_HASH`, which inspects the running JVM to choose the correct memory layout strategy. +* `hashChars` and `hash(CharSequence...)` delegate to `Util.VALID_STRING_HASH`, which inspects the running JVM to choose the correct memory layout strategy. Altering char sequence hashing must preserve this runtime detection, or mixed HotSpot/OpenJ9 estates will diverge (`Util.java:29-63`, `ModernCompactStringHash.java`, `ModernHotSpotStringHash.java`, `HotSpotPrior7u6StringHash.java`). * Latin-1 compact strings are read through `CompactLatin1CharSequenceAccess`, which reinterprets the backing `byte[]` without allocating. Any change to string support must maintain zero-allocation access for both UTF-16 and compact encodings (`CompactLatin1CharSequenceAccess.java`). @@ -50,7 +51,7 @@ Any change to string support must maintain zero-allocation access for both UTF-1 * Methods that accept `byte[]` plus `off` and `len` use `Util.checkArrayOffs` for bounds validation. Negative lengths or offsets, or slices that extend past the array end, raise `IndexOutOfBoundsException` immediately (`Util.java:70-77`, `LongHashFunction.java:480-547`). -* ByteBuffer hashing honours the buffer’s position, limit, and order. +* ByteBuffer hashing honours the buffer's position, limit, and order. The implementation temporarily adjusts `Buffer` state to satisfy IBM JDK 7 quirks, then restores the original markers (`LongHashFunction.java:392-470`, `LongHashFunctionTest.java:120-176`). === Thread Safety diff --git a/src/main/docs/performance-benchmarks.adoc b/src/main/docs/performance-benchmarks.adoc index a3121a4..77a3771 100644 --- a/src/main/docs/performance-benchmarks.adoc +++ b/src/main/docs/performance-benchmarks.adoc @@ -1,9 +1,10 @@ -== Performance Benchmarks += Performance Benchmarks +:toc: +:lang: en-GB +:source-highlighter: rouge Chronicle Software -toc::[] - === Current Baseline * The published README table reports throughput (GB/s) and bootstrap latency (ns) per algorithm. diff --git a/src/main/docs/project-requirements.adoc b/src/main/docs/project-requirements.adoc new file mode 100644 index 0000000..d6d0aa7 --- /dev/null +++ b/src/main/docs/project-requirements.adoc @@ -0,0 +1,64 @@ += Project Requirements +:toc: +:lang: en-GB +:source-highlighter: rouge + +Chronicle Software + +== 1. Overview + +The **Zero-Allocation Hashing** project aims to provide a high-performance, Java-based API for hashing various input sources (byte arrays, buffers, memory addresses) without incurring garbage collection overhead during the hashing process. It serves as a drop-in, allocation-free alternative to libraries like Guava Hashing for performance-critical applications. + +Identifiers below follow the Nine-Box taxonomy defined in `AGENTS.md`. + +== 2. Functional Requirements (FN) + +=== 2.1. Core Hashing Capabilities +* **FN-001:** The library must provide implementations for 64-bit hashing via the `LongHashFunction` interface. +* **FN-002:** The library must provide implementations for 128-bit (and potentially higher) hashing via the `LongTupleHashFunction` interface. +* **FN-003:** The library must allow hashing of the following input types without intermediate object allocation: +** Primitive values (`long`, `int`, `short`, `char`, `byte`). +** Primitive arrays. +** `java.nio.ByteBuffer` (both heap and direct). +** `java.lang.CharSequence` (Strings, StringBuilders). +** Raw memory addresses (off-heap memory). +* **FN-004:** The library must support an empty input (Void), returning a deterministic constant for the specific algorithm. + +=== 2.2. Algorithms +The library must implement the following non-cryptographic hash algorithms: +* **FN-005:** CityHash (v1.1). +* **FN-006:** FarmHash (variants `na` and `uo`). +* **FN-007:** MurmurHash3 (128-bit and low 64-bit). Implemented in class `MurmurHash_3`. +* **FN-008:** xxHash (XXH64). Implemented in class `XxHash`. +* **FN-009:** XXH3 (64-bit and 128-bit). +* **FN-010:** WyHash (v3). +* **FN-011:** MetroHash (64-bit, variant 2). + +=== 2.3. Seeding +* **FN-012:** Algorithms must provide factory methods for unseeded (default seed) instances. +* **FN-013:** Algorithms supporting seeds must provide factory methods for single-seed and, where applicable, dual-seed instances. + +=== 2.4. Determinism and Endianness +* **FN-014:** Hash outputs must be deterministic. The same input sequence must produce the same hash code on every invocation. +* **FN-015:** Hash outputs must be cross-platform consistent. The library must normalise byte order internally (typically to Little-Endian) to ensure that Big-Endian architectures (e.g., s390x) produce the exact same hash values as Little-Endian architectures (e.g., x86_64). + +== 3. Non-Functional Requirements + +=== 3.1. Performance (NF-P) +* **NF-P-001:** **Zero Allocation:** Steady-state hashing operations must not allocate objects on the Java Heap. Heap-array entry points validate offsets via `Util.checkArrayOffs`; raw-memory entry points assume the caller has validated addresses. +* **NF-P-002:** **Throughput:** Algorithms should demonstrate multi-GB/s throughput on modern hardware (Baseline: Core i7-4870HQ benchmarks). +* **NF-P-003:** **Bootstrap:** Static initialisation overhead should be minimised, though one-time allocation during class loading is permitted. + +=== 3.2. Reliability and Safety (NF-S, NF-O) +* **NF-S-001:** **Thread Safety:** Hash function instances must be immutable and thread-safe. +* **NF-S-002:** **Memory Safety:** While `Unsafe` is used internally, the API must validate array offsets and lengths where standard Java arrays are used (via `Util.checkArrayOffs`) to prevent SIGSEGV/crashes on heap access. Raw memory hashing assumes the caller validates addresses. + +=== 3.3. Compatibility (NF-O) +* **NF-O-001:** **JDK Version:** The library must support JDK 8 through the current LTS (JDK 21+). +* **NF-O-002:** **Module System:** The library must function in Java 9+ environments. Tests and runtime require `--add-opens java.base/jdk.internal.misc=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED` for `sun.misc.Unsafe` and `sun.nio.ch.DirectBuffer` access (see `unsafe-and-platform-notes.adoc`). +* **NF-O-003:** **VM Support:** The library must support major JVMs including HotSpot, OpenJDK, and OpenJ9/IBM J9. + +== 4. Interface Constraints + +* **NF-O-004:** The API must not depend on `ThreadLocal` variables to avoid memory leaks in containerised environments. +* **NF-O-005:** The `Access` pattern must be extensible to allow users to define custom access strategies for proprietary data structures. diff --git a/src/main/docs/specifications.adoc b/src/main/docs/specifications.adoc index 2b00814..df7537a 100644 --- a/src/main/docs/specifications.adoc +++ b/src/main/docs/specifications.adoc @@ -1,9 +1,10 @@ -== Zero-Allocation Hashing Specification += Zero-Allocation Hashing Specification +:toc: +:lang: en-GB +:source-highlighter: rouge Chronicle Software -toc::[] - === DOC-001 Scope * Provides zero-allocation hashing utilities for byte-oriented inputs in Java. diff --git a/src/main/docs/testing-strategy.adoc b/src/main/docs/testing-strategy.adoc index 8f277bc..ca872cb 100644 --- a/src/main/docs/testing-strategy.adoc +++ b/src/main/docs/testing-strategy.adoc @@ -1,10 +1,11 @@ -== Testing Strategy += Testing Strategy +:toc: :pp: ++ +:lang: en-GB +:source-highlighter: rouge Chronicle Software -toc::[] - === Regression Coverage * `LongHashFunctionTest.test` is the canonical harness for verifying that an algorithm produces identical values across the entire API surface (primitives, arrays, buffers, `Access`-backed inputs, and direct memory). diff --git a/src/main/docs/unsafe-and-platform-notes.adoc b/src/main/docs/unsafe-and-platform-notes.adoc index f14fbfc..492a6eb 100644 --- a/src/main/docs/unsafe-and-platform-notes.adoc +++ b/src/main/docs/unsafe-and-platform-notes.adoc @@ -1,9 +1,10 @@ -== Unsafe and Platform Notes += Unsafe and Platform Notes +:toc: +:lang: en-GB +:source-highlighter: rouge Chronicle Software -toc::[] - === Internal API Usage * `UnsafeAccess` reflects on `sun.misc.Unsafe.theUnsafe` to obtain the singleton and uses it for raw array access, field offsets, and direct memory loads (`UnsafeAccess.java:40-118`). diff --git a/src/main/java/net/openhft/hashing/DualHashFunction.java b/src/main/java/net/openhft/hashing/DualHashFunction.java index 259bddd..e38a89f 100644 --- a/src/main/java/net/openhft/hashing/DualHashFunction.java +++ b/src/main/java/net/openhft/hashing/DualHashFunction.java @@ -7,8 +7,12 @@ import org.jetbrains.annotations.NotNull; import javax.annotation.ParametersAreNonnullByDefault; -// An internal helper class for casting LongTupleHashFunction as LongHashFunction - +/** + * Internal base class that exposes a tuple hash as both tuple and single-value + * {@link LongHashFunction}. Subclasses implement the dualHash* variants; this + * wrapper handles result-array checks and caches a single-value view to avoid + * repeated allocation. + */ @ParametersAreNonnullByDefault abstract class DualHashFunction extends LongTupleHashFunction { private static final long serialVersionUID = 0L; diff --git a/src/main/java/net/openhft/hashing/LongHashFunction.java b/src/main/java/net/openhft/hashing/LongHashFunction.java index 6eb940d..969efbb 100644 --- a/src/main/java/net/openhft/hashing/LongHashFunction.java +++ b/src/main/java/net/openhft/hashing/LongHashFunction.java @@ -67,6 +67,7 @@ public abstract class LongHashFunction implements Serializable { * @see #city_1_1(long) * @see #city_1_1(long, long) */ + @SuppressWarnings("checkstyle:MethodName") public static LongHashFunction city_1_1() { return CityAndFarmHash_1_1.asLongHashFunctionWithoutSeed(); } @@ -83,6 +84,7 @@ public static LongHashFunction city_1_1() { * @see #city_1_1() * @see #city_1_1(long, long) */ + @SuppressWarnings("checkstyle:MethodName") public static LongHashFunction city_1_1(long seed) { return CityAndFarmHash_1_1.asLongHashFunctionWithSeed(seed); } @@ -100,6 +102,7 @@ public static LongHashFunction city_1_1(long seed) { * @see #city_1_1() * @see #city_1_1(long) */ + @SuppressWarnings("checkstyle:MethodName") public static LongHashFunction city_1_1(long seed0, long seed1) { return CityAndFarmHash_1_1.asLongHashFunctionWithTwoSeeds(seed0, seed1); } @@ -225,6 +228,7 @@ public static LongHashFunction farmUo(long seed0, long seed1) { * @return a {@code LongHashFunction} implementing the MurmurHash3 algorithm without seed values * @see #murmur_3(long) */ + @SuppressWarnings("checkstyle:MethodName") public static LongHashFunction murmur_3() { return MurmurHash_3.asLongHashFunctionWithoutSeed(); } @@ -240,6 +244,7 @@ public static LongHashFunction murmur_3() { * @return a {@code LongHashFunction} implementing the MurmurHash3 algorithm with the given seed value * @see #murmur_3() */ + @SuppressWarnings("checkstyle:MethodName") public static LongHashFunction murmur_3(long seed) { return MurmurHash_3.asLongHashFunctionWithSeed(seed); } @@ -335,6 +340,7 @@ public static LongHashFunction xx128low(final long seed) { * @return a {@code LongHashFunction} implementing the wyhash algorithm, version 3, without a seed value * @see #wy_3(long) */ + @SuppressWarnings("checkstyle:MethodName") public static LongHashFunction wy_3() { return WyHash.asLongHashFunctionWithoutSeed(); } @@ -350,6 +356,7 @@ public static LongHashFunction wy_3() { * @return a {@code LongHashFunction} implementing the wyhash algorithm, version 3, with the given seed value * @see #wy_3() */ + @SuppressWarnings("checkstyle:MethodName") public static LongHashFunction wy_3(long seed) { return WyHash.asLongHashFunctionWithSeed(seed); } @@ -669,9 +676,9 @@ public long hashChars(@NotNull String input, int off, int len) { /** * Shortcut for {@link #hashChars(StringBuilder, int, int) hashChars(input, 0, input.length())}. - * - * @param input the StringBuilder to be hashed - * @return the hash code for the given StringBuilder + * + * @param input the StringBuilder to be hashed + * @return the hash code for the given StringBuilder */ public long hashChars(@NotNull StringBuilder input) { return hashNativeChars(input); @@ -697,33 +704,33 @@ public long hashChars(@NotNull StringBuilder input, int off, int len) { return hashNativeChars(input, off, len); } -/** - * Returns the hash code for the entire CharSequence. - * - * @param input the CharSequence to be hashed - * @return the hash code for the given CharSequence - */ + /** + * Returns the hash code for the entire CharSequence. + * + * @param input the CharSequence to be hashed + * @return the hash code for the given CharSequence + */ long hashNativeChars(CharSequence input) { return hashNativeChars(input, 0, input.length()); } -/** - * Returns the hash code for a subsequence of the given CharSequence. - * - * @param input the CharSequence to be hashed - * @param off the index of the first char in the subsequence - * @param len the length of the subsequence - * @return the hash code for the specified subsequence of the given CharSequence - */ + /** + * Returns the hash code for a subsequence of the given CharSequence. + * + * @param input the CharSequence to be hashed + * @param off the index of the first char in the subsequence + * @param len the length of the subsequence + * @return the hash code for the specified subsequence of the given CharSequence + */ long hashNativeChars(CharSequence input, int off, int len) { return hash(input, nativeCharSequenceAccess(), off * 2L, len * 2L); } /** * Shortcut for {@link #hashShorts(short[], int, int) hashShorts(input, 0, input.length)}. - * - * @param input the short array to be hashed - * @return the hash code for the given short array + * + * @param input the short array to be hashed + * @return the hash code for the given short array */ public long hashShorts(@NotNull short[] input) { return unsafeHash(input, SHORT_BASE, input.length * 2L); diff --git a/src/main/java/net/openhft/hashing/Primitives.java b/src/main/java/net/openhft/hashing/Primitives.java index cc91f62..ec9049c 100644 --- a/src/main/java/net/openhft/hashing/Primitives.java +++ b/src/main/java/net/openhft/hashing/Primitives.java @@ -44,9 +44,9 @@ private static class ByteOrderHelper { char adjustByteOrder(final char v) { return v; } } private static class ByteOrderHelperReverse extends ByteOrderHelper { - long adjustByteOrder(final long v) { return Long.reverseBytes(v); } - int adjustByteOrder(final int v) { return Integer.reverseBytes(v); } - short adjustByteOrder(final short v) { return Short.reverseBytes(v); } - char adjustByteOrder(final char v) { return Character.reverseBytes(v); } + @Override long adjustByteOrder(final long v) { return Long.reverseBytes(v); } + @Override int adjustByteOrder(final int v) { return Integer.reverseBytes(v); } + @Override short adjustByteOrder(final short v) { return Short.reverseBytes(v); } + @Override char adjustByteOrder(final char v) { return Character.reverseBytes(v); } } }