Skip to content

OpenHFT/Chronicle-Values

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Chronicle-Values

About

Generation of constantly-sized flyweight accessors to Chronicle Bytes and simple bean-style on-heap implementations from interfaces. Interfaces that can be processed by Chronicle-Values generation are called value interfaces.

Project status: Stable - the feature matrix (see below) is very vast and not expected to be implemented further. Please write the value interface according to the specification below, with unit tests for your interface. If the generation from the value interface (according to spec) doesn’t work, please report the case via GitHub issues.

All classes in the package net.openhft.chronicle.values.internal and its subpackages are internal implementation details. They may change without notice and must not be referenced directly.

Value interface specification

Value interfaces must belong to a non-empty package (they cannot sit in the default package).

Simple example
package test; interface Balance { long getDollars(); void setDollars(long dollars); long addDollars(long addition); int getCents(); void setCents(int cents); }

The above is compiled into either a 12-byte flyweight or a regular bean with long dollars and int cents.

Supported field types

All Java primitives

byte, boolean, char, short, int, long, float, double

String or CharSequence

Must be annotated with @MaxUtf8Length:

interface Client { CharSequence getName(); void setName(@MaxUtf8Length(20) CharSequence name); CharSequence getStateCode(); void setStateCode(@NotNull @MaxUtf8Length(2) CharSequence stateCode); }

The value supplied to @MaxUtf8Length is the allowed size once the text is encoded as UTF-8. The native form checks this limit when writing to the backing BytesStore. Heap implementations assume the caller supplies a compliant value and any breach is detected only when copied to a native instance.

Another value interface

Allows nested structures:

interface Point { double getX(); void setX(double x); double getY(); void setY(double y); } interface Circle { Point getCenter(); void getUsingCenter(Point using); void setCenter(Point center); double getRadius(); void setRadius(double radius); }

(Self-references are forbidden.)

Any Java enum type
interface Order { enum State { NEW, CANCELLED, FILLED } State getState(); void setState(@NotNull State state); }

The generated classes keep a reference to the array of constants for each enum field. The Enums helper obtains this array via reflection from EnumSet.getUniverse(Class). Caching the array lets the marshalling code translate ordinals without calling values() on every access.

java.util.Date
Array fields

Use the -At suffix and make the first parameter int index:

interface SomeStats { @Array(length = 100) long getPercentFreqAt(int index); void setPercentFreqAt(int index, long percentFreq); long addPercentFreqAt(int index, long addition); }

Supported methods

Simple get/set

[get]FieldName[At], [set]FieldName[At], or isFieldName for boolean fields. You may omit the get/set prefixes:

interface Point { double x(); void x(double x); double y(); void y(double y); }
Volatile get/set

getVolatileFieldName[At], setVolatileFieldName[At]

Ordered set

setOrderedFieldName[At] (similar to AtomicInteger.lazySet)

Simple add

type addFieldName[At]([int index, ]type addition) Works for numeric primitives.

Atomic add

type addAtomicFieldName[At]([int index, ]type addition) Atomic variant for numeric primitives.

Compare-and-swap

boolean compareAndSwapFieldName[At]([int index, ]type expected, type newValue) Works for primitives, enums, and Date.

getUsing

getUsingFieldName[At]([int index, ]Type using) For String/CharSequence, using must be a StringBuilder. For nested value interfaces, using is that interface.

Table of supported methods

Integer (byte-long) float, double boolean Char sequence Value interface enum Date

get / set

Volatile get/set, ordered set

Compare-and-swap

Simple add, atomic add

getUsing

Field configuration via annotations

Field ordering in flyweight layout

Fields are laid out group by group. Use @Group to cluster fields and control their ordering. Fields without @Group form the default group, which always comes first:

interface Complex { @Group(1) double real(); void real(double real); @Group(2) double imaginary(); void imaginary(double imaginary); }

Field nullability

enum and string fields are nullable by default; mark with @NotNull to forbid null:

interface Instrument { CharSequence getSymbol(); void setSymbol(@NotNull @MaxUtf8Length(5) CharSequence symbol); }

When a field is nullable the generated native form stores a marker instead of the text. Heap implementations use an extra boolean for CharSequence fields to track a null value. With @NotNull the setters emit a NullPointerException if given null.

Numeric field ranges

Annotate with @Range(min=, max=) to reduce flyweight size:

interface Transaction { int getSecondFromDayStart(); void setSecondFromDayStart(@Range(min = 0, max = 24 * 60 * 60) int secondFromDayStart); }

Field alignment

Ensure a field does not cross a cache-line boundary, for example:

interface Message { // ... other fields ... @Align(dontCross = 64) long getImportantField(); void setImportantField(long importantValue); }

See the Javadocs of @Align and @Array for details.

Using Chronicle Values

// Flyweight Point offHeapPoint = Values.newNativeReference(Point.class); ((Byteable) offHeapPoint).bytesStore(bytesStore, offset, 16); offHeapPoint.setX(0); offHeapPoint.setY(0); // On-heap Point onHeapPoint = Values.newHeapInstance(Point.class); onHeapPoint.setX(1); onHeapPoint.setY(2);

Generated classes implement:

  • Copyable<Point> - e.g. onHeapPoint.copyFrom(offHeapPoint)

  • BytesMarshallable (Chronicle-Bytes)

  • Correct equals, hashCode, toString

  • Byteable (no-op on the heap implementation)

The Copyable feature allows an on-heap value to take a snapshot of a native instance or vice versa. This provides a simple way to shuttle data between the two representations.

You may extend these in the interface to avoid casting:

interface Point extends Byteable, BytesMarshallable, Copyable<Point> { ... } Point offHeapPoint = Values.newNativeReference(Point.class); // no cast required offHeapPoint.bytesStore(bytesStore, offset, offHeapPoint.maxSize());

Why Use Chronicle-Values and How It Fits In

Chronicle-Values empowers you to work with structured data with high performance and type safety, especially when interfacing with low-latency and off-heap memory systems. It achieves this by generating efficient data accessors from Java interfaces.

Core Benefits

Chronicle-Values is designed to provide:

  • High Performance & Low Latency: Access and manipulate data with minimal overhead, crucial for systems where every microsecond counts. This is particularly true when using its "native" (off-heap) implementations.

  • Minimized GC Impact: Through the use of off-heap 'flyweight' objects, which act as lightweight views onto raw byte data. This significantly reduces garbage collection pressure by avoiding the creation of numerous heap objects.

  • Type Safety: Define your data structures using standard Java interfaces. This provides compile-time checking, clear API contracts, and reduces runtime errors.

  • Developer Convenience: The library automatically generates the necessary accessor boilerplate (getters, setters, etc.) based on your interface definition and intuitive annotations. This saves development time and reduces manual coding errors.

  • "Zero-Deserialization" Access: Read data directly from memory without the traditional cost of hydrating full heap objects, allowing for faster processing.

  • Seamless Integration: Designed to work smoothly with other Chronicle libraries like Chronicle Map (for off-heap key-value stores) and Chronicle Queue (for low-latency messaging), enabling efficient data storage and exchange.

Primary Use Cases

Chronicle-Values is ideal when you need to work with data that has a fixed, well-defined structure at compile time. Common scenarios include:

  • Representing financial messages (e.g., FIX, SBE, or custom binary protocols).

  • Defining event payloads for event-sourcing architectures.

  • Creating complex, fixed-layout keys or values for use with Chronicle Map or Chronicle Queue.

  • Any situation where you need performant, typed access to structured binary data, whether on-heap or off-heap.

How Chronicle-Values Complements Chronicle-Bytes

Chronicle-Values operates on top of Chronicle-Bytes, which provides the foundational layer for byte manipulation. While Values offers a higher-level, typed abstraction for structured data, Chronicle-Bytes remains essential for:

  • Low-level, direct manipulation of byte sequences.

  • Implementing custom serialization or deserialization logic for arbitrary formats.

  • Parsing and constructing binary network protocols from scratch.

  • Handling dynamic, unstructured, or schema-less binary data.

Typical Workflow: A common pattern is to use Chronicle-Bytes for initial I/O operations (e.g., reading from a network socket or a file) or to parse the outer envelope of a raw data stream. Once you’ve identified a segment of that stream that corresponds to a known, fixed structure defined by a value interface, you can then map a Chronicle-Values native reference (flyweight) over that region for typed, convenient, and performant access.

Heap vs. Native (Flyweight) Implementations

For every value interface, Chronicle-Values can generate two types of implementations:

  • Native (Flyweight) References (Values.newNativeReference(..)):

    • These are lightweight views onto a BytesStore (which can be on-heap or off-heap memory).

    • They provide maximum performance and are central to off-heap data strategies.

    • They do not store data themselves; they merely provide typed accessors to the underlying bytes.

    • Careful management of the backing `BytesStore’s lifecycle is crucial (see section on Lifecycle Management).

  • Heap Instances (Values.newHeapInstance(..)):

    • These are standard Java objects (POJOs) that store their data on the Java heap.

    • Useful for interacting with other parts of your system that expect POJOs, or when off-heap performance is not the primary concern for a particular data structure.

    • Data can be easily copied between native and heap instances using the generated copyFrom() method (from the Copyable interface).

About

http://chronicle.software

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 22

Languages