Skip to content

Commit

Permalink
Merge pull request #346 from pbatko/java-integer
Browse files Browse the repository at this point in the history
Implement Long and Integer's parseUnsigned and toStringUnsigned
  • Loading branch information
densh committed Oct 25, 2016
2 parents 2c2ed58 + 447905a commit 79d2316
Show file tree
Hide file tree
Showing 4 changed files with 280 additions and 10 deletions.
84 changes: 79 additions & 5 deletions javalib/src/main/scala/java/lang/Integer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -451,9 +451,83 @@ object Integer {
@inline def valueOf(s: String, radix: scala.Int): Integer =
valueOf(parseInt(s, radix))

// TODO:
// def parseUnsignedInt(s: String): scala.Int = parseUnsignedInt(s, 10)
// def parseUnsignedInt(s: String, radix: scala.Int): scala.Int = ???
// def toUnsignedString(i: scala.Int): String = toUnsignedString(i, 10)
// def toUnsignedString(_i: scala.Int, _radix: scala.Int): String = ???
@inline def parseUnsignedInt(s: String): scala.Int = parseUnsignedInt(s, 10)

def parseUnsignedInt(s: String, radix: scala.Int): scala.Int = {
if (s == null || radix < Character.MIN_RADIX ||
radix > Character.MAX_RADIX) throw new NumberFormatException(s)

val len = s.length()

if (len == 0) throw new NumberFormatException(s)

val hasPlusSign = s.charAt(0) == '+'

if (hasPlusSign && len == 1) throw new NumberFormatException(s)

val offset = if (hasPlusSign) 1 else 0

parseUnsigned(s, offset, radix)
}

private def parseUnsigned(s: String, _offset: Int, radix: Int): scala.Int = {
val unsignedIntMaxValue = -1
val max = divideUnsigned(unsignedIntMaxValue, radix)
var result = 0
var offset = _offset
val length = s.length()

while (offset < length) {
val digit = Character.digit(s.charAt(offset), radix)
offset += 1

if (digit == -1) throw new NumberFormatException(s)

if (compareUnsigned(result, max) > 0) throw new NumberFormatException(s)

result = result * radix + digit

if (compareUnsigned(digit, result) > 0)
throw new NumberFormatException(s)
}

result
}

@inline def toUnsignedString(i: scala.Int): String = toUnsignedString(i, 10)

def toUnsignedString(_i: scala.Int, _radix: scala.Int): String = {
if (_i == 0) {
"0"
} else {

val radix =
if (_radix < Character.MIN_RADIX || _radix > Character.MAX_RADIX) {
10
} else _radix

var j = _i
var l = _i

// calculate string size
var count = 1
l = divideUnsigned(l, radix)
while (l != 0) {
count += 1
l = divideUnsigned(l, radix)
}

// populate string with characters
val buffer = new Array[Char](count)
do {
val digit = remainderUnsigned(j, radix)
val ch = Character.forDigit(digit.toInt, radix)
count -= 1
buffer(count) = ch
j = divideUnsigned(j, radix)
} while (j != 0)

new String(buffer)
}
}
}
86 changes: 81 additions & 5 deletions javalib/src/main/scala/java/lang/Long.scala
Original file line number Diff line number Diff line change
Expand Up @@ -338,9 +338,85 @@ object Long {
@inline def valueOf(s: String, radix: Int): Long =
valueOf(parseLong(s, radix))

// TODO:
// def parseUnsignedLong(s: String): scala.Long = parseUnsignedLong(s, 10)
// def parseUnsignedLong(s: String, radix: Int): scala.Long = ???
// def toUnsignedString(l: scala.Long): String = toUnsignedString(l, 10)
// def toUnsignedString(l: scala.Long, radix: Int): String = ???
@inline def parseUnsignedLong(s: String): scala.Long =
parseUnsignedLong(s, 10)

def parseUnsignedLong(s: String, radix: Int): scala.Long = {
if (s == null || radix < Character.MIN_RADIX ||
radix > Character.MAX_RADIX) throw new NumberFormatException(s)

val len = s.length()

if (len == 0) throw new NumberFormatException(s)

val hasPlusSign = s.charAt(0) == '+'

if (hasPlusSign && len == 1) throw new NumberFormatException(s)

val offset = if (hasPlusSign) 1 else 0

parseUnsigned(s, offset, radix)
}

private def parseUnsigned(s: String, _offset: Int, radix: Int): scala.Long = {
val unsignedLongMaxValue = -1L
val max = divideUnsigned(unsignedLongMaxValue, radix)
var result = 0L
var offset = _offset
val length = s.length()

while (offset < length) {
val digit = Character.digit(s.charAt(offset), radix)
offset += 1

if (digit == -1) throw new NumberFormatException(s)

if (compareUnsigned(result, max) > 0) throw new NumberFormatException(s)

result = result * radix + digit

if (compareUnsigned(digit, result) > 0)
throw new NumberFormatException(s)
}

result
}

@inline def toUnsignedString(l: scala.Long): String = toUnsignedString(l, 10)

def toUnsignedString(_l: scala.Long, _radix: Int): String = {
if (_l == 0L) {
"0"
} else {

val radix =
if (_radix < Character.MIN_RADIX || _radix > Character.MAX_RADIX) {
10
} else _radix

var j = _l
var l = _l

// calculate string size
var count = 1
l = divideUnsigned(l, radix)
while (l != 0L) {
count += 1
l = divideUnsigned(l, radix)
}

// populate string with characters
val buffer = new Array[Char](count)
do {
val digit = remainderUnsigned(j, radix)
val ch = Character.forDigit(digit.toInt, radix)
count -= 1
buffer(count) = ch
j = divideUnsigned(j, radix)
} while (j != 0)

new String(buffer)
}
}

}
55 changes: 55 additions & 0 deletions unit-tests/src/main/scala/java/lang/IntegerSuite.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package java.lang

object IntegerSuite extends tests.Suite {

val unsignedMaxValue = -1
val unsignedMaxValueText = "4294967295"
val unsignedMaxPlusOneText = "4294967296"

test("parseInt") {
assert(Integer.parseInt("-1").equals(-1))
assert(Integer.parseInt("+1").equals(1))
Expand Down Expand Up @@ -37,7 +42,44 @@ object IntegerSuite extends tests.Suite {

}

test("parseUnsignedInt") {
assert(Integer.parseUnsignedInt("1").equals(1))
assert(Integer.parseUnsignedInt("+1").equals(1))

assert(Integer.parseUnsignedInt("0").equals(0))
assert(Integer.parseUnsignedInt("00").equals(0))

assert(Integer.parseUnsignedInt("+100", 2).equals(4))
assert(Integer.parseUnsignedInt("100", 2).equals(4))

assert(
Integer
.parseUnsignedInt(unsignedMaxValueText)
.equals(unsignedMaxValue))

assertThrows[NumberFormatException](Integer.parseUnsignedInt(null))
assertThrows[NumberFormatException](Integer.parseUnsignedInt("+"))
assertThrows[NumberFormatException](Integer.parseUnsignedInt("-"))
assertThrows[NumberFormatException](Integer.parseUnsignedInt(""))
assertThrows[NumberFormatException](Integer.parseUnsignedInt("-1"))
assertThrows[NumberFormatException](
Integer.parseUnsignedInt("123", Character.MIN_RADIX - 1))
assertThrows[NumberFormatException](
Integer.parseUnsignedInt("123", Character.MAX_RADIX + 1))
assertThrows[NumberFormatException](Integer.parseUnsignedInt("123a", 10))
assertThrows[NumberFormatException](
Integer.parseUnsignedInt(unsignedMaxPlusOneText))

val octalMulOverflow = "137777777770"
// in binary:
// octalMulOverflow: 01011111111111111111111111111111000
// max unsigned: 00011111111111111111111111111111111
assertThrows[NumberFormatException](
Integer.parseUnsignedInt(octalMulOverflow, 8))
}

test("toString") {
assert(Integer.toUnsignedString(0).equals("0"))
assert(Integer.toString(1).equals("1"))
assert(Integer.toString(-1).equals("-1"))
assert(Integer.toString(123).equals("123"))
Expand All @@ -46,6 +88,19 @@ object IntegerSuite extends tests.Suite {
assert(Integer.toString(-1234).equals("-1234"))
}

test("toUnsignedString") {
assert(Integer.toUnsignedString(0).equals("0"))
assert(Integer.toUnsignedString(1).equals("1"))
assert(
Integer
.toUnsignedString(unsignedMaxValue)
.equals(unsignedMaxValueText))
assert(Integer.toUnsignedString(123).equals("123"))
assert(Integer.toUnsignedString(-123).equals("4294967173"))
assert(Integer.toUnsignedString(1234).equals("1234"))
assert(Integer.toUnsignedString(-1234).equals("4294966062"))
}

test("equals") {
assert(new Integer(0).equals(new Integer(0)))
assert(new Integer(1).equals(new Integer(1)))
Expand Down
65 changes: 65 additions & 0 deletions unit-tests/src/main/scala/java/lang/LongSuite.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package java.lang

object LongSuite extends tests.Suite {
val unsignedMaxValueText = "18446744073709551615"
val unsignedMaxPlusOneText = "18446744073709551616"
val unsignedMaxValue = -1L

val signedMaxPlusOneText = "9223372036854775808"
val signedMinMinusOneText = "-9223372036854775809"

test("parseLong") {
assert(Long.parseLong("-1").equals(-1L))
assert(Long.parseLong("+1").equals(1L))
Expand Down Expand Up @@ -30,5 +37,63 @@ object LongSuite extends tests.Suite {
assertThrows[NumberFormatException](
Long.parseLong("123", Character.MAX_RADIX + 1))
assertThrows[NumberFormatException](Long.parseLong("123a", 10))
assertThrows[NumberFormatException](Long.parseLong(signedMinMinusOneText))
assertThrows[NumberFormatException](Long.parseLong(signedMaxPlusOneText))
}

test("parseUnsignedLong") {
assert(Long.parseUnsignedLong("1").equals(1L))
assert(Long.parseUnsignedLong("+1").equals(1L))

assert(Long.parseUnsignedLong("0").equals(0L))
assert(Long.parseUnsignedLong("00").equals(0L))

assert(Long.parseUnsignedLong("+100", 2).equals(4L))
assert(Long.parseUnsignedLong("100", 2).equals(4L))

assert(
Long.parseUnsignedLong(unsignedMaxValueText).equals(unsignedMaxValue))

assertThrows[NumberFormatException](Long.parseUnsignedLong(null))
assertThrows[NumberFormatException](Long.parseUnsignedLong("+"))
assertThrows[NumberFormatException](Long.parseUnsignedLong("-"))
assertThrows[NumberFormatException](Long.parseUnsignedLong(""))
assertThrows[NumberFormatException](Long.parseUnsignedLong("-1"))
assertThrows[NumberFormatException](
Long.parseUnsignedLong("123", Character.MIN_RADIX - 1))
assertThrows[NumberFormatException](
Long.parseUnsignedLong("123", Character.MAX_RADIX + 1))
assertThrows[NumberFormatException](Long.parseUnsignedLong("123a", 10))
assertThrows[NumberFormatException](
Long.parseUnsignedLong(unsignedMaxPlusOneText))

val octalMulOverflow = "5777777777777777777770"
// in binary:
// octalMulOverflow: 0101111111111111111111111111111111111111111111111111111111111111000
// max unsigned: 0001111111111111111111111111111111111111111111111111111111111111111
assertThrows[NumberFormatException](
Long.parseUnsignedLong(octalMulOverflow, 8))
}

test("toString") {
assert(Long.toString(0L).equals("0"))
assert(Long.toString(1L).equals("1"))
assert(Long.toString(-1L).equals("-1"))
assert(Long.toString(123L).equals("123"))
assert(Long.toString(-123L).equals("-123"))
assert(Long.toString(1234L).equals("1234"))
assert(Long.toString(-1234L).equals("-1234"))
}

test("toUnsignedString") {
assert(Long.toUnsignedString(0L).equals("0"))
assert(Long.toUnsignedString(1L).equals("1"))
assert(
Long.toUnsignedString(unsignedMaxValue).equals(unsignedMaxValueText))
assert(Long.toUnsignedString(123L).equals("123"))
assert(Long.toUnsignedString(-123L).equals("18446744073709551493"))
assert(Long.toUnsignedString(1234L).equals("1234"))
assert(Long.toUnsignedString(-1234L).equals("18446744073709550382"))
}

}

0 comments on commit 79d2316

Please sign in to comment.