Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2026-07-02 - Java 21 HexFormat vs String.format Performance
**Learning:** In Java 21 codebases, using `String.format("%02x", b)` inside a loop to convert a byte array to a hex string incurs significant memory allocation and execution time overhead.
**Action:** Use `java.util.HexFormat.of().formatHex(bytes)` instead, which is optimized for byte array to hex string conversions without the overhead of manual looping and string formatting allocations.
Original file line number Diff line number Diff line change
Expand Up @@ -419,15 +419,12 @@ private static String nullableClean(String value) {
return cleaned.isEmpty() ? null : cleaned;
}

private static String sha256Hex(byte[] bytes) {
private static String sha256Hex(final byte[] bytes) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] raw = digest.digest(bytes);
StringBuilder hex = new StringBuilder(raw.length * 2);
for (byte b : raw) {
hex.append(String.format("%02x", b));
}
return hex.toString();
// Bolt optimization: use HexFormat to avoid per-byte String.format allocations
return java.util.HexFormat.of().formatHex(raw);
} catch (GeneralSecurityException ex) {
throw new IllegalStateException("SHA-256 digest unavailable", ex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public RetryDeadLetterResult retryDeadLettered(UUID jobId, String operatorId) {
return RetryDeadLetterResult.ACCEPTED;
}

private String contentHash(MultipartFile file) {
private String contentHash(final MultipartFile file) {
try (InputStream stream = file.getInputStream()) {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] buffer = new byte[8192];
Expand All @@ -160,12 +160,8 @@ private String contentHash(MultipartFile file) {
}

byte[] raw = digest.digest();
StringBuilder hex = new StringBuilder(raw.length * 2);
for (byte b : raw) {
hex.append(String.format("%02x", b));
}

return hex.toString();
// Bolt optimization: use HexFormat to avoid per-byte String.format allocations
return java.util.HexFormat.of().formatHex(raw);
} catch (NoSuchAlgorithmException ex) {
throw new IllegalStateException("SHA-256 digest unavailable", ex);
} catch (IOException ex) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.clearfolio.viewer.artifact;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.HexFormat;

import org.junit.jupiter.api.Test;

public class ArtifactLinkServiceHexFormatTest {
@Test
public void testSha256HexEncoding() throws Exception {
byte[] input = "abc".getBytes(StandardCharsets.UTF_8);
String hex = HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(input));
assertEquals("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", hex);
assertEquals(64, hex.length());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.clearfolio.viewer.service;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.HexFormat;

import org.junit.jupiter.api.Test;

public class DefaultDocumentConversionServiceHexFormatTest {
@Test
public void testContentHashHexEncoding() throws Exception {
byte[] input = "abc".getBytes(StandardCharsets.UTF_8);
String hex = HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(input));
assertEquals("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", hex);
assertEquals(64, hex.length());
}
}