Bump softprops/action-gh-release from 1 to 2 (#62)
[storage-units.git] / README.md
blob733f6b6cf1cd62e060bf0bc7d91d1e00b29d848e
1 <!--
2 SPDX-FileCopyrightText: The Storage-Units Authors
3 SPDX-License-Identifier: 0BSD
4  -->
6 # Storage-Units [![Chat](https://img.shields.io/badge/matrix-%23talk.metio:matrix.org-brightgreen.svg?style=social&label=Matrix)](https://matrix.to/#/#talk.metio:matrix.org)
8 Storage units according to ISO IEC 80000-13:2008 implemented in Java.
10 ## Features
12 * Immutable, type- and thread-safe object model for storage units
13 * Convenience factories to create units
14 * Basic arithmetic operators
15 * Comparisons and equality checks between units
16 * Lossless conversion between all units
17 * Human-readable text format, including custom formats
18 * Compatible with any `java.lang.Number`
19 * Custom serializers for Jackson, MongoDB, EclipseLink, and others
21 ### Available Units
23 | Name     | Symbol | Exponential          | Absolute                                       |
24 |----------|--------|----------------------|------------------------------------------------|
25 | Byte     | B      | 2<sup>0</sup> Byte   | 1 Byte                                         |
26 | Kibibyte | KiB    | 2<sup>10</sup> Byte  | 1 024 Byte                                     |
27 | Mebibyte | MiB    | 2<sup>20</sup> Byte  | 1 048 576 Byte                                 |
28 | Gibibyte | GiB    | 2<sup>30</sup> Byte  | 1 073 741 824 Byte                             |
29 | Tebibyte | TiB    | 2<sup>40</sup> Byte  | 1 099 511 627 776 Byte                         |
30 | Pebibyte | PiB    | 2<sup>50</sup> Byte  | 1 125 899 906 842 624 Byte                     |
31 | Exbibyte | EiB    | 2<sup>60</sup> Byte  | 1 152 921 504 606 846 976 Byte                 |
32 | Zebibyte | ZiB    | 2<sup>70</sup> Byte  | 1 180 591 620 717 411 303 424 Byte             |
33 | Yobibyte | YiB    | 2<sup>80</sup> Byte  | 1 208 925 819 614 629 174 706 176 Byte         |
34 | Robibyte | RiB    | 2<sup>90</sup> Byte  | 1 237 940 039 285 380 274 899 124 224 Byte     |
35 | Qubibyte | QiB    | 2<sup>100</sup> Byte | 1 267 650 600 228 229 401 496 703 205 376 Byte |
37 | Name       | Symbol | Exponential          | Absolute                                       |
38 |------------|--------|----------------------|------------------------------------------------|
39 | Byte       | B      | 10<sup>0</sup> Byte  | 1 Byte                                         |
40 | Kilobyte   | KB     | 10<sup>3</sup> Byte  | 1 000 Byte                                     |
41 | Megabyte   | MB     | 10<sup>6</sup> Byte  | 1 000 000 Byte                                 |
42 | Gigabyte   | GB     | 10<sup>9</sup> Byte  | 1 000 000 000 Byte                             |
43 | Terabyte   | TB     | 10<sup>12</sup> Byte | 1 000 000 000 000 Byte                         |
44 | Petabyte   | PB     | 10<sup>15</sup> Byte | 1 000 000 000 000 000 Byte                     |
45 | Exabyte    | EB     | 10<sup>18</sup> Byte | 1 000 000 000 000 000 000 Byte                 |
46 | Zettabyte  | ZB     | 10<sup>21</sup> Byte | 1 000 000 000 000 000 000 000 Byte             |
47 | Yottabyte  | YB     | 10<sup>24</sup> Byte | 1 000 000 000 000 000 000 000 000 Byte         |
48 | Ronnabyte  | RB     | 10<sup>27</sup> Byte | 1 000 000 000 000 000 000 000 000 000 Byte     |
49 | Quettabyte | QB     | 10<sup>30</sup> Byte | 1 000 000 000 000 000 000 000 000 000 000 Byte |
51 ## Usage
53 ### Factories
55 Each unit implements a Byte-based static factory method (`valueOf(BigInteger)` or `valueOf(long)`) that can be used to represent a given number of bytes in a specific unit. Note that `Long.MAX_VALUE == 8 Exabyte`, thus use `BigInteger` if you want to work with anything bigger than a eight Exabyte. When in doubt, always use `BigInteger`.
57 ```java
58 // 'long' based
59 Kilobyte unit = Kilobyte.valueOf(500)                           // 500 Byte or "0.50 kB"
60 Kibibyte unit = Kibibyte.valueOf(512)                           // 512 Byte or "0.50 KiB"
62 Megabyte unit = Megabyte.valueOf(1_000_000)                     // 1 000 000 Byte or "1.00 MB"
63 Mebibyte unit = Mebibyte.valueOf(1_048_576)                     // 1 048 576 Byte or "1.00 MiB"
65 // 'BigInteger' based
66 Kilobyte unit = Kilobyte.valueOf(BigInteger.valueOf(500))       // 500 Byte or "0.50 kB"
67 Kibibyte unit = Kibibyte.valueOf(BigInteger.valueOf(512))       // 512 Byte or "0.50 KiB"
69 Megabyte unit = Megabyte.valueOf(BigInteger.valueOf(1000000))   // 1 000 000 Byte or "1.00 MB"
70 Mebibyte unit = Mebibyte.valueOf(BigInteger.valueOf(1_048_576)) // 1 048 576 Byte or "1.00 MB"
71 ```
73 The `StorageUnits` class offers three factory methods that automatically pick the best-matching unit for a given number of bytes.
75 #### Binary Units
77 ```java
78 // 'long' based
79 StorageUnit<?> unit = StorageUnits.binaryValueOf(256)                         // Kibibyte (0.25 KiB)
80 StorageUnit<?> unit = StorageUnits.binaryValueOf(1048576)                     // Mebibyte (1.00 MiB)
82 // 'BigInteger' based
83 StorageUnit<?> unit = StorageUnits.binaryValueOf(BigInteger.valueOf(256))     // Kibibyte (0.25 MiB)
84 StorageUnit<?> unit = StorageUnits.binaryValueOf(BigInteger.valueOf(1048576)) // Mebibyte (1.00 MiB)
85 ```
87 #### Decimal Units
89 ```java
90 // 'long' based
91 StorageUnit<?> unit = StorageUnits.decimalValueOf(120000)                      // Kilobyte (120.00 kB)
92 StorageUnit<?> unit = StorageUnits.decimalValueOf(1000000)                     // Megabyte (1.00 MB)
94 // 'BigInteger' based
95 StorageUnit<?> unit = StorageUnits.decimalValueOf(BigInteger.valueOf(120000))  // Kilobyte (120.00 kB)
96 StorageUnit<?> unit = StorageUnits.decimalValueOf(BigInteger.valueOf(1000000)) // Megabyte (1.00 MB)
97 ```
99 Additionally high-level factory methods are also available in the `StorageUnits` class.
101 ```java
102 import static wtf.metio.storageunits.model.StorageUnits.*;
104 Kibibyte unit = kibibyte(1)   // 1 024 Byte
105 Mebibyte unit = mebibyte(1)   // 1 048 576 Byte
106 Gibibyte unit = gibibyte(1)   // 1 073 741 824 Byte
107 Tebibyte unit = tebibyte(1)   // 1 099 511 627 776 Byte
108 Pebibyte unit = pebibyte(1)   // 1 125 899 906 842 624 Byte
109 Exbibyte unit = exbibyte(1)   // 1 152 921 504 606 846 976 Byte
110 Zebibyte unit = zebibyte(1)   // 1 180 591 620 717 411 303 424 Byte
111 Yobibyte unit = yobibyte(1)   // 1 208 925 819 614 629 174 706 176 Byte
112 Robibyte unit = robibyte(1)   // 1 237 940 039 285 380 274 899 124 224 Byte
113 Qubibyte unit = qubibyte(1)   // 1 267 650 600 228 229 401 496 703 205 376 Byte
115 Kilobyte unit = kilobyte(1)     // 1 000 Byte
116 Megabyte unit = megabyte(1)     // 1 000 000 Byte
117 Gigabyte unit = gigabyte(1)     // 1 000 000 000 Byte
118 Terabyte unit = terabyte(1)     // 1 000 000 000 000 Byte
119 Petabyte unit = petabyte(1)     // 1 000 000 000 000 000 Byte
120 Exabyte unit = exabyte(1)       // 1 000 000 000 000 000 000 Byte
121 Zettabyte unit = zettabyte(1)   // 1 000 000 000 000 000 000 000 Byte
122 Yottabyte unit = yottabyte(1)   // 1 000 000 000 000 000 000 000 000 Byte
123 Ronnabyte unit = ronnabyte(1)   // 1 000 000 000 000 000 000 000 000 000 Byte
124 Quettabyte unit = quettabyte(1) // 1 000 000 000 000 000 000 000 000 000 000 Byte
127 ### Add, Subtract, Multiply, Divide
129 Each unit implements the basic four math operations. All operations retain their original type, e.g. `[Kilobyte] + [Megabyte] = [Kilobyte]`
131 ```java
132 import static wtf.metio.storageunits.model.StorageUnits.*;
134 kilobyte(4).add(kilobyte(8))        // 4 Kilobyte + 8 Kilobyte = 12 Kilobyte = 12 000 Byte
135 kibibyte(1).add(1024)               // 1 Kibibyte + 1 024 Byte = 2 Kibibyte = 2 048 Byte
136 kibibyte(1).subtract(24)            // 1 024 Byte - 24 Byte = 1 000 Byte
137 megabyte(5).subtract(kilobyte(500)) // 5 Megabyte - 500 Kilobyte = 4.5 Megabyte = 4 500 Kilobyte = 4 500 000 Byte
138 gigabyte(1).multiply(5)             // 1 Gigabyte times 5 = 5 Gigabyte
139 terabyte(1).divide(5)               // 1 Terabyte divided by 5 = 0.2 Terabyte = 200 Gigabyte
142 ### Comparison & Equality
144 Each unit is comparable to each other unit.
146 ```java
147 import static wtf.metio.storageunits.model.StorageUnits.*;
149 kibibyte(1024).compareTo(mebibyte(1)) == 0 // true
150 kibibyte(1000).compareTo(mebibyte(1)) == 0 // false
151 petabyte(3).compareTo(terabyte(3000)) == 0 // true
153 megabyte(1000).equals(gigabyte(1))         // true
154 megabyte(1024).equals(gigabyte(1))         // false
155 terabyte(12).equals(gigabyte(12000))       // true
158 ### Formatting
160 Each unit prints a human-readable string, representing the amount of bytes in the given unit using the symbol specified in ISO IEC 80000-13:2008.
162 ```java
163 import static wtf.metio.storageunits.model.StorageUnits.*;
165 // default pattern '0.00'
166 terabyte(2).toString()                         // "2.00 TB"
167 gigabyte(1).add(megabyte(200)).toString()      // "1.20 GB"
168 petabyte(1).subtract(terabyte(250)).toString() // "0.75 PB"
170 // use custom pattern
171 kilobyte(212345).toString("0.0")                                    // "212345.0 kB"
172 gibibyte(2123458).asTebibyte().toString("#,###.000")                // "2,073.689 TiB"
173 kilobyte(120).asMegabyte().add(gigabyte(1)).toString("#,##0.00000") // "1,000.12000 MB"
175 // use custom pattern with specific Locale
176 kilobyte(212345).toString("0.0", Locale.GERMAN)                     // "212345,0 kB"
177 gibibyte(2123458).asTebibyte().toString("#,###.000", Locale.GERMAN) // "2.073,689 TiB"
179 // use custom format
180 Format customFormat = new DecimalFormat("#.00000");
181 terabyte(4).asTebibyte().toString(customFormat) // "3.63798 TiB"
183 // without creating unit type first
184 long numberOfBytes = 1_000_000_000_000_000L;
185 formatAsPetabyte(numberOfBytes) // "1.00 PB"
186 formatAsTerabyte(numberOfBytes) // "1000.00 TB"
187 formatAsPebibyte(numberOfBytes) // "0.89 PiB"
189 // use custom pattern
190 formatAsTerabyte(numberOfBytes, "#0.#####") // "1000 TB"
191 formatAsPebibyte(numberOfBytes, "#0.#####") // "0.88818 PiB"
193 // use custom pattern with specific Locale
194 formatAsTerabyte(numberOfBytes, "#0.#####", Locale.GERMAN) // "1000 TB"
195 formatAsPebibyte(numberOfBytes, "#0.#####", Locale.GERMAN) // "0,88818 PiB"
197 // use custom format
198 formatAsTerabyte(numberOfBytes, customFormat) // "1000.00000 TB"
199 formatAsPebibyte(numberOfBytes, customFormat) // ".88818 PiB"
202 ### Conversions
204 Each unit can be converted to each other unit without loss of information.
206 ```java
207 import static wtf.metio.storageunits.model.StorageUnits.*;
209 Megabyte unit = kilobyte(1000).asMegabyte() // "1.00 MB"
210 Kilobyte unit = gigabyte(12).asKilobyte()   // "12000000.00 kB"
211 Gigabyte unit = terabyte(1).asGigabyte()    // "1000.00 GB"
213 // convert to best-match
214 kilobyte(1100).asBestMatchingUnit()          // "1.10 MB"
215 kilobyte(1100).asBestMatchingBinaryUnit()    // "1.05 MiB"
216 kilobyte(1100).asBestMatchingDecimalUnit()   // "1.10 MB"
219 Each unit can be expressed as a fraction of another unit (precise up to 24 decimal places)
221 ```java
222 import static wtf.metio.storageunits.model.StorageUnits.*;
224 BigDecimal kilobytes = megabyte(1).inKilobyte()  // 1 000
225 BigInteger bytes = kibibyte(2).inByte()          // 2 048
226 BigDecimal terabytes = gigabyte(15).inTerabyte() // 0.015
229 ### Serialization/Converters/Mappers
231 Multiple custom serializers, converters, and mappers are available for all storage units.
233 #### Dozer
235 Use a [Dozer](https://dozermapper.github.io/) converter like this:
237 ```java
238 import static wtf.metio.storageunits.dozer.*;
240 DozerBeanMapperBuilder.create()
241         .withCustomConverter(new BigIntegerBinaryStorageUnitConverter())
242         .withCustomConverter(new BigIntegerDecimalStorageUnitConverter())
243         .withCustomConverter(new LongBinaryStorageUnitConverter())
244         .withCustomConverter(new LongDecimalStorageUnitConverter())
245         .build();
248 #### EclipseLink
250 Use any of the three converters like this:
252 ```java
253 import static wtf.metio.storageunits.eclipselink.*;
255 @Entity
256 public class HardDisk implements Serializable {
258     @Basic
259     @Converter (
260         name="binaryConverter",
261         converterClass=BinaryStorageUnitConverter.class
262     )
263     @Convert("binaryConverter")
264     public StorageUnit<?> getFreeSize() {
265         return freeSize;
266     }
268     @Basic
269     @Converter (
270         name="decimalConverter",
271         converterClass=DecimalyStorageUnitConverter.class
272     )
273     @Convert("decimalConverter")
274     public StorageUnit<?> getTotalSize() {
275         return totalSize;
276     }
281 #### GSON
283 Use a [GSON](https://github.com/google/gson) serializer/deserializer like this:
285 ```java
286 import static wtf.metio.storageunits.gson.*;
288 new GsonBuilder()
289         .registerTypeHierarchyAdapter(StorageUnit.class, new StorageUnitSerializer())
290         .registerTypeHierarchyAdapter(StorageUnit.class, new BinaryStorageUnitDeserializer())
291         .registerTypeHierarchyAdapter(StorageUnit.class, new DecimalStorageUnitDeserializer())
292         .create();
295 #### Jackson
297 Use the provided `StorageUnitModule` like this:
299 ```java
300 import static wtf.metio.storageunits.jackson.*;
302 ObjectMapper objectMapper = new ObjectMapper();
303 objectMapper.registerModule(new StorageUnitModule()); // defaults to binary units
304 objectMapper.registerModule(new StorageUnitModule(StorageUnitModule.PreferredUnitType.BINARY));
305 objectMapper.registerModule(new StorageUnitModule(StorageUnitModule.PreferredUnitType.DECIMAL));
308 #### Jakarta
310 Use the provided `AttributeConverter`s like this:
312 ```java
313 import static wtf.metio.storageunits.jakarta.*;
315 @Entity
316 public class HardDisk implements Serializable {
318     @Convert(converter = BinaryStorageUnitConverter.class)
319     public StorageUnit<?> getFreeSize() {
320         return freeSize;
321     }
323     @Convert(converter = DecimalStorageUnitConverter.class)
324     public StorageUnit<?> getTotalSize() {
325         return totalSize;
326     }
331 #### MapStruct
333 Use any of the available mappers like this:
335 ```java
336 import static wtf.metio.storageunits.mapstruct.*;
338 @Mapper( uses = BigIntegerToBinaryUnitMapper.class )
339 public interface MovieMapper {
341      DestinationType toDestination(SourceType sourceValue);
346 #### ModelMapper
348 Use any of the available converters like this:
350 ```java
351 import static wtf.metio.storageunits.modelmapper.*;
353 modelMapper.addConverter(new BigIntegerToBinaryUnitConverter());
354 modelMapper.addConverter(new BigIntegerToDecimalUnitConverter());
355 modelMapper.addConverter(new LongToBinaryUnitConverter());
356 modelMapper.addConverter(new LongToDecimalUnitConverter());
357 modelMapper.addConverter(new StorageUnitToBigIntegerConverter());
360 #### MongoDB
362 Use any of the three codecs like this:
364 ```java
365 import static wtf.metio.storageunits.mongodb.*;
367 CodecRegistry binaryRegistry = CodecRegistries.fromCodecs(new BinaryStorageUnitCodec(), ...);
368 CodecRegistry decimalRegistry = CodecRegistries.fromCodecs(new DecimalStorageUnitCodec(), ...);
371 #### Orika
373 Use any of the provided converters like this:
375 ```java
376 import static wtf.metio.storageunits.orika.*;
378 ConverterFactory converterFactory = mapperFactory.getConverterFactory();
379 converterFactory.registerConverter(new BinaryStorageUnitConverter());
380 converterFactory.registerConverter(new DecimalStorageUnitConverter());
383 ### Integration
385 To use this project just declare the following dependency inside your POM:
387 ```xml
388 <dependencies>
389     <dependency>
390         <groupId>wtf.metio.storage-units</groupId>
391         <artifactId>storage-units-model</artifactId>
392         <version>${version.storage-units}</version>
393     </dependency>
395     <!-- Dozer ONLY -->
396     <dependency>
397         <groupId>wtf.metio.storage-units</groupId>
398         <artifactId>storage-units-dozer</artifactId>
399         <version>${version.storage-units}</version>
400     </dependency>
401     <!-- Dozer ONLY -->
403     <!-- EclipseLink ONLY -->
404     <dependency>
405         <groupId>wtf.metio.storage-units</groupId>
406         <artifactId>storage-units-eclipselink</artifactId>
407         <version>${version.storage-units}</version>
408     </dependency>
409     <!-- EclipseLink ONLY -->
411     <!-- GSON ONLY -->
412     <dependency>
413         <groupId>wtf.metio.storage-units</groupId>
414         <artifactId>storage-units-gson</artifactId>
415         <version>${version.storage-units}</version>
416     </dependency>
417     <!-- GSON ONLY -->
419     <!-- Jackson ONLY -->
420     <dependency>
421         <groupId>wtf.metio.storage-units</groupId>
422         <artifactId>storage-units-jackson</artifactId>
423         <version>${version.storage-units}</version>
424     </dependency>
425     <!-- Jackson ONLY -->
427     <!-- Jakarta ONLY -->
428     <dependency>
429         <groupId>wtf.metio.storage-units</groupId>
430         <artifactId>storage-units-jakarta</artifactId>
431         <version>${version.storage-units}</version>
432     </dependency>
433     <!-- Jakarta ONLY -->
435     <!-- MapStruct ONLY -->
436     <dependency>
437         <groupId>wtf.metio.storage-units</groupId>
438         <artifactId>storage-units-mapstruct</artifactId>
439         <version>${version.storage-units}</version>
440     </dependency>
441     <!-- MapStruct ONLY -->
443     <!-- ModelMapper ONLY -->
444     <dependency>
445         <groupId>wtf.metio.storage-units</groupId>
446         <artifactId>storage-units-modelmapper</artifactId>
447         <version>${version.storage-units}</version>
448     </dependency>
449     <!-- ModelMapper ONLY -->
451     <!-- MongoDB ONLY -->
452     <dependency>
453         <groupId>wtf.metio.storage-units</groupId>
454         <artifactId>storage-units-mongodb</artifactId>
455         <version>${version.storage-units}</version>
456     </dependency>
457     <!-- MongoDB ONLY -->
459     <!-- Orika ONLY -->
460     <dependency>
461         <groupId>wtf.metio.storage-units</groupId>
462         <artifactId>storage-units-orika</artifactId>
463         <version>${version.storage-units}</version>
464     </dependency>
465     <!-- Orika ONLY -->
466 </dependencies>
469 Replace `${version.storage-units}` with the [latest release](https://central.sonatype.com/namespace/wtf.metio.storage-units).
471 ## Reference
473 Originally inspired by [Twitters util](https://github.com/twitter/util#space) package.
475 ## Alternatives
477 * [Byte Units](https://github.com/JakeWharton/byteunits)
478 * [triava](https://github.com/trivago/triava)