Skip to content

Commit

Permalink
util-core: Add Java-friendly API for StorageUnit
Browse files Browse the repository at this point in the history
Problem

`StorageUnit` is lacking a non-implicit API for building instances from
different units (bytes, kilobytes, megabytes, etc). This makes it quite
unusable from the Java side.

Solution

Add `fromX` method on the companion object so they will compile into
static methods hence will be available from Java.

RB_ID=864546
  • Loading branch information
vkostyukov authored and jenkins committed Aug 26, 2016
1 parent 19d7613 commit d0aca38
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 27 deletions.
3 changes: 3 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ New Features:
runs blocking code, it will log the stacktrace for that fraction of
the calls. ``RB_ID=861892``

* util-core: Add Java-friendly API for `StorageUnit`. See `StorageUnit.fromX`
and `StorageUnit.{times, plus, minus, divide}` methods. ``RB_ID=864546``

Changes in Runtime Behavior:

* util-eval: The compiler reporter is now reset between code check invocations.
Expand Down
12 changes: 6 additions & 6 deletions util-core/src/main/scala/com/twitter/conversions/storage.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ import scala.language.implicitConversions
object storage {
class RichWholeNumber(wrapped: Long) {
def byte: StorageUnit = bytes
def bytes: StorageUnit = new StorageUnit(wrapped)
def bytes: StorageUnit = StorageUnit.fromBytes(wrapped)
def kilobyte: StorageUnit = kilobytes
def kilobytes: StorageUnit = new StorageUnit(wrapped * 1024)
def kilobytes: StorageUnit = StorageUnit.fromKilobytes(wrapped)
def megabyte: StorageUnit = megabytes
def megabytes: StorageUnit = new StorageUnit(wrapped * 1024 * 1024)
def megabytes: StorageUnit = StorageUnit.fromMegabytes(wrapped)
def gigabyte: StorageUnit = gigabytes
def gigabytes: StorageUnit = new StorageUnit(wrapped * 1024 * 1024 * 1024)
def gigabytes: StorageUnit = StorageUnit.fromGigabytes(wrapped)
def terabyte: StorageUnit = terabytes
def terabytes: StorageUnit = new StorageUnit(wrapped * 1024 * 1024 * 1024 * 1024)
def terabytes: StorageUnit = StorageUnit.fromTerabytes(wrapped)
def petabyte: StorageUnit = petabytes
def petabytes: StorageUnit = new StorageUnit(wrapped * 1024 * 1024 * 1024 * 1024 * 1024)
def petabytes: StorageUnit = StorageUnit.fromPetabytes(wrapped)

def thousand: Long = wrapped * 1000
def million: Long = wrapped * 1000 * 1000
Expand Down
67 changes: 46 additions & 21 deletions util-core/src/main/scala/com/twitter/util/StorageUnit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,19 @@ package com.twitter.util
import java.util.Locale

object StorageUnit {

def fromBytes(bytes: Long): StorageUnit = new StorageUnit(bytes)
def fromKilobytes(kilobytes: Long): StorageUnit = new StorageUnit(kilobytes * 1024)
def fromMegabytes(megabytes: Long): StorageUnit = new StorageUnit(megabytes * 1024 * 1024)
def fromGigabytes(gigabytes: Long): StorageUnit = new StorageUnit(gigabytes * 1024 * 1024 * 1024)
def fromTerabytes(terabytes: Long): StorageUnit = new StorageUnit(terabytes * 1024 * 1024 * 1024 * 1024)
def fromPetabytes(petabytes: Long): StorageUnit = new StorageUnit(petabytes * 1024 * 1024 * 1024 * 1024 * 1024)
def fromExabytes(exabytes: Long): StorageUnit = new StorageUnit(exabytes * 1024 * 1024 * 1024 * 1024 * 1024 * 1024)

val infinite = new StorageUnit(Long.MaxValue)
val zero = new StorageUnit(0)

private def factor(s: String) = {
private def factor(s: String): Long = {
var lower = s.toLowerCase
if (lower endsWith "s")
lower = lower dropRight 1
Expand Down Expand Up @@ -58,39 +67,55 @@ object StorageUnit {
/**
* Representation of storage units.
*
* If you import the [[com.twitter.conversions.storage]] implicits you can
* write human-readable values such as `1.gigabyte` or `50.megabytes`.
* Use either `StorageUnit.fromX` or [[com.twitter.conversions.storage implicit conversions]]
* from `Long` and `Int` to construct instances.
*
* {{{
* import com.twitter.conversions.storage._
* import com.twitter.util.StorageUnit
*
* val size: StorageUnit = 10.kilobytes
* }}}
*
* Note: operations can cause overflows of the Long used to represent the
* number of bytes.
* @note While all the methods in this abstraction are prefixed according
* to the International System of Units (kilo-, mega-, etc), they
* actually operate on the 1024 scale (not 1000).
*
* @note Operations can cause overflows of the `Long` used to represent the
* number of bytes.
*/
class StorageUnit(val bytes: Long) extends Ordered[StorageUnit] {
def inBytes = bytes
def inKilobytes = bytes / (1024L)
def inMegabytes = bytes / (1024L * 1024)
def inGigabytes = bytes / (1024L * 1024 * 1024)
def inTerabytes = bytes / (1024L * 1024 * 1024 * 1024)
def inPetabytes = bytes / (1024L * 1024 * 1024 * 1024 * 1024)
def inExabytes = bytes / (1024L * 1024 * 1024 * 1024 * 1024 * 1024)
def inBytes: Long = bytes
def inKilobytes: Long = bytes / 1024L
def inMegabytes: Long = bytes / (1024L * 1024)
def inGigabytes: Long = bytes / (1024L * 1024 * 1024)
def inTerabytes: Long = bytes / (1024L * 1024 * 1024 * 1024)
def inPetabytes: Long = bytes / (1024L * 1024 * 1024 * 1024 * 1024)
def inExabytes: Long = bytes / (1024L * 1024 * 1024 * 1024 * 1024 * 1024)

def +(that: StorageUnit): StorageUnit = new StorageUnit(this.bytes + that.bytes)
def -(that: StorageUnit): StorageUnit = new StorageUnit(this.bytes - that.bytes)
def *(scalar: Double): StorageUnit = new StorageUnit((this.bytes.toDouble*scalar).toLong)
def *(scalar: Long): StorageUnit = new StorageUnit(this.bytes * scalar)
def /(scalar: Long): StorageUnit = new StorageUnit(this.bytes / scalar)

override def equals(other: Any) = {
other match {
case other: StorageUnit =>
inBytes == other.inBytes
case _ =>
false
}
// Java-friendly API for binary operations.
def plus(that: StorageUnit): StorageUnit = this + that
def minus(that: StorageUnit): StorageUnit = this - that
def times(scalar: Double): StorageUnit = this * scalar
def times(scalar: Long): StorageUnit = this * scalar
def divide(scalar: Long): StorageUnit = this / scalar

override def equals(other: Any): Boolean = other match {
case other: StorageUnit =>
inBytes == other.inBytes
case _ =>
false
}

override def hashCode: Int = bytes.hashCode

override def compare(other: StorageUnit) =
override def compare(other: StorageUnit): Int =
if (bytes < other.bytes) -1 else if (bytes > other.bytes) 1 else 0

def min(other: StorageUnit): StorageUnit =
Expand All @@ -99,7 +124,7 @@ class StorageUnit(val bytes: Long) extends Ordered[StorageUnit] {
def max(other: StorageUnit): StorageUnit =
if (this > other) this else other

override def toString() = inBytes + ".bytes"
override def toString(): String = inBytes + ".bytes"

def toHuman(): String = {
val prefix = "KMGTPE"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.twitter.util;

import org.junit.Test;

public class StorageUnitCompilationTest {

@Test
public void testFrom() {
StorageUnit a = StorageUnit.fromBytes(1);
StorageUnit b = StorageUnit.fromKilobytes(1);
StorageUnit c = StorageUnit.fromMegabytes(1);
StorageUnit d = StorageUnit.fromTerabytes(1);
StorageUnit e = StorageUnit.fromPetabytes(1);
StorageUnit f = StorageUnit.fromExabytes(1);
}

@Test
public void testBinaryOperations() {
StorageUnit a = StorageUnit.fromBytes(1);
StorageUnit b = StorageUnit.fromBytes(1);

a.plus(b).minus(a).times(1.0).times(1L).divide(1L);
}
}

0 comments on commit d0aca38

Please sign in to comment.