#3520 - fixing putLongParts in ByteStringBuilder and adding tests

This commit is contained in:
Viktor Klang 2013-07-23 11:59:36 +02:00
parent bc9d8da258
commit 6783bb2729
2 changed files with 75 additions and 48 deletions

View file

@ -67,6 +67,17 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers {
val arbitraryDoubleArray: Arbitrary[Array[Double]] = Arbitrary { Gen.sized { n Gen.containerOfN[Array, Double](n, arbitrary[Double]) } } val arbitraryDoubleArray: Arbitrary[Array[Double]] = Arbitrary { Gen.sized { n Gen.containerOfN[Array, Double](n, arbitrary[Double]) } }
implicit val arbitraryDoubleArraySlice: Arbitrary[ArraySlice[Double]] = arbSlice(arbitraryDoubleArray) implicit val arbitraryDoubleArraySlice: Arbitrary[ArraySlice[Double]] = arbSlice(arbitraryDoubleArray)
type ArrayNumBytes[A] = (Array[A], Int)
implicit val arbitraryLongArrayNumBytes: Arbitrary[ArrayNumBytes[Long]] = Arbitrary {
for {
xs arbitraryLongArray.arbitrary
from choose(0, xs.length)
until choose(from, xs.length)
bytes choose(0, 8)
} yield (xs.slice(from, until), bytes)
}
def likeVector(bs: ByteString)(body: IndexedSeq[Byte] Any): Boolean = { def likeVector(bs: ByteString)(body: IndexedSeq[Byte] Any): Boolean = {
val vec = Vector(bs: _*) val vec = Vector(bs: _*)
body(bs) == body(vec) body(bs) == body(vec)
@ -110,9 +121,9 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers {
bytes.asByteBuffer.order(byteOrder).asShortBuffer.get(reference, 0, n) bytes.asByteBuffer.order(byteOrder).asShortBuffer.get(reference, 0, n)
val input = bytes.iterator val input = bytes.iterator
val decoded = Array.ofDim[Short](n) val decoded = Array.ofDim[Short](n)
for (i 0 to a - 1) decoded(i) = input.getShort(byteOrder) for (i 0 until a) decoded(i) = input.getShort(byteOrder)
input.getShorts(decoded, a, b - a)(byteOrder) input.getShorts(decoded, a, b - a)(byteOrder)
for (i b to n - 1) decoded(i) = input.getShort(byteOrder) for (i b until n) decoded(i) = input.getShort(byteOrder)
(decoded.toSeq == reference.toSeq) && (input.toSeq == bytes.drop(n * elemSize)) (decoded.toSeq == reference.toSeq) && (input.toSeq == bytes.drop(n * elemSize))
} }
@ -124,9 +135,9 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers {
bytes.asByteBuffer.order(byteOrder).asIntBuffer.get(reference, 0, n) bytes.asByteBuffer.order(byteOrder).asIntBuffer.get(reference, 0, n)
val input = bytes.iterator val input = bytes.iterator
val decoded = Array.ofDim[Int](n) val decoded = Array.ofDim[Int](n)
for (i 0 to a - 1) decoded(i) = input.getInt(byteOrder) for (i 0 until a) decoded(i) = input.getInt(byteOrder)
input.getInts(decoded, a, b - a)(byteOrder) input.getInts(decoded, a, b - a)(byteOrder)
for (i b to n - 1) decoded(i) = input.getInt(byteOrder) for (i b until n) decoded(i) = input.getInt(byteOrder)
(decoded.toSeq == reference.toSeq) && (input.toSeq == bytes.drop(n * elemSize)) (decoded.toSeq == reference.toSeq) && (input.toSeq == bytes.drop(n * elemSize))
} }
@ -138,9 +149,9 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers {
bytes.asByteBuffer.order(byteOrder).asLongBuffer.get(reference, 0, n) bytes.asByteBuffer.order(byteOrder).asLongBuffer.get(reference, 0, n)
val input = bytes.iterator val input = bytes.iterator
val decoded = Array.ofDim[Long](n) val decoded = Array.ofDim[Long](n)
for (i 0 to a - 1) decoded(i) = input.getLong(byteOrder) for (i 0 until a) decoded(i) = input.getLong(byteOrder)
input.getLongs(decoded, a, b - a)(byteOrder) input.getLongs(decoded, a, b - a)(byteOrder)
for (i b to n - 1) decoded(i) = input.getLong(byteOrder) for (i b until n) decoded(i) = input.getLong(byteOrder)
(decoded.toSeq == reference.toSeq) && (input.toSeq == bytes.drop(n * elemSize)) (decoded.toSeq == reference.toSeq) && (input.toSeq == bytes.drop(n * elemSize))
} }
@ -152,9 +163,9 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers {
bytes.asByteBuffer.order(byteOrder).asFloatBuffer.get(reference, 0, n) bytes.asByteBuffer.order(byteOrder).asFloatBuffer.get(reference, 0, n)
val input = bytes.iterator val input = bytes.iterator
val decoded = Array.ofDim[Float](n) val decoded = Array.ofDim[Float](n)
for (i 0 to a - 1) decoded(i) = input.getFloat(byteOrder) for (i 0 until a) decoded(i) = input.getFloat(byteOrder)
input.getFloats(decoded, a, b - a)(byteOrder) input.getFloats(decoded, a, b - a)(byteOrder)
for (i b to n - 1) decoded(i) = input.getFloat(byteOrder) for (i b until n) decoded(i) = input.getFloat(byteOrder)
((decoded.toSeq map floatToRawIntBits) == (reference.toSeq map floatToRawIntBits)) && ((decoded.toSeq map floatToRawIntBits) == (reference.toSeq map floatToRawIntBits)) &&
(input.toSeq == bytes.drop(n * elemSize)) (input.toSeq == bytes.drop(n * elemSize))
} }
@ -167,70 +178,85 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers {
bytes.asByteBuffer.order(byteOrder).asDoubleBuffer.get(reference, 0, n) bytes.asByteBuffer.order(byteOrder).asDoubleBuffer.get(reference, 0, n)
val input = bytes.iterator val input = bytes.iterator
val decoded = Array.ofDim[Double](n) val decoded = Array.ofDim[Double](n)
for (i 0 to a - 1) decoded(i) = input.getDouble(byteOrder) for (i 0 until a) decoded(i) = input.getDouble(byteOrder)
input.getDoubles(decoded, a, b - a)(byteOrder) input.getDoubles(decoded, a, b - a)(byteOrder)
for (i b to n - 1) decoded(i) = input.getDouble(byteOrder) for (i b until n) decoded(i) = input.getDouble(byteOrder)
((decoded.toSeq map doubleToRawLongBits) == (reference.toSeq map doubleToRawLongBits)) && ((decoded.toSeq map doubleToRawLongBits) == (reference.toSeq map doubleToRawLongBits)) &&
(input.toSeq == bytes.drop(n * elemSize)) (input.toSeq == bytes.drop(n * elemSize))
} }
def testShortEncoding(slice: ArraySlice[Short], byteOrder: ByteOrder): Boolean = { def testShortEncoding(slice: ArraySlice[Short], byteOrder: ByteOrder): Boolean = {
val elemSize = 2 val elemSize = 2
val (data, from, until) = slice val (data, from, to) = slice
val reference = Array.ofDim[Byte](data.length * elemSize) val reference = Array.ofDim[Byte](data.length * elemSize)
ByteBuffer.wrap(reference).order(byteOrder).asShortBuffer.put(data) ByteBuffer.wrap(reference).order(byteOrder).asShortBuffer.put(data)
val builder = ByteString.newBuilder val builder = ByteString.newBuilder
for (i 0 to from - 1) builder.putShort(data(i))(byteOrder) for (i 0 until from) builder.putShort(data(i))(byteOrder)
builder.putShorts(data, from, until - from)(byteOrder) builder.putShorts(data, from, to - from)(byteOrder)
for (i until to data.length - 1) builder.putShort(data(i))(byteOrder) for (i to until data.length) builder.putShort(data(i))(byteOrder)
reference.toSeq == builder.result reference.toSeq == builder.result
} }
def testIntEncoding(slice: ArraySlice[Int], byteOrder: ByteOrder): Boolean = { def testIntEncoding(slice: ArraySlice[Int], byteOrder: ByteOrder): Boolean = {
val elemSize = 4 val elemSize = 4
val (data, from, until) = slice val (data, from, to) = slice
val reference = Array.ofDim[Byte](data.length * elemSize) val reference = Array.ofDim[Byte](data.length * elemSize)
ByteBuffer.wrap(reference).order(byteOrder).asIntBuffer.put(data) ByteBuffer.wrap(reference).order(byteOrder).asIntBuffer.put(data)
val builder = ByteString.newBuilder val builder = ByteString.newBuilder
for (i 0 to from - 1) builder.putInt(data(i))(byteOrder) for (i 0 until from) builder.putInt(data(i))(byteOrder)
builder.putInts(data, from, until - from)(byteOrder) builder.putInts(data, from, to - from)(byteOrder)
for (i until to data.length - 1) builder.putInt(data(i))(byteOrder) for (i to until data.length) builder.putInt(data(i))(byteOrder)
reference.toSeq == builder.result reference.toSeq == builder.result
} }
def testLongEncoding(slice: ArraySlice[Long], byteOrder: ByteOrder): Boolean = { def testLongEncoding(slice: ArraySlice[Long], byteOrder: ByteOrder): Boolean = {
val elemSize = 8 val elemSize = 8
val (data, from, until) = slice val (data, from, to) = slice
val reference = Array.ofDim[Byte](data.length * elemSize) val reference = Array.ofDim[Byte](data.length * elemSize)
ByteBuffer.wrap(reference).order(byteOrder).asLongBuffer.put(data) ByteBuffer.wrap(reference).order(byteOrder).asLongBuffer.put(data)
val builder = ByteString.newBuilder val builder = ByteString.newBuilder
for (i 0 to from - 1) builder.putLong(data(i))(byteOrder) for (i 0 until from) builder.putLong(data(i))(byteOrder)
builder.putLongs(data, from, until - from)(byteOrder) builder.putLongs(data, from, to - from)(byteOrder)
for (i until to data.length - 1) builder.putLong(data(i))(byteOrder) for (i to until data.length) builder.putLong(data(i))(byteOrder)
reference.toSeq == builder.result reference.toSeq == builder.result
} }
def testLongPartEncoding(anb: ArrayNumBytes[Long], byteOrder: ByteOrder): Boolean = {
val elemSize = 8
val (data, nBytes) = anb
val reference = Array.ofDim[Byte](data.length * elemSize)
ByteBuffer.wrap(reference).order(byteOrder).asLongBuffer.put(data)
val builder = ByteString.newBuilder
for (i 0 until data.length) builder.putLongPart(data(i), nBytes)(byteOrder)
reference.zipWithIndex.collect({ // Since there is no partial put on LongBuffer, we need to collect only the interesting bytes
case (r, i) if byteOrder == ByteOrder.LITTLE_ENDIAN && i % elemSize < nBytes r
case (r, i) if byteOrder == ByteOrder.BIG_ENDIAN && i % elemSize >= (elemSize - nBytes) r
}).toSeq == builder.result
}
def testFloatEncoding(slice: ArraySlice[Float], byteOrder: ByteOrder): Boolean = { def testFloatEncoding(slice: ArraySlice[Float], byteOrder: ByteOrder): Boolean = {
val elemSize = 4 val elemSize = 4
val (data, from, until) = slice val (data, from, to) = slice
val reference = Array.ofDim[Byte](data.length * elemSize) val reference = Array.ofDim[Byte](data.length * elemSize)
ByteBuffer.wrap(reference).order(byteOrder).asFloatBuffer.put(data) ByteBuffer.wrap(reference).order(byteOrder).asFloatBuffer.put(data)
val builder = ByteString.newBuilder val builder = ByteString.newBuilder
for (i 0 to from - 1) builder.putFloat(data(i))(byteOrder) for (i 0 until from) builder.putFloat(data(i))(byteOrder)
builder.putFloats(data, from, until - from)(byteOrder) builder.putFloats(data, from, to - from)(byteOrder)
for (i until to data.length - 1) builder.putFloat(data(i))(byteOrder) for (i to until data.length) builder.putFloat(data(i))(byteOrder)
reference.toSeq == builder.result reference.toSeq == builder.result
} }
def testDoubleEncoding(slice: ArraySlice[Double], byteOrder: ByteOrder): Boolean = { def testDoubleEncoding(slice: ArraySlice[Double], byteOrder: ByteOrder): Boolean = {
val elemSize = 8 val elemSize = 8
val (data, from, until) = slice val (data, from, to) = slice
val reference = Array.ofDim[Byte](data.length * elemSize) val reference = Array.ofDim[Byte](data.length * elemSize)
ByteBuffer.wrap(reference).order(byteOrder).asDoubleBuffer.put(data) ByteBuffer.wrap(reference).order(byteOrder).asDoubleBuffer.put(data)
val builder = ByteString.newBuilder val builder = ByteString.newBuilder
for (i 0 to from - 1) builder.putDouble(data(i))(byteOrder) for (i 0 until from) builder.putDouble(data(i))(byteOrder)
builder.putDoubles(data, from, until - from)(byteOrder) builder.putDoubles(data, from, to - from)(byteOrder)
for (i until to data.length - 1) builder.putDouble(data(i))(byteOrder) for (i to until data.length) builder.putDouble(data(i))(byteOrder)
reference.toSeq == builder.result reference.toSeq == builder.result
} }
@ -405,12 +431,12 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers {
"getting Bytes, using getByte and getBytes" in { "getting Bytes, using getByte and getBytes" in {
// mixing getByte and getBytes here for more rigorous testing // mixing getByte and getBytes here for more rigorous testing
check { slice: ByteStringSlice check { slice: ByteStringSlice
val (bytes, from, until) = slice val (bytes, from, to) = slice
val input = bytes.iterator val input = bytes.iterator
val output = Array.ofDim[Byte](bytes.length) val output = Array.ofDim[Byte](bytes.length)
for (i 0 to from - 1) output(i) = input.getByte for (i 0 until from) output(i) = input.getByte
input.getBytes(output, from, until - from) input.getBytes(output, from, to - from)
for (i until to bytes.length - 1) output(i) = input.getByte for (i to until bytes.length) output(i) = input.getByte
(output.toSeq == bytes) && (input.isEmpty) (output.toSeq == bytes) && (input.isEmpty)
} }
} }
@ -418,9 +444,9 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers {
"getting Bytes, using the InputStream wrapper" in { "getting Bytes, using the InputStream wrapper" in {
// combining skip and both read methods here for more rigorous testing // combining skip and both read methods here for more rigorous testing
check { slice: ByteStringSlice check { slice: ByteStringSlice
val (bytes, from, until) = slice val (bytes, from, to) = slice
val a = (0 max from) min bytes.length val a = (0 max from) min bytes.length
val b = (a max until) min bytes.length val b = (a max to) min bytes.length
val input = bytes.iterator val input = bytes.iterator
val output = Array.ofDim[Byte](bytes.length) val output = Array.ofDim[Byte](bytes.length)
@ -435,7 +461,7 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers {
} }
if (eof) throw new RuntimeException("Unexpected EOF") if (eof) throw new RuntimeException("Unexpected EOF")
for (i b to bytes.length - 1) output(i) = input.asInputStream.read().toByte for (i b until bytes.length) output(i) = input.asInputStream.read().toByte
(output.toSeq.drop(a) == bytes.drop(a)) && (output.toSeq.drop(a) == bytes.drop(a)) &&
(input.asInputStream.read() == -1) && (input.asInputStream.read() == -1) &&
@ -488,11 +514,11 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers {
"putting Bytes, using putByte and putBytes" in { "putting Bytes, using putByte and putBytes" in {
// mixing putByte and putBytes here for more rigorous testing // mixing putByte and putBytes here for more rigorous testing
check { slice: ArraySlice[Byte] check { slice: ArraySlice[Byte]
val (data, from, until) = slice val (data, from, to) = slice
val builder = ByteString.newBuilder val builder = ByteString.newBuilder
for (i 0 to from - 1) builder.putByte(data(i)) for (i 0 until from) builder.putByte(data(i))
builder.putBytes(data, from, until - from) builder.putBytes(data, from, to - from)
for (i until to data.length - 1) builder.putByte(data(i)) for (i to until data.length) builder.putByte(data(i))
data.toSeq == builder.result data.toSeq == builder.result
} }
} }
@ -500,11 +526,11 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers {
"putting Bytes, using the OutputStream wrapper" in { "putting Bytes, using the OutputStream wrapper" in {
// mixing the write methods here for more rigorous testing // mixing the write methods here for more rigorous testing
check { slice: ArraySlice[Byte] check { slice: ArraySlice[Byte]
val (data, from, until) = slice val (data, from, to) = slice
val builder = ByteString.newBuilder val builder = ByteString.newBuilder
for (i 0 to from - 1) builder.asOutputStream.write(data(i).toInt) for (i 0 until from) builder.asOutputStream.write(data(i).toInt)
builder.asOutputStream.write(data, from, until - from) builder.asOutputStream.write(data, from, to - from)
for (i until to data.length - 1) builder.asOutputStream.write(data(i).toInt) for (i to until data.length) builder.asOutputStream.write(data(i).toInt)
data.toSeq == builder.result data.toSeq == builder.result
} }
} }
@ -517,6 +543,8 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers {
"encoding Int in little-endian" in { check { slice: ArraySlice[Int] testIntEncoding(slice, LITTLE_ENDIAN) } } "encoding Int in little-endian" in { check { slice: ArraySlice[Int] testIntEncoding(slice, LITTLE_ENDIAN) } }
"encoding Long in big-endian" in { check { slice: ArraySlice[Long] testLongEncoding(slice, BIG_ENDIAN) } } "encoding Long in big-endian" in { check { slice: ArraySlice[Long] testLongEncoding(slice, BIG_ENDIAN) } }
"encoding Long in little-endian" in { check { slice: ArraySlice[Long] testLongEncoding(slice, LITTLE_ENDIAN) } } "encoding Long in little-endian" in { check { slice: ArraySlice[Long] testLongEncoding(slice, LITTLE_ENDIAN) } }
"encoding LongPart in big-endian" in { check { slice: ArrayNumBytes[Long] testLongPartEncoding(slice, BIG_ENDIAN) } }
"encoding LongPart in little-endian" in { check { slice: ArrayNumBytes[Long] testLongPartEncoding(slice, LITTLE_ENDIAN) } }
"encoding Float in big-endian" in { check { slice: ArraySlice[Float] testFloatEncoding(slice, BIG_ENDIAN) } } "encoding Float in big-endian" in { check { slice: ArraySlice[Float] testFloatEncoding(slice, BIG_ENDIAN) } }
"encoding Float in little-endian" in { check { slice: ArraySlice[Float] testFloatEncoding(slice, LITTLE_ENDIAN) } } "encoding Float in little-endian" in { check { slice: ArraySlice[Float] testFloatEncoding(slice, LITTLE_ENDIAN) } }
"encoding Double in big-endian" in { check { slice: ArraySlice[Double] testDoubleEncoding(slice, BIG_ENDIAN) } } "encoding Double in big-endian" in { check { slice: ArraySlice[Double] testDoubleEncoding(slice, BIG_ENDIAN) } }

View file

@ -658,10 +658,9 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] {
fillArray(n) { (target, offset) fillArray(n) { (target, offset)
if (byteOrder == ByteOrder.BIG_ENDIAN) { if (byteOrder == ByteOrder.BIG_ENDIAN) {
val start = n * 8 - 8 val start = n * 8 - 8
(0 until n) foreach (i target(offset + i) = (x >>> start - 8 * i).toByte) (0 until n) foreach { i target(offset + i) = (x >>> start - 8 * i).toByte }
} else if (byteOrder == ByteOrder.LITTLE_ENDIAN) { } else if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
val end = offset + n - 1 (0 until n) foreach { i target(offset + i) = (x >>> 8 * i).toByte }
(0 until n) foreach (i target(end - i) = (x >>> 8 * i).toByte)
} else throw new IllegalArgumentException("Unknown byte order " + byteOrder) } else throw new IllegalArgumentException("Unknown byte order " + byteOrder)
} }
} }