vector-reader release notes
1.1.0 — 2026-07-31
Element types are now named by their stored width and interpretation. ElementType and a single as call replace the six named terminals, which are deprecated for removal.
New public API
ElementType names the element type a file was written with, and Decoding#as(ElementType) decodes vectors as that type. Nothing in the vecs family records its own element type, so the caller still names it, but the vocabulary is now the stored width and interpretation rather than the Java primitive it lands in.
ElementType.INT8- signed 8-bit two's complement, decoded into
byte[] ElementType.INT16- signed 16-bit two's complement, decoded into
short[] ElementType.INT32- signed 32-bit two's complement, decoded into
int[] ElementType.FLOAT32- IEEE 754 binary32, decoded into
float[] ElementType.INT64- signed 64-bit two's complement, decoded into
long[] ElementType.FLOAT64- IEEE 754 binary64, decoded into
double[]
try (CloseableIterator<float[]> vectors = VectorReader.from(Source.file(path))
.fixed(Buffers.heap(1 << 16))
.as(ElementType.FLOAT32)) {
while (vectors.hasNext()) {
float[] vector = vectors.next();
// process vector
}
}
Naming the element type as a value rather than as a method keeps one terminal per family as further formats are added, and lets the vocabulary grow to stored widths that have no matching Java primitive.
Deprecated for removal
The named element terminals on VectorReader.Decoding are deprecated since 1.1.0, for removal in 2.0.0. Each has an exact replacement returning the same type:
bytes()becomesas(ElementType.INT8)shorts()becomesas(ElementType.INT16)ints()becomesas(ElementType.INT32)floats()becomesas(ElementType.FLOAT32)longs()becomesas(ElementType.INT64)doubles()becomesas(ElementType.FLOAT64)
VectorFileIterators is unaffected, its own method names are unchanged.
javac enables removal warnings by default, unlike plain deprecation warnings, so a build compiling with -Werror fails on these calls until they are migrated or suppressed with @SuppressWarnings("removal").
1.0.0 — 2026-07-14
The first stable release. The helper surface deprecated through 0.7.0 is removed, the jar is declared as a JPMS module, and the public API is annotated for nullness and use-site contracts.
Removed
Types and members deprecated for removal in 0.7.0 and earlier are gone:
VectorConvertors, the misspelled duplicate ofVectorConvertersOptionsCloseableIterable,Resource.Iterator, andResource.Iterable- the named per-type file-iterator classes (
FloatVectorFileIteratorand friends), replaced by theVectorFileIteratorsfactory methods - the
VectorConvertersiterator adapters ThrowingSupplierandLazyThrowingSingleton
Helpers that were public but never part of the reader API are withdrawn:
Mutable,MutableInt, andMutableReferencemoved to test scopePrefetchingIteratormoved to an encapsulated internal packageThrowingRunnableis now package-private
Behavior
- the jar is a JPMS module,
module uk.co.parnmatt.vector, exporting thereader,convert, andutilpackages and encapsulating its internals. Itrequires transitive org.jspecify, so consumers see the nullness annotations - the public API is
@NullMarked(JSpecify), so every type, parameter, and return has a specified nullness. It is declared on each package, so both module-path and classpath consumers observe it - the iterator types accept null elements, taking
<E extends @Nullable Object>:
Iterator<@Nullable String> values = Iterator.adapt(source);
Buffersis now a functional interface, an opaque allocation strategy. Theheapanddirectfactories are unchanged, but its former concrete surface is removed: theBLOCK_ALIGNMENTconstant, thePlacementenum, the public constructor, and thecapacity(),order(),placement(), andalignment()accessors. A strategy can be supplied directly, such as a lambda:
Buffers buffers = () -> ByteBuffer.allocate(4096).order(ByteOrder.LITTLE_ENDIAN);
- the resource-returning API carries
@MustBeClosedand the pure, builder, and fluent API carries@CheckReturnValue, enforced at compile time for consumers that run Error Prone. The annotations are compile-time only, adding no runtime dependency - the on-disk record format and decode semantics are unchanged
0.7.0 — 2026-07-12
Internal utility helpers that were never part of the reader API are deprecated for removal, and the jar now declares a stable module name.
Deprecated for removal
Internal helpers, still functional:
Mutable,MutableInt, andMutableReference, moving to test scopeThrowingRunnable, becoming package-privatePrefetchingIterator, moving to an internal module
Behavior
- the jar declares
Automatic-Module-Name: uk.co.parnmatt.vector, giving module-path consumers a stable module name - the on-disk record format and decode semantics are unchanged
0.6.0 — 2026-07-12
Lazy filter, peek, limit, skip, takeWhile, and dropWhile operations on the vector iterators, composing with the existing map.
New public API
Iterator<E>,ResourceIterator<E, X>, andCloseableIterator<E>- gain lazy
filter,peek,limit,skip,takeWhile, anddropWhile, alongside the existingmap. Each returns an iterator of the same kind, evaluated as elements are drawn. They close through their source, so a derived iterator still releases the original.
try (CloseableIterator<float[]> vectors = VectorReader.from(Source.file(path))
.fixed(Buffers.heap(1 << 16))
.ints()
.map(VectorConverters::intsToFloats)
.filter(vector -> vector.length == 128)
.limit(1_000)) {
while (vectors.hasNext()) {
float[] vector = vectors.next();
}
}
Deprecated for removal
Still functional:
MutableInt#increment(), which in 1.0.0 will return the value before incrementing. Do not rely on theincrement()voidbinary symbol.
Behavior
- the operations are lazy, applied only as elements are drawn from the iterator
- the on-disk record format and decode semantics are unchanged
0.5.0 — 2026-07-11
A lazy map on the vector iterators, with element-wise converters to feed it, and a Buffers::direct fix for small buffers. Several implementation-detail members are deprecated for removal.
New public API
Iterator<E>andResourceIterator<E, X>- library iterators over
java.util.Iterator, adding a lazymapthat applies a function to each element.CloseableIteratorgains the samemap. These close through their source, so a mapped iterator still releases the original.Iterator#adapt(java.util.Iterator)lifts an existing iterator into the fluent API.
try (CloseableIterator<float[]> vectors = VectorReader.from(Source.file(path))
.fixed(Buffers.heap(1 << 16))
.bytes()
.map(VectorConverters::bytesToFloats)) {
while (vectors.hasNext()) {
float[] vector = vectors.next();
}
}
VectorConverters- element-wise converters (
unsignedBytesToShorts,bytesToFloats,shortsToFloats,intsToFloats,longsToFloats,doublesToFloats) that convert a single vector, for use asmapfunctions. The former iterator-to-iterator methods are deprecated.
Deprecated for removal
Still functional, delegating to the new API:
- the
Bufferscanonical constructor, itscapacity,order,placement, andalignmentaccessors, thePlacementenum, andBLOCK_ALIGNMENT, replaced by building withBuffers::heaporBuffers::directand reading the capacity or order from the allocated buffer - the iterator-to-iterator
VectorConvertersmethods, replaced by the element-wise converters applied throughIterator::map Resource.Iterator, replaced byResourceIteratorResource.Iterable,CloseableIterable,ThrowingSupplier, andLazyThrowingSingleton, no longer part of the public API
Behavior
Buffers::directnow guarantees the requested capacity for buffers smaller than the block alignment, where an aligned slice could previously collapse to fewer bytes than requested (down to zero) depending on the allocation's base address- the deprecated surface produces identical output by delegation, and the on-disk record format and decode semantics are unchanged
0.4.0 — 2026-07-03
Composable VectorReader API. The legacy iterator and converter classes are deprecated for removal.
New public API
VectorReader- fluent entry point.
from(Source)chooses the source, thenfixed(Buffers)orgrowable(ByteOrder)chooses buffering, thenbytes,shorts,ints,longs,floats, ordoubles()returns aCloseableIteratorand throwsIOException.
try (CloseableIterator<float[]> vectors = VectorReader.from(Source.file(path))
.fixed(Buffers.heap(1 << 16))
.floats()) {
while (vectors.hasNext()) {
float[] vector = vectors.next();
}
}
Source- functional interface opening a
ReadableByteChannel, with factoriesfile,directFile,stream,channel, andbytes.file,directFile, andbytesare re-openable, whilestreamandchannelare single-use. Buffers- record describing buffer allocation (capacity, order, placement, alignment), with
heapanddirectfactories and a publicBLOCK_ALIGNMENTconstant. CloseableIterator<E>Closeableresource iterator, the return type of the reader factories.VectorConverters- correctly spelled replacement for
VectorConvertors, with the same conversions as static factories. VectorFileIterators- gained static factories
bytes,shorts,ints,longs,floats, anddoubles(Path)returning aCloseableIteratorwith a default 256 KiB little-endian heap buffer.VectorFileIteratorsitself is not deprecated.
Deprecated for removal
Still functional, delegating to the new API:
Options, replaced bySourceandBuffersVectorConvertorsand its nested iterators, replaced byVectorConverters- the nested
VectorFileIteratorsper-type classes taking(Path, Options)
Behavior
The deprecated surface produces identical output by delegation. The on-disk record format and decode semantics are unchanged.
Internal
ByteSource was renamed to ByteWindow, and VectorSourceIterator to VectorRecordIterator.
0.3.0 — 2026-07-03
Internal buffering refactor, with no change to the public API. The file iterators behave as before.
Internal
The decoding path was decoupled from raw channel and buffer access, separating the physical read, the buffered view, and record framing:
- decoding is independent of the buffering strategy, allowing either a shared read-ahead buffer or a per-record growable buffer
- physical reads sit behind their own abstraction, with clearer blocking-mode handling
Testing expanded with an integration test across all vector types and byte orders, plus unit tests for the new internal abstractions.
0.2.0 — 2026-06-30
Direct-IO alignment fix and stricter validation. No change to the public API.
Behavior
- direct-IO buffers are now block-aligned, fixing a direct-IO read bug
- a negative dimension prefix is rejected with
IOException - read-failure messages were split into distinct cases
Internal
Converters no longer use reflection, and decoding moved behind a package-private abstraction, with no public impact. Test coverage expanded across decoders, iterators, converters, and options, including a Linux-only direct-IO read.
0.1.0 — 2026-06-27
Initial release: lazy iterators over binary vector files, with lazy float conversions. Published as uk.co.parnmatt:vector-reader (Java 21). Each vector is a 4-byte int dimension followed by that many typed components.
New public API
VectorFileIterators- provides six iterators, one per component type:
ByteVectorFileIterator,ShortVectorFileIterator,IntVectorFileIterator,LongVectorFileIterator,FloatVectorFileIterator,DoubleVectorFileIterator. Each iterates the matching primitive array, takes(Path, Options), and isAutoCloseable(closing releases theFileChannel).
Options options = new Options(1 << 20, ByteOrder.LITTLE_ENDIAN, false);
try (FloatVectorFileIterator vectors = new FloatVectorFileIterator(path, options)) {
while (vectors.hasNext()) {
float[] vector = vectors.next();
}
}
Options- record
Options(int capacity, ByteOrder order, boolean directIO)for buffer capacity, byte order, and optional direct IO. VectorConvertors- provides lazy iterator adapters: unsigned-byte reinterpretation, and
byte,short,int,long, anddoubletofloat. utiltypesResource(anAutoCloseablebase withIteratorandIterablevariants),PrefetchingIterator, andLazyThrowingSingleton, used by the file iterators.
Behavior
- lazy iteration with prefetching and lazy buffer allocation
- read failures during iteration surface as
UncheckedIOException - a vector larger than the buffer capacity or the remaining data raises
IOException
Internal
Javadoc is provided on all public types.
