Skip to content

Commit d52e8c0

Browse files
dfa1claude
andcommitted
refactor(inspect): dedup formatBytes into shared ByteSize util
formatBytes was copy-pasted four times (VortexInspector, ExportCommand, ImportCommand, InspectorRender). Extract one public ByteSize.format(long) in the inspector module (cli already depends on it) and repoint every call site; add Locale.ROOT so the decimal separator is stable. Covered by a parameterized ByteSizeTest across the B/KB/MB boundaries (the obsolete InspectorRender.formatBytes test is removed). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 8741dad commit d52e8c0

8 files changed

Lines changed: 74 additions & 66 deletions

File tree

cli/src/main/java/io/github/dfa1/vortex/cli/ExportCommand.java

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.github.dfa1.vortex.cli;
22

33
import io.github.dfa1.vortex.csv.CsvExporter;
4+
import io.github.dfa1.vortex.inspect.ByteSize;
45
import io.github.dfa1.vortex.csv.ExportOptions;
56

67
import java.io.IOException;
@@ -63,17 +64,7 @@ private static void printResult(Path inputPath, Path outputPath) throws IOExcept
6364
long inputBytes = Files.size(inputPath);
6465
long outputBytes = Files.size(outputPath);
6566
System.out.printf("written: %s (%s → %s)%n",
66-
outputPath, formatBytes(inputBytes), formatBytes(outputBytes));
67-
}
68-
69-
private static String formatBytes(long bytes) {
70-
if (bytes < 1024L) {
71-
return bytes + " B";
72-
}
73-
if (bytes < 1024L * 1024) {
74-
return String.format("%.1f KB", bytes / 1024.0);
75-
}
76-
return String.format("%.1f MB", bytes / (1024.0 * 1024));
67+
outputPath, ByteSize.format(inputBytes), ByteSize.format(outputBytes));
7768
}
7869

7970
private static void renderProgress(long done, long total) {

cli/src/main/java/io/github/dfa1/vortex/cli/ImportCommand.java

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.github.dfa1.vortex.cli;
22

33
import io.github.dfa1.vortex.csv.CsvImporter;
4+
import io.github.dfa1.vortex.inspect.ByteSize;
45
import io.github.dfa1.vortex.csv.ImportOptions;
56
import io.github.dfa1.vortex.parquet.ParquetImporter;
67

@@ -109,7 +110,7 @@ private static void printResult(Path inputPath, Path vortexPath, int cascadingDe
109110
? String.format(", cascading depth %d", cascadingDepth)
110111
: "";
111112
System.out.printf("written: %s (%s → %s, %s%s)%n",
112-
vortexPath, formatBytes(inputBytes), formatBytes(vortexBytes),
113+
vortexPath, ByteSize.format(inputBytes), ByteSize.format(vortexBytes),
113114
sizeChange, cascadingInfo);
114115
}
115116

@@ -130,16 +131,6 @@ private static void clearProgress() {
130131
System.err.flush();
131132
}
132133

133-
private static String formatBytes(long bytes) {
134-
if (bytes < 1024L) {
135-
return bytes + " B";
136-
}
137-
if (bytes < 1024L * 1024) {
138-
return String.format("%.1f KB", bytes / 1024.0);
139-
}
140-
return String.format("%.1f MB", bytes / (1024.0 * 1024));
141-
}
142-
143134
private static Path deriveOutputPath(Path inputPath) {
144135
String name = inputPath.getFileName().toString();
145136
if (name.endsWith(".csv")) {

cli/src/main/java/io/github/dfa1/vortex/cli/tui/InspectorRender.java

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -174,20 +174,6 @@ static String formatHexRow(byte[] data, int offset) {
174174
return sb.toString();
175175
}
176176

177-
/// Formats a byte count as B / KB / MB.
178-
///
179-
/// @param bytes raw byte count
180-
/// @return human-readable size string
181-
static String formatBytes(long bytes) {
182-
if (bytes < 1024) {
183-
return bytes + " B";
184-
}
185-
if (bytes < 1024 * 1024) {
186-
return String.format("%.1f KB", bytes / 1024.0);
187-
}
188-
return String.format("%.1f MB", bytes / (1024.0 * 1024.0));
189-
}
190-
191177
/// Pads or truncates `s` to exactly `width` characters.
192178
///
193179
/// @param s source string

cli/src/main/java/io/github/dfa1/vortex/cli/tui/VortexInspectorTui.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import io.github.dfa1.vortex.cli.tui.term.Ansi;
88
import io.github.dfa1.vortex.cli.tui.term.Key;
99
import io.github.dfa1.vortex.cli.tui.term.Terminal;
10+
import io.github.dfa1.vortex.inspect.ByteSize;
1011
import io.github.dfa1.vortex.inspect.InspectorTree;
1112
import io.github.dfa1.vortex.inspect.ZonedStatsSchema;
1213
import io.github.dfa1.vortex.reader.VortexHandle;
@@ -391,10 +392,10 @@ private void drawStatus(StringBuilder buf, int width, int row) {
391392

392393
private void drawHeader(StringBuilder buf, int width) {
393394
String header = " vortex-inspect — v" + tree.version()
394-
+ " " + InspectorRender.formatBytes(tree.fileSize())
395+
+ " " + ByteSize.format(tree.fileSize())
395396
+ " rows=" + tree.totalRowCount()
396397
+ " segs=" + tree.segmentCount()
397-
+ " (" + InspectorRender.formatBytes(tree.totalSegmentBytes()) + ")";
398+
+ " (" + ByteSize.format(tree.totalSegmentBytes()) + ")";
398399
buf.append(Ansi.moveTo(1, 1));
399400
buf.append(Ansi.bg(46)).append(Ansi.fg(30));
400401
buf.append(InspectorRender.pad(header, width));
@@ -479,15 +480,15 @@ private List<String> detailLines(InspectorTree.Node node) {
479480
subtotal += tree.segmentSpecs().get(idx).length();
480481
}
481482
lines.add("Segments: " + layout.segments().size()
482-
+ " (" + InspectorRender.formatBytes(subtotal) + ")");
483+
+ " (" + ByteSize.format(subtotal) + ")");
483484
long rows = layout.rowCount();
484485
for (int idx : layout.segments()) {
485486
SegmentSpec spec = tree.segmentSpecs().get(idx);
486487
String bits = rows > 0
487488
? " bits/elem=" + String.format("%.2f", spec.length() * 8.0 / rows)
488489
: "";
489490
lines.add(" [" + idx + "] off=" + spec.offset()
490-
+ " len=" + InspectorRender.formatBytes(spec.length())
491+
+ " len=" + ByteSize.format(spec.length())
491492
+ " compression=" + spec.compression().name()
492493
+ bits);
493494
}
@@ -559,7 +560,7 @@ private List<String> detailLines(InspectorTree.Node node) {
559560
int segIdx = layout.segments().getFirst();
560561
SegmentSpec spec = tree.segmentSpecs().get(segIdx);
561562
lines.add("Hex (first " + preview.length + " B of segment "
562-
+ segIdx + ", total " + InspectorRender.formatBytes(spec.length()) + "):");
563+
+ segIdx + ", total " + ByteSize.format(spec.length()) + "):");
563564
for (int off = 0; off < preview.length; off += 16) {
564565
lines.add(InspectorRender.formatHexRow(preview, off));
565566
}

cli/src/test/java/io/github/dfa1/vortex/cli/tui/InspectorRenderTest.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -250,18 +250,6 @@ void padsShortTrailingRowAndDotsNonPrintable() {
250250
}
251251
}
252252

253-
@Nested
254-
class FormatBytes {
255-
256-
@Test
257-
void formatsAcrossUnitBoundaries() {
258-
// Given / When / Then — B / KB / MB thresholds
259-
assertThat(InspectorRender.formatBytes(512)).isEqualTo("512 B");
260-
assertThat(InspectorRender.formatBytes(2048)).isEqualTo("2.0 KB");
261-
assertThat(InspectorRender.formatBytes(3 * 1024 * 1024)).isEqualTo("3.0 MB");
262-
}
263-
}
264-
265253
@Nested
266254
class PadAndTruncate {
267255

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package io.github.dfa1.vortex.inspect;
2+
3+
import java.util.Locale;
4+
5+
/// Formats a byte count as a short human-readable string using binary (1024-based) units.
6+
public final class ByteSize {
7+
8+
private static final long KIB = 1024L;
9+
private static final long MIB = KIB * KIB;
10+
11+
private ByteSize() {
12+
}
13+
14+
/// Formats `bytes` as `B` below 1 KiB, `KB` below 1 MiB, otherwise `MB`, with one decimal
15+
/// place for the KB/MB forms. Uses [Locale#ROOT] so the decimal separator is stable regardless
16+
/// of the default locale.
17+
///
18+
/// @param bytes the byte count
19+
/// @return a human-readable size such as `512 B`, `1.5 KB`, or `2.0 MB`
20+
public static String format(long bytes) {
21+
if (bytes < KIB) {
22+
return bytes + " B";
23+
}
24+
if (bytes < MIB) {
25+
return String.format(Locale.ROOT, "%.1f KB", bytes / (double) KIB);
26+
}
27+
return String.format(Locale.ROOT, "%.1f MB", bytes / (double) MIB);
28+
}
29+
}

inspector/src/main/java/io/github/dfa1/vortex/inspect/VortexInspector.java

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public static String render(InspectorTree tree) {
3030
var sb = new StringBuilder();
3131

3232
sb.append("Vortex v").append(tree.version())
33-
.append(" ").append(formatBytes(tree.fileSize()))
33+
.append(" ").append(ByteSize.format(tree.fileSize()))
3434
.append(" ").append(tree.totalRowCount()).append(" rows").append('\n');
3535
sb.append('\n');
3636

@@ -45,7 +45,7 @@ public static String render(InspectorTree tree) {
4545
sb.append('\n');
4646

4747
sb.append("Segments: ").append(tree.segmentCount())
48-
.append(" total ").append(formatBytes(tree.totalSegmentBytes())).append('\n');
48+
.append(" total ").append(ByteSize.format(tree.totalSegmentBytes())).append('\n');
4949
appendSegmentTable(sb, tree.segmentSpecs(), " ");
5050
sb.append('\n');
5151

@@ -60,7 +60,7 @@ private static void appendSegmentTable(StringBuilder sb, List<SegmentSpec> specs
6060
SegmentSpec spec = specs.get(i);
6161
sb.append(indent).append('[').append(i).append("] ")
6262
.append("off=").append(spec.offset())
63-
.append(" len=").append(formatBytes(spec.length()))
63+
.append(" len=").append(ByteSize.format(spec.length()))
6464
.append(" compression=").append(spec.compression().name())
6565
.append('\n');
6666
}
@@ -190,14 +190,4 @@ private static String formatDType(DType dtype) {
190190
case DType.Variant(var nullable) -> "variant" + (nullable ? "?" : "");
191191
};
192192
}
193-
194-
private static String formatBytes(long bytes) {
195-
if (bytes < 1024) {
196-
return bytes + " B";
197-
}
198-
if (bytes < 1024 * 1024) {
199-
return String.format("%.1f KB", bytes / 1024.0);
200-
}
201-
return String.format("%.1f MB", bytes / (1024.0 * 1024.0));
202-
}
203193
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package io.github.dfa1.vortex.inspect;
2+
3+
import org.junit.jupiter.params.ParameterizedTest;
4+
import org.junit.jupiter.params.provider.CsvSource;
5+
6+
import static org.assertj.core.api.Assertions.assertThat;
7+
8+
class ByteSizeTest {
9+
10+
@ParameterizedTest
11+
@CsvSource({
12+
// bytes, expected
13+
"0, 0 B",
14+
"1, 1 B",
15+
"1023, 1023 B", // last value below the KB boundary
16+
"1024, 1.0 KB", // first KB
17+
"1536, 1.5 KB", // one-decimal rounding
18+
"1048575, 1024.0 KB", // last value below the MB boundary
19+
"1048576, 1.0 MB", // first MB
20+
"1572864, 1.5 MB",
21+
"5242880, 5.0 MB",
22+
})
23+
void format_rendersBinaryUnits(long bytes, String expected) {
24+
// Given a byte count
25+
26+
// When
27+
String result = ByteSize.format(bytes);
28+
29+
// Then it picks B / KB / MB with one decimal for the scaled forms
30+
assertThat(result).isEqualTo(expected);
31+
}
32+
}

0 commit comments

Comments
 (0)