-
Notifications
You must be signed in to change notification settings - Fork 580
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Problem Some of the conversions in `c.t.conversions` could benefit from using `AnyVal`. However, due to ABI breakages this is, wait for it, a tough conversion to make. Solution Introduce new implementations that follow a naming scheme of `SomethingOps`. Where possible the implementations are `AnyVal` based avoiding allocations for the common usage pattern. Migrate using drop-in replacements by updating imports. Result Consistency and less allocations for users. JIRA Issues: CSL-7356 Differential Revision: https://phabricator.twitter.biz/D249403
- Loading branch information
1 parent
8919bb2
commit ee56e5f
Showing
18 changed files
with
438 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
util-core/src/main/scala/com/twitter/conversions/DurationOps.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package com.twitter.conversions | ||
|
||
import com.twitter.util.Duration | ||
import java.util.concurrent.TimeUnit | ||
|
||
/** | ||
* Implicits for writing readable [[Duration]]s. | ||
* | ||
* @example | ||
* {{{ | ||
* import com.twitter.conversions.DurationOps._ | ||
* | ||
* 2000.nanoseconds | ||
* 50.milliseconds | ||
* 1.second | ||
* 24.hours | ||
* 40.days | ||
* }}} | ||
*/ | ||
object DurationOps { | ||
|
||
implicit class RichLong(val numNanos: Long) extends AnyVal { | ||
def nanoseconds: Duration = Duration(numNanos, TimeUnit.NANOSECONDS) | ||
def nanosecond: Duration = nanoseconds | ||
def microseconds: Duration = Duration(numNanos, TimeUnit.MICROSECONDS) | ||
def microsecond: Duration = microseconds | ||
def milliseconds: Duration = Duration(numNanos, TimeUnit.MILLISECONDS) | ||
def millisecond: Duration = milliseconds | ||
def millis: Duration = milliseconds | ||
def seconds: Duration = Duration(numNanos, TimeUnit.SECONDS) | ||
def second: Duration = seconds | ||
def minutes: Duration = Duration(numNanos, TimeUnit.MINUTES) | ||
def minute: Duration = minutes | ||
def hours: Duration = Duration(numNanos, TimeUnit.HOURS) | ||
def hour: Duration = hours | ||
def days: Duration = Duration(numNanos, TimeUnit.DAYS) | ||
def day: Duration = days | ||
} | ||
|
||
} |
33 changes: 33 additions & 0 deletions
33
util-core/src/main/scala/com/twitter/conversions/PercentOps.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package com.twitter.conversions | ||
|
||
/** | ||
* Implicits for turning `x.percent` (where `x` is an `Int` or `Double`) into a `Double` | ||
* scaled to where 1.0 is 100 percent. | ||
* | ||
* @note Negative values, fractional values, and values greater than 100 are permitted. | ||
* | ||
* @example | ||
* {{{ | ||
* import com.twitter.conversions.PercentOps._ | ||
* | ||
* 1.percent == 0.01 | ||
* 100.percent == 1.0 | ||
* 99.9.percent == 0.999 | ||
* 500.percent == 5.0 | ||
* -10.percent == -0.1 | ||
* }}} | ||
*/ | ||
object PercentOps { | ||
|
||
private val BigDecimal100 = BigDecimal(100.0) | ||
|
||
implicit class RichDouble(val value: Double) extends AnyVal { | ||
// convert wrapped to BigDecimal to preserve precision when dividing Doubles | ||
def percent: Double = | ||
if (value.equals(Double.NaN) | ||
|| value.equals(Double.PositiveInfinity) | ||
|| value.equals(Double.NegativeInfinity)) value | ||
else (BigDecimal(value) / BigDecimal100).doubleValue | ||
} | ||
|
||
} |
37 changes: 37 additions & 0 deletions
37
util-core/src/main/scala/com/twitter/conversions/StorageUnitOps.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package com.twitter.conversions | ||
|
||
import com.twitter.util.StorageUnit | ||
|
||
/** | ||
* Implicits for writing readable [[StorageUnit]]s. | ||
* | ||
* @example | ||
* {{{ | ||
* import com.twitter.conversions.StorageUnitOps._ | ||
* | ||
* 5.bytes | ||
* 1.kilobyte | ||
* 256.gigabytes | ||
* }}} | ||
*/ | ||
object StorageUnitOps { | ||
|
||
implicit class RichLong(val numBytes: Long) extends AnyVal { | ||
def byte: StorageUnit = bytes | ||
def bytes: StorageUnit = StorageUnit.fromBytes(numBytes) | ||
def kilobyte: StorageUnit = kilobytes | ||
def kilobytes: StorageUnit = StorageUnit.fromKilobytes(numBytes) | ||
def megabyte: StorageUnit = megabytes | ||
def megabytes: StorageUnit = StorageUnit.fromMegabytes(numBytes) | ||
def gigabyte: StorageUnit = gigabytes | ||
def gigabytes: StorageUnit = StorageUnit.fromGigabytes(numBytes) | ||
def terabyte: StorageUnit = terabytes | ||
def terabytes: StorageUnit = StorageUnit.fromTerabytes(numBytes) | ||
def petabyte: StorageUnit = petabytes | ||
def petabytes: StorageUnit = StorageUnit.fromPetabytes(numBytes) | ||
|
||
def thousand: Long = numBytes * 1000 | ||
def million: Long = numBytes * 1000 * 1000 | ||
def billion: Long = numBytes * 1000 * 1000 * 1000 | ||
} | ||
} |
144 changes: 144 additions & 0 deletions
144
util-core/src/main/scala/com/twitter/conversions/StringOps.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
package com.twitter.conversions | ||
|
||
import scala.util.matching.Regex | ||
|
||
object StringOps { | ||
|
||
// we intentionally don't unquote "\$" here, so it can be used to escape interpolation later. | ||
private val UNQUOTE_RE = """\\(u[\dA-Fa-f]{4}|x[\dA-Fa-f]{2}|[/rnt\"\\])""".r | ||
|
||
private val QUOTE_RE = "[\u0000-\u001f\u007f-\uffff\\\\\"]".r | ||
|
||
implicit final class RichString(val string: String) extends AnyVal { | ||
|
||
/** | ||
* For every section of a string that matches a regular expression, call | ||
* a function to determine a replacement (as in python's | ||
* `re.sub`). The function will be passed the Matcher object | ||
* corresponding to the substring that matches the pattern, and that | ||
* substring will be replaced by the function's result. | ||
* | ||
* For example, this call: | ||
* {{{ | ||
* "ohio".regexSub("""h.""".r) { m => "n" } | ||
* }}} | ||
* will return the string `"ono"`. | ||
* | ||
* The matches are found using `Matcher.find()` and so | ||
* will obey all the normal java rules (the matches will not overlap, | ||
* etc). | ||
* | ||
* @param re the regex pattern to replace | ||
* @param replace a function that takes Regex.MatchData objects and | ||
* returns a string to substitute | ||
* @return the resulting string with replacements made | ||
*/ | ||
def regexSub(re: Regex)(replace: Regex.MatchData => String): String = { | ||
var offset = 0 | ||
val out = new StringBuilder() | ||
|
||
for (m <- re.findAllIn(string).matchData) { | ||
if (m.start > offset) { | ||
out.append(string.substring(offset, m.start)) | ||
} | ||
|
||
out.append(replace(m)) | ||
offset = m.end | ||
} | ||
|
||
if (offset < string.length) { | ||
out.append(string.substring(offset)) | ||
} | ||
out.toString | ||
} | ||
|
||
/** | ||
* Quote a string so that unprintable chars (in ASCII) are represented by | ||
* C-style backslash expressions. For example, a raw linefeed will be | ||
* translated into `"\n"`. Control codes (anything below 0x20) | ||
* and unprintables (anything above 0x7E) are turned into either | ||
* `"\xHH"` or `"\\uHHHH"` expressions, depending on | ||
* their range. Embedded backslashes and double-quotes are also quoted. | ||
* | ||
* @return a quoted string, suitable for ASCII display | ||
*/ | ||
def quoteC(): String = { | ||
regexSub(QUOTE_RE) { m => | ||
m.matched.charAt(0) match { | ||
case '\r' => "\\r" | ||
case '\n' => "\\n" | ||
case '\t' => "\\t" | ||
case '"' => "\\\"" | ||
case '\\' => "\\\\" | ||
case c => | ||
if (c <= 255) { | ||
"\\x%02x".format(c.asInstanceOf[Int]) | ||
} else { | ||
"\\u%04x" format c.asInstanceOf[Int] | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Unquote an ASCII string that has been quoted in a style like | ||
* [[quoteC()]] and convert it into a standard unicode string. | ||
* `"\\uHHHH"` and `"\xHH"` expressions are unpacked | ||
* into unicode characters, as well as `"\r"`, `"\n"`, | ||
* `"\t"`, `"\\"`, and `'\"'`. | ||
* | ||
* @return an unquoted unicode string | ||
*/ | ||
def unquoteC(): String = { | ||
regexSub(UNQUOTE_RE) { m => | ||
val ch = m.group(1).charAt(0) match { | ||
// holy crap! this is terrible: | ||
case 'u' => | ||
Character.valueOf(Integer.valueOf(m.group(1).substring(1), 16).asInstanceOf[Int].toChar) | ||
case 'x' => | ||
Character.valueOf(Integer.valueOf(m.group(1).substring(1), 16).asInstanceOf[Int].toChar) | ||
case 'r' => '\r' | ||
case 'n' => '\n' | ||
case 't' => '\t' | ||
case x => x | ||
} | ||
ch.toString | ||
} | ||
} | ||
|
||
/** | ||
* Turn a string of hex digits into a byte array. This does the exact | ||
* opposite of `Array[Byte]#hexlify`. | ||
*/ | ||
def unhexlify(): Array[Byte] = { | ||
val buffer = new Array[Byte]((string.length + 1) / 2) | ||
string.grouped(2).toSeq.zipWithIndex.foreach { | ||
case (substr, i) => | ||
buffer(i) = Integer.parseInt(substr, 16).toByte | ||
} | ||
buffer | ||
} | ||
} | ||
|
||
def hexlify(array: Array[Byte], from: Int, to: Int): String = { | ||
val out = new StringBuffer | ||
for (i <- from until to) { | ||
val b = array(i) | ||
val s = (b.toInt & 0xff).toHexString | ||
if (s.length < 2) { | ||
out.append('0') | ||
} | ||
out.append(s) | ||
} | ||
out.toString | ||
} | ||
|
||
implicit final class RichByteArray(val bytes: Array[Byte]) extends AnyVal { | ||
|
||
/** | ||
* Turn an `Array[Byte]` into a string of hex digits. | ||
*/ | ||
def hexlify: String = StringOps.hexlify(bytes, 0, bytes.length) | ||
} | ||
|
||
} |
19 changes: 19 additions & 0 deletions
19
util-core/src/main/scala/com/twitter/conversions/ThreadOps.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package com.twitter.conversions | ||
|
||
import java.util.concurrent.Callable | ||
import scala.language.implicitConversions | ||
|
||
/** | ||
* Implicits for turning a block of code into a Runnable or Callable. | ||
*/ | ||
object ThreadOps { | ||
|
||
implicit def makeRunnable(f: => Unit): Runnable = new Runnable() { | ||
def run(): Unit = f | ||
} | ||
|
||
implicit def makeCallable[T](f: => T): Callable[T] = new Callable[T]() { | ||
def call(): T = f | ||
} | ||
|
||
} |
22 changes: 22 additions & 0 deletions
22
util-core/src/main/scala/com/twitter/conversions/U64Ops.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.twitter.conversions | ||
|
||
object U64Ops { | ||
|
||
/** | ||
* Parses this HEX string as an unsigned 64-bit long value. Be careful, this can throw | ||
* [[NumberFormatException]]. | ||
* | ||
* @see [[java.lang.Long.parseUnsignedLong()]] | ||
*/ | ||
implicit class StringOps(val self: String) extends AnyVal { | ||
def toU64Long: Long = java.lang.Long.parseUnsignedLong(self, 16) | ||
} | ||
|
||
/** | ||
* Converts this unsigned 64-bit long value into a 16-character HEX string. | ||
*/ | ||
implicit class LongOps(val self: Long) extends AnyVal { | ||
def toU64HexString: String = "%016x".format(self) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.