Releases: jdereg/java-util
Release list
4.108.0
java-util 4.108.0
A small, test-only release shipped in lock-step with json-io 4.108.0 (which fixes a slow-path escape bug in its CharStreamTokenizer — the bug was not in java-util).
Testing — FastReader buffer-boundary regression coverage. New differential sweeps exercise the internal 8192-char buffer refill boundary (and 16384) for readUntil, readLine (including a \r\n line ending straddling the boundary), bulk read(char[]), and the borrowed readUntil + fallback pattern. Each test reconstructs the stream and compares against the original, confirming FastReader handles delimiters and line endings correctly across a refill. No behavioral change.
The json-io test-scope dependency remains 4.107.0 — it always trails java-util by one release to avoid the java-util↔json-io dependency cycle.
Full changelog: https://github.com/jdereg/java-util/blob/master/changelog.md
4.107.0
java-util 4.107.0
Performance (concurrency) — removed the dominant multi-threaded deserialization lock.
ClassUtilities.trySetAccessible probed a Collections.synchronizedMap(WeakHashMap) on every object instantiation and field injection. Under concurrent load that single monitor serialized worker threads — 13-thread profiling of concurrent json-io reads showed ~74% of threads BLOCKED on it. It's now a lock-free ConcurrentHashMap; post-change profiling shows zero threads blocked. Single-threaded performance is unchanged. This directly improves json-io's concurrent read scaling.
Performance — ReflectionUtils.getAllConstructors(Class) no longer allocates on a cache hit. It's now keyed directly by Class with a get()-first fast path (no per-probe key allocation, no capturing compute lambda on hits); the redundant SortedConstructorsCacheKey wrapper was removed. Anti-poisoning/security semantics are unchanged.
Documentation — ClassValueMap / ClassValueSet Javadoc, userguide.md, and README.md now document their critical usage constraints. They are specialists for a few, pre-populated, read-mostly class registries — not general-purpose faster Map<Class,V> / Set<Class>. The docs now cover: the non-local cost (every Class has one small, PROBE_LIMIT-bounded ClassValue cache array shared across all ClassValue instances, so many instances thrash each other and the advantage inverts past ~10); that lazy get-then-put population is an anti-pattern (put() invalidates the whole ClassValue → ≈O(N²) warm-up); and that the strong-referencing backing store forfeits ClassValue's leak-safety. No behavioral change.
Full changelog: https://github.com/jdereg/java-util/blob/master/changelog.md
4.106.0
java-util 4.106.0
Maven Central: com.cedarsoftware:java-util:4.106.0
A broad, class-by-class hardening pass across the library — real correctness, security, and performance fixes in the core utilities, each pinned by new tests. Full details in changelog.md.
Highlights
Security
- Turkish-locale defect class fixed across
DateUtilities,IOUtilities, andSystemUtilities— default-locale case conversion (e.g."Windows"→"wındows","FILE"→"fıle") silently disabled Windows system-path checks and protocol allow-listing; all security-relevant conversions now useLocale.ROOT. - ReDoS timeout protection now actually stops runaway matches in
DateUtilitiesandRegexUtilities—java.util.regexnever checks interrupts, so timed-out matches previously kept burning pool threads (turning ReDoS into thread/CPU exhaustion). Matching now runs over an interrupt-checkingCharSequence. IOUtilitiesupload paths now get the same SSRF protocol validation as downloads;ClassUtilitiesblocked-supertype and ClassLoader-leak fixes.
Correctness
MultiKeyMap— lock-freesize()/isEmpty()no longer transiently collapse toward 0 during a resize.CaseInsensitiveMap/CaseInsensitiveSet— serialization now works, and copy-constructors preserve the source's concurrency/ordering.SafeSimpleDateFormat—clone()no longer entangles the copy with the original; serialization fixed.ConcurrentListdeadlock hazard,IntervalSetreader atomicity,DeepEqualslarge-value hash saturation,Converteruser-override cache masking, and more.
Performance
- Lock-free read fast paths for
ClassUtilities.forName(~51× at 8 threads) and theReflectionUtilscaches (~3× at 8 threads). CompactMap/CompactSetback to a single instance field (memory) with fasterequals/hashCode;Converternegative-result caching; widespread collection presizing.
API
UrlUtilitiesis deprecated, scheduled for removal in 5.0 — no ecosystem consumers; preferjava.net.http.HttpClientandIOUtilities.
JDK compatibility: 8+.
4.105.0
java-util 4.105.0
Maven Central: com.cedarsoftware:java-util:4.105.0
Highlights
Bug fix (supersedes 4.104.0)
DateUtilities.parseDate— the 4.104.0 strict-ISO fast path mishandled bracketed[zone]forms (e.g....Z[GMT],...-04:56[America/New_York]): it skipped theGMT → Etc/GMTzone normalization and trusted a minute-truncated written offset over the zone region, shifting the instant by the dropped offset-seconds for ancient LMT dates. Such forms now defer to the flexible path, which reconciles the region against the offset and preserves the instant. Common machine forms (trailingZ, bare offset) keep the fast path. 4.104.0 is superseded by this release.
Also in the 4.104.x line (carried from 4.104.0): strict-ISO fast path in DateUtilities.parseDate for fully-zoned forms, and a lock-free read fast path for the ReflectionUtils caches (8-thread contended micro-benchmark 42 → 141 M ops/s).
JDK compatibility: 8 – 26.
4.104.0
java-util 4.104.0
Maven Central: com.cedarsoftware:java-util:4.104.0
Highlights
Performance
- Strict-ISO fast path in
DateUtilities.parseDate. Fully-zoned ISO-8601 date-times (the wire format json-io / Jackson /java.time.toString()emit) parse via the JDK's strict parsers (Instant.parse/ZonedDateTime.parse) before the regex recognizer; everyConverterString → temporal conversion benefits. Output parity is exact — bare-offset keepsGMT±HH:MMzones,+00:00→GMT, zone-less uses the caller's defaultZoneId, non-ISO/epoch formats unchanged — and bracketed-offset forms like...+05:30[+05:30]now parse instead of throwing. Per-parse: trailing-Z instants 759 → ~470 ns, nanos 924 → ~480 ns, bare-offset 849 → ~600 ns, bracketed-zone 1212 → ~690 ns. - Lock-free read fast path for the
ReflectionUtilscaches. The populate-once constructor / method / field / annotation caches went throughMap.computeIfAbsent, whose defaultLRUCacheLOCKING strategy takes the cache's exclusive lock even on a hit — serializing concurrent reflection (e.g. multi-threaded deserialization). Lookups now try the non-blockingget()first, falling back tocomputeIfAbsentonly on a miss; behavior is identical (negative lookups are sentinels, nevernull). Single-class contended micro-benchmark (12-core, 8 threads): 42 → 141 M ops/s (~3.3×) — the old path never scaled past ~42 M ops/s at any thread count. Single-threaded marginally faster; no downside.
JDK compatibility: 8 – 26.
4.103.0
java-util 4.103.0
Maven Central: com.cedarsoftware:java-util:4.103.0
Highlights
New APIs
com.cedarsoftware.util.internal.VectorizedArrays— exposesequalsRange/mismatchRange/compareRangefor bothchar[]andbyte[]. Dispatches at runtime to JDK 9+ SIMD-vectorizedArrays.*intrinsics, with hand-rolled loop fallbacks for JDK 8. Resolution happens once at class load viaMethodHandles, so per-call cost is a static-field read +invokeExact. Internal package for now; promoted to public API once the contract stabilizes.Converter.convert(String, byte[].class)(and transitivelyString → ByteBuffer) performs multi-format detection — stringified JSON number arrays, spaced hex, unspaced hex (length ≥ 8, catchesCAFEBABE/DEADBEEFmagic numbers, compact UUIDs, SHA hashes), URL-safe Base64, then standard Base64. Tightness rules prevent short tokens like"DATA"/"ABCD"from being mis-classified and let them fall through to the historical charset path.MapConversions.toByteArray(and refactoredtoByteBuffer) handles the wrapped-form contract{"@type":"<type>","value":"<base64>"}. Strict Base64 decode first to preserve round-trip for short blobs; falls through to generalConverterdispatch on failure. Enables clean round-trip of the wrapped form thatJsonGenerator.writeBinaryemits in json-io 4.103.0.
Performance
- Cycle-tracking
IdentityHashMapallocation deferred to first-need acrossArrayConversions.arrayToArray/collectionToArrayandCollectionConversions.arrayToCollection/collectionToCollection— and skipped entirely for primitive-target conversions (primitive arrays can't form cycles). Profiling showed ~560 ms of accumulated allocation+put cost inarrayToArrayalone. ClassUtilities.newInstance()— cached constructor plans now fast-invoke non-varargs constructors when supplied arguments are already in constructor-parameter order and directly assignable, bypassing matcher allocations on repeated positional construction. No-arg cached construction reuses the empty argument-shape key and skips argument matching. Plan-cache entries retain constructor parameter metadata, avoiding repeatedConstructor.getParameters()calls.
Build
- Test-scope dependency bumps —
junit-jupiter5.14.3 → 5.14.4;agrona1.22.0 → 1.23.1 (still JDK 8 compatible). - Registered the JDK 9+ standard
@apiNote/@implSpec/@implNotetags withmaven-javadoc-pluginso the tool no longer emits "unknown tag" warnings.
Full changelog: https://github.com/jdereg/java-util/blob/master/changelog.md
4.102.0
java-util 4.102.0
Maven Central: com.cedarsoftware:java-util:4.102.0
Highlights
Performance
- Vendored Werner Randelshofer's FastDoubleParser (MIT, Java 8) under
com.cedarsoftware.util.fastdoubleparser. Eisel-Lemire algorithm forDouble/Float; recursive-multiplication forBigDecimal/BigInteger. 2-4× faster thanDouble.parseDouble; 5-50× faster thannew BigInteger(String)on 100+ digit values. Keeps java-util dependency-free (mirrors Jackson's vendoring choice). MathUtilities— added 20 fast number-parsing entry points (parseDouble/parseFloat/parseBigDecimal/parseBigInteger×CharSequence/char[]/char[]+offset+length/byte[]/byte[]+offset+length). Thechar[]/byte[]overloads avoid intermediateStringmaterialization.ConverterStringConversionsroutes through them, soConverter.convert(String, Double.class)and analogous calls pick up the speedup transparently.DateUtilities— two regex hot-path fixes: cached security-config getters now use avolatiletri-state instead ofSystem.getProperty(...)per parse (removes the synchronizedHashtablelock); per-Patternthread-localMatcherreuse instead ofpattern.matcher(input)per call. Together: ~2.7s self-time eliminated onJsonPerformanceTest.RegexUtilities.SafeMatchResult— replaced eager group extraction with a lazyMatchResultsnapshot. Unaccessed capturing groups no longer allocate aStringper call. ~840ms ofJsonPerformanceTestwall time recovered.FastReader— added borrowed-slice fast pathsreadUntilBorrowed(...)andreadLineBorrowed(...)for callers that can consume directly from the internal buffer. Existing delimiter semantics preserved; falls back cleanly to copying APIs when pushback is active or the token crosses a buffer boundary.
Correctness
FastReader.readUntilBorrowed()now clamps its scan boundary by available buffer length instead of computingpos + maxLen(which could overflow on very largemaxLenwith a non-zero buffer position).FastReader.BufferSlice— added assertion-checkedrelease()lifecycle for borrowed slices. Callers must release before the next read/pushback/close; outstanding borrows fail fast under-ea. Production behavior unchanged.
Build
- jackson-databind test dependency bumped 2.21.2 → 2.21.3 (test-scope only).
Full changelog: https://github.com/jdereg/java-util/blob/master/changelog.md
4.101.0
java-util 4.101.0
Maven Central: com.cedarsoftware:java-util:4.101.0
Highlights
New APIs
ClassValueMap.getByClass(Class)— typed fast-path lookup that skips theinstanceof Classguard inget(Object), compiling to a near-directClassValue.get(type)call. Converted 30+ hot-path call sites acrossClassUtilities,Converter.ClassPairMap,MultiKeyMap,TypeUtilities,Unsafe,CompactMap, andCollectionHandling.ClassValueSet.containsClass(Class)— parallel typed fast path on the Set side. Skips the null check andinstanceof Classguard incontains(Object). Converted 9 hot-path call sites inMultiKeyMap(flatten/hash/equals paths) andClassUtilities.SECURITY_CHECK_CACHE.ReflectionUtils.getDirectClassAnnotation(Class, Class)— strict JDK-semantics companion togetClassAnnotation. MatchesClass.getAnnotation(Class)exactly (respects@Inherited, never walks interfaces), for identity-tied annotations like@Entity,@JsonNaming,@IoNaming.StringUtilities.getChars(String, int)+getChars(String)— SIMD bulk-copy helpers for hot-pathcharAt-loop replacements.
Performance
Converter— refactored 4 internal caches (CONVERSION_DB,FULL_CONVERSION_CACHE,USER_DB,cacheInheritancePairs) fromMap<ConversionPair, V>to nestedClassValueMap<ClassValueMap<V>>, eliminating per-lookupConversionPairallocation from everyconvert()call.ConversionPairis gone from the JFR hot-path allocation top-5.Converter.USER_DB— dropped redundantinstanceIdfrom cache keys (legacy from an earlier static design).FastReader.readUntil/readLine— 30% reduction in GETFIELD field accesses on the common path (scan-loop local hoisting, elided unconditional PUTFIELD writes, refill moved to loop top).FastReader.readLine(char[], int, int)— ~22% self-time reduction via explicit CSE hoisting ofb[scanPos].StringUtilities— SIMD-intrinsicString.getChars()bulk-copy pattern applied tolevenshteinDistance,damerauLevenshteinDistance, anddecode(String)(hex). Loop-invariant chars hoisted out of the Levenshtein inner loops.
Correctness
ClassValueMap— markedcachedEntrySet,cachedValues,cachedUnmodifiableViewvolatileto close a latent DCL publication race.ReflectionUtils.getAllConstructorsInternal()— added deterministic tie-breaker to constructor sort (fixes ~30% intermittent failure rate inClassUtilitiesVarargsArrayStoreTestunder JaCoCo).
Security
ReflectionUtils.isTrustedCaller()— stack walk no longer misidentifies java-util's own internal cache infrastructure as a trusted caller. Previous prefix-match allowed external callers to bypass dangerous-class blocking on paths routed through cache-populating lambdas.
Build
- JaCoCo code coverage plugin wired in permanently. Reports auto-generated on every
mvn testattarget/site/jacoco/.
Testing
- 19,964 tests total — +380 new coverage tests across
EncryptionUtilities,ReflectionUtils,UrlUtilities,IOUtilities,DeepEquals,MultiKeyMap,ClassUtilities, plus newClassValueMapTestandClassValueSetTestentries for the typed fast paths.
Full changelog: https://github.com/jdereg/java-util/blob/master/changelog.md
4.100.0
What's Changed
-
BUG FIX: Fixed unbounded memory leak in
Converter.FULL_CONVERSION_CACHE. Each short-livedConverterinstance (e.g., one perJsonIo.toJava()call) cached inheritance-resolved conversions keyed by its uniqueinstanceIdin a staticConcurrentHashMap. When the instance was GC'd, its entries remained forever. In production under sustained load, this grew to 1GB+. Fix: cache resolved conversions at the shared level (instanceId=0L) when no user-added conversions exist; skip static caching otherwise. The cache is now bounded by the number of unique(source, target)type pairs, not by the number ofConverterinstances created. -
PERFORMANCE: Optimized
Converter.getCachedConverter()to skip the now-unnecessary instance-specific lookup, eliminating a wastedConversionPairallocation per call. -
Updated test dependencies: JUnit 5.14.2→5.14.3, Jackson 2.21.1→2.21.2
4.99.0
4.99.0 - 2026-03-28
- FEATURE: New
StringUtilities.getChars(String s)— public API that returns a ThreadLocalchar[]buffer populated viaString.getChars()(SIMD-optimized bulk copy). Callers can replacestr.charAt(i)loops with directbuf[i]array access. - PERFORMANCE:
StringUtilities.hashCodeIgnoreCase(String)— usesStringUtilities.getChars()into a ThreadLocalchar[]buffer, then hashes from the array directly. CaseInsensitiveMap GET improved 70-75%, PUT improved 48-52%, and MIXED-CASE GET improved 68% on 100K entries. - PERFORMANCE: New
FastReader.readLine(char[] dest, int off, int maxLen)— dedicated line-reading method optimized for TOON's line-oriented parsing. Combines scanning, copying, and line-ending consumption into a single call. JFR shows TOON line-reading improved 28%. - PERFORMANCE:
FastReader.readUntil()pushback drain loop now uses a local variable forpushbackPosition, avoiding load/store throughthison each iteration. JFR shows 14.8% reduction in FastReader CPU share. - PERFORMANCE:
FastReader.readUntil()replacedMath.min()with inline ternary in tight buffer-scan loop. JFR confirmed 3.5% wall-clock improvement. - PERFORMANCE:
FastReader.readUntil()now cachesthis.limitinto a local variable afterfill(). JFR showsreadUntilself-time dropped ~15%. - PERFORMANCE:
FastReader.fill()extracted core I/O intorefill()which uses a localint nfor thein.read()result, reducing PUTFIELD/GETFIELD overhead.