Skip to content

Commit b40d34d

Browse files
committed
add ByteUnit
1 parent 311d682 commit b40d34d

File tree

3 files changed

+359
-0
lines changed

3 files changed

+359
-0
lines changed
Lines changed: 339 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,339 @@
1+
package org.lmdbjava;
2+
3+
import java.text.DecimalFormat;
4+
import java.text.NumberFormat;
5+
6+
/**
7+
* A {@code BinaryByteUnit} represents power-of-two byte sizes at a given unit of granularity and
8+
* provides utility methods to convert across units. A {@code BinaryByteUnit} does not maintain
9+
* byte size information, but only helps organize and use byte size representations that may be
10+
* maintained separately across various contexts.
11+
*
12+
* @author Jake Wharton
13+
*/
14+
public enum ByteUnit {
15+
16+
/** Byte unit representing one byte. */
17+
BYTES {
18+
@Override public long convert(long sourceCount, ByteUnit sourceUnit) {
19+
return sourceUnit.toBytes(sourceCount);
20+
}
21+
22+
@Override public long toBytes(long count) {
23+
return count;
24+
}
25+
26+
@Override public long toKibibytes(long count) {
27+
return count / (KB / B);
28+
}
29+
30+
@Override public long toMebibytes(long count) {
31+
return count / (MB / B);
32+
}
33+
34+
@Override public long toGibibytes(long count) {
35+
return count / (GB / B);
36+
}
37+
38+
@Override public long toTebibytes(long count) {
39+
return count / (TB / B);
40+
}
41+
42+
@Override public long toPebibytes(long count) {
43+
return count / (PB / B);
44+
}
45+
},
46+
47+
/** A byte unit representing 1024 bytes. */
48+
KIBIBYTES {
49+
@Override public long convert(long sourceCount, ByteUnit sourceUnit) {
50+
return sourceUnit.toKibibytes(sourceCount);
51+
}
52+
53+
@Override public long toBytes(long count) {
54+
return multiply(count, KB / B, MAX / (KB / B));
55+
}
56+
57+
@Override public long toKibibytes(long count) {
58+
return count;
59+
}
60+
61+
@Override public long toMebibytes(long count) {
62+
return count / (MB / KB);
63+
}
64+
65+
@Override public long toGibibytes(long count) {
66+
return count / (GB / KB);
67+
}
68+
69+
@Override public long toTebibytes(long count) {
70+
return count / (TB / KB);
71+
}
72+
73+
@Override public long toPebibytes(long count) {
74+
return count / (PB / KB);
75+
}
76+
},
77+
78+
/** A byte unit representing 1024 kibibytes. */
79+
MEBIBYTES {
80+
@Override public long convert(long sourceCount, ByteUnit sourceUnit) {
81+
return sourceUnit.toMebibytes(sourceCount);
82+
}
83+
84+
@Override public long toBytes(long count) {
85+
return multiply(count, MB / B, MAX / (MB / B));
86+
}
87+
88+
@Override public long toKibibytes(long count) {
89+
return multiply(count, MB / KB, MAX / (MB / KB));
90+
}
91+
92+
@Override public long toMebibytes(long count) {
93+
return count;
94+
}
95+
96+
@Override public long toGibibytes(long count) {
97+
return count / (GB / MB);
98+
}
99+
100+
@Override public long toTebibytes(long count) {
101+
return count / (TB / MB);
102+
}
103+
104+
@Override public long toPebibytes(long count) {
105+
return count / (PB / MB);
106+
}
107+
},
108+
109+
/** A byte unit representing 1024 mebibytes. */
110+
GIBIBYTES {
111+
@Override public long convert(long sourceCount, ByteUnit sourceUnit) {
112+
return sourceUnit.toGibibytes(sourceCount);
113+
}
114+
115+
@Override public long toBytes(long count) {
116+
return multiply(count, GB / B, MAX / (GB / B));
117+
}
118+
119+
@Override public long toKibibytes(long count) {
120+
return multiply(count, GB / KB, MAX / (GB / KB));
121+
}
122+
123+
@Override public long toMebibytes(long count) {
124+
return multiply(count, GB / MB, MAX / (GB / MB));
125+
}
126+
127+
@Override public long toGibibytes(long count) {
128+
return count;
129+
}
130+
131+
@Override public long toTebibytes(long count) {
132+
return count / (TB / GB);
133+
}
134+
135+
@Override public long toPebibytes(long count) {
136+
return count / (PB / GB);
137+
}
138+
},
139+
140+
/** A byte unit representing 1024 gibibytes. */
141+
TEBIBYTES {
142+
@Override public long convert(long sourceCount, ByteUnit sourceUnit) {
143+
return sourceUnit.toTebibytes(sourceCount);
144+
}
145+
146+
@Override public long toBytes(long count) {
147+
return multiply(count, TB / B, MAX / (TB / B));
148+
}
149+
150+
@Override public long toKibibytes(long count) {
151+
return multiply(count, TB / KB, MAX / (TB / KB));
152+
}
153+
154+
@Override public long toMebibytes(long count) {
155+
return multiply(count, TB / MB, MAX / (TB / MB));
156+
}
157+
158+
@Override public long toGibibytes(long count) {
159+
return multiply(count, TB / GB, MAX / (TB / GB));
160+
}
161+
162+
@Override public long toTebibytes(long count) {
163+
return count;
164+
}
165+
166+
@Override public long toPebibytes(long count) {
167+
return count / (PB / TB);
168+
}
169+
},
170+
171+
/** A byte unit representing 1024 tebibytes. */
172+
PEBIBYTES {
173+
@Override public long convert(long sourceCount, ByteUnit sourceUnit) {
174+
return sourceUnit.toPebibytes(sourceCount);
175+
}
176+
177+
@Override public long toBytes(long count) {
178+
return multiply(count, PB / B, MAX / (PB / B));
179+
}
180+
181+
@Override public long toKibibytes(long count) {
182+
return multiply(count, PB / KB, MAX / (PB / KB));
183+
}
184+
185+
@Override public long toMebibytes(long count) {
186+
return multiply(count, PB / MB, MAX / (PB / MB));
187+
}
188+
189+
@Override public long toGibibytes(long count) {
190+
return multiply(count, PB / GB, MAX / (PB / GB));
191+
}
192+
193+
@Override public long toTebibytes(long count) {
194+
return multiply(count, PB / TB, MAX / (PB / TB));
195+
}
196+
197+
@Override public long toPebibytes(long count) {
198+
return count;
199+
}
200+
};
201+
202+
private static final long B = 1L;
203+
private static final long KB = B * 1024L;
204+
private static final long MB = KB * 1024L;
205+
private static final long GB = MB * 1024L;
206+
private static final long TB = GB * 1024L;
207+
private static final long PB = TB * 1024L;
208+
209+
private static final long MAX = Long.MAX_VALUE;
210+
211+
/**
212+
* Converts the given size in the given unit to bytes. Conversions with arguments that would
213+
* numerically overflow saturate to {@code Long.MIN_VALUE} if negative or {@code Long.MAX_VALUE}
214+
* if positive.
215+
*
216+
* @param count the bit count
217+
* @return the converted count, or {@code Long.MIN_VALUE} if conversion would negatively
218+
* overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
219+
*/
220+
public long toBytes(long count) { throw new AbstractMethodError(); }
221+
222+
/**
223+
* Converts the given size in the given unit to this unit. Conversions from finer to coarser
224+
* granularities truncate, so lose precision. For example, converting from {@code 999} bytes to
225+
* kibibytes results in {@code 0}. Conversions from coarser to finer granularities with arguments
226+
* that would numerically overflow saturate to {@code Long.MIN_VALUE} if negative or
227+
* {@code Long.MAX_VALUE} if positive.
228+
* <p>
229+
* For example, to convert 10 kilobytes to bytes, use:
230+
* {@code ByteUnit.KIBIBYTES.convert(10, ByteUnit.BYTES)}
231+
*
232+
* @param sourceCount the size in the given {@code sourceUnit}.
233+
* @param sourceUnit the unit of the {@code sourceCount} argument.
234+
* @return the converted size in this unit, or {@code Long.MIN_VALUE} if conversion would
235+
* negatively overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
236+
*/
237+
public long convert(long sourceCount, ByteUnit sourceUnit) {
238+
throw new AbstractMethodError();
239+
}
240+
241+
/**
242+
* Equivalent to {@link #convert(long, ByteUnit) KIBIBYTES.convert(count, this)}.
243+
* @param count the bit count
244+
* @return the converted count, or {@code Long.MIN_VALUE} if conversion would negatively
245+
* overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
246+
*/
247+
public long toKibibytes(long count) {
248+
throw new AbstractMethodError();
249+
}
250+
251+
/**
252+
* Equivalent to {@link #convert(long, ByteUnit) MEBIBYTES.convert(count, this)}.
253+
* @param count the bit count
254+
* @return the converted count, or {@code Long.MIN_VALUE} if conversion would negatively
255+
* overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
256+
*/
257+
public long toMebibytes(long count) {
258+
throw new AbstractMethodError();
259+
}
260+
261+
/**
262+
* Equivalent to {@link #convert(long, ByteUnit) GIBIBYTES.convert(count, this)}.
263+
* @param count the bit count
264+
* @return the converted count, or {@code Long.MIN_VALUE} if conversion would negatively
265+
* overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
266+
*/
267+
public long toGibibytes(long count) {
268+
throw new AbstractMethodError();
269+
}
270+
271+
/**
272+
* Equivalent to {@link #convert(long, ByteUnit) TEBIBYTES.convert(count, this)}.
273+
* @param count the bit count
274+
* @return the converted count, or {@code Long.MIN_VALUE} if conversion would negatively
275+
* overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
276+
*/
277+
public long toTebibytes(long count) {
278+
throw new AbstractMethodError();
279+
}
280+
281+
/**
282+
* Equivalent to {@link #convert(long, ByteUnit) PEBIBYTES.convert(count, this)}.
283+
* @param count the bit count
284+
* @return the converted count, or {@code Long.MIN_VALUE} if conversion would negatively
285+
* overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
286+
*/
287+
public long toPebibytes(long count) {
288+
throw new AbstractMethodError();
289+
}
290+
291+
private static final String[] UNITS = { "B", "KiB", "MiB", "GiB", "TiB", "PiB" };
292+
293+
/**
294+
* Return {@code bytes} as human-readable size string (e.g., "1.2 GiB". This will use a default
295+
* {@link DecimalFormat} instance for formatting the number.
296+
*/
297+
public static String format(long bytes) {
298+
return format(bytes, new DecimalFormat(DEFAULT_FORMAT_PATTERN));
299+
}
300+
301+
/**
302+
* Return {@code bytes} as human-readable size string (e.g., "1.2 GiB". This will use a
303+
* {@link DecimalFormat} instance with {@code pattern} for formatting the number.
304+
*/
305+
public static String format(long bytes, String pattern) {
306+
return format(bytes, new DecimalFormat(pattern));
307+
}
308+
309+
/**
310+
* Return {@code bytes} as human-readable size string (e.g., "1.2 GiB". This will use {@code
311+
* format} for formatting the number.
312+
*/
313+
public static String format(long bytes, NumberFormat format) {
314+
if (bytes < 0) {
315+
throw new IllegalArgumentException("bytes < 0: " + bytes);
316+
}
317+
318+
int unitIndex = 0;
319+
double count = bytes;
320+
while (count >= 1024d && unitIndex < UNITS.length - 1) {
321+
count /= 1024d;
322+
unitIndex += 1;
323+
}
324+
return format.format(count) + ' ' + UNITS[unitIndex];
325+
}
326+
327+
static final String DEFAULT_FORMAT_PATTERN = "#,##0.#";
328+
329+
/** Multiply {@code size} by {@code factor} accounting for overflow. */
330+
private static long multiply(long size, long factor, long over) {
331+
if (size > over) {
332+
return Long.MAX_VALUE;
333+
}
334+
if (size < -over) {
335+
return Long.MIN_VALUE;
336+
}
337+
return size * factor;
338+
}
339+
}

src/main/java/org/lmdbjava/Env.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,16 @@ public void setMapSize(final long mapSize) {
148148
checkRc(LIB.mdb_env_set_mapsize(ptr, mapSize));
149149
}
150150

151+
/**
152+
* Sets the map size.
153+
*
154+
* @param size the size in given unit.
155+
* @param unit the unit to use for the size.
156+
*/
157+
public void setMapSize(final int size, ByteUnit unit) {
158+
setMapSize(unit.toBytes(size));
159+
}
160+
151161
/**
152162
* Sets the maximum number of databases (ie {@link Dbi}s permitted.
153163
*

src/test/java/org/lmdbjava/EnvTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,16 @@ public void info() throws IOException {
247247
assertThat(info.numReaders, is(0));
248248
}
249249

250+
@Test
251+
public void byteUnit() throws IOException {
252+
final Env<ByteBuffer> env = create();
253+
final File path = tmp.newFile();
254+
env.setMapSize(1, ByteUnit.MEBIBYTES);
255+
env.open(path, POSIX_MODE, MDB_NOSUBDIR);
256+
EnvInfo info = env.info();
257+
assertThat(info.mapSize, is(ByteUnit.MEBIBYTES.toBytes(1)));
258+
}
259+
250260
@Test
251261
public void stats() throws IOException {
252262
final Env<ByteBuffer> env = create();

0 commit comments

Comments
 (0)