Backport of ENT-1303 applied to 3.0-RC3 (#2332)

This commit is contained in:
Rick Parker 2018-01-08 12:11:55 +00:00 committed by GitHub
parent 5924427659
commit c5149bab9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 37 additions and 14 deletions

View File

@ -13,8 +13,22 @@ import javax.xml.bind.DatatypeConverter
*/ */
@CordaSerializable @CordaSerializable
sealed class ByteSequence : Comparable<ByteSequence> { sealed class ByteSequence : Comparable<ByteSequence> {
constructor() {
this._bytes = COPY_BYTES
}
/** /**
* The underlying bytes. * This constructor allows to bypass calls to [bytes] for functions in this class if the implementation
* of [bytes] makes a copy of the underlying [ByteArray] (as [OpaqueBytes] does for safety). This improves
* performance. It is recommended to use this constructor rather than the default constructor.
*/
constructor(uncopiedBytes: ByteArray) {
this._bytes = uncopiedBytes
}
/**
* The underlying bytes. Some implementations may choose to make a copy of the underlying [ByteArray] for
* security reasons. For example, [OpaqueBytes].
*/ */
abstract val bytes: ByteArray abstract val bytes: ByteArray
/** /**
@ -26,8 +40,11 @@ sealed class ByteSequence : Comparable<ByteSequence> {
*/ */
abstract val offset: Int abstract val offset: Int
private val _bytes: ByteArray
get() = if (field === COPY_BYTES) bytes else field
/** Returns a [ByteArrayInputStream] of the bytes */ /** Returns a [ByteArrayInputStream] of the bytes */
fun open() = ByteArrayInputStream(bytes, offset, size) fun open() = ByteArrayInputStream(_bytes, offset, size)
/** /**
* Create a sub-sequence backed by the same array. * Create a sub-sequence backed by the same array.
@ -38,19 +55,22 @@ sealed class ByteSequence : Comparable<ByteSequence> {
fun subSequence(offset: Int, size: Int): ByteSequence { fun subSequence(offset: Int, size: Int): ByteSequence {
require(offset >= 0) require(offset >= 0)
require(offset + size <= this.size) require(offset + size <= this.size)
// Intentionally use bytes rather than _bytes, to mirror the copy-or-not behaviour of that property.
return if (offset == 0 && size == this.size) this else of(bytes, this.offset + offset, size) return if (offset == 0 && size == this.size) this else of(bytes, this.offset + offset, size)
} }
companion object { companion object {
/** /**
* Construct a [ByteSequence] given a [ByteArray] and optional offset and size, that represents that potentially * Construct a [ByteSequence] given a [ByteArray] and optional offset and size, that represents that potentially
* sub-sequence of bytes. The returned implementation is optimised when the whole [ByteArray] is the sequence. * sub-sequence of bytes.
*/ */
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun of(bytes: ByteArray, offset: Int = 0, size: Int = bytes.size): ByteSequence { fun of(bytes: ByteArray, offset: Int = 0, size: Int = bytes.size): ByteSequence {
return if (offset == 0 && size == bytes.size && size != 0) OpaqueBytes(bytes) else OpaqueBytesSubSequence(bytes, offset, size) return OpaqueBytesSubSequence(bytes, offset, size)
} }
private val COPY_BYTES: ByteArray = ByteArray(0)
} }
/** /**
@ -65,7 +85,7 @@ sealed class ByteSequence : Comparable<ByteSequence> {
* Copy this sequence, complete with new backing array. This can be helpful to break references to potentially * Copy this sequence, complete with new backing array. This can be helpful to break references to potentially
* large backing arrays from small sub-sequences. * large backing arrays from small sub-sequences.
*/ */
fun copy(): ByteSequence = of(bytes.copyOfRange(offset, offset + size)) fun copy(): ByteSequence = of(_bytes.copyOfRange(offset, offset + size))
/** /**
* Compare byte arrays byte by byte. Arrays that are shorter are deemed less than longer arrays if all the bytes * Compare byte arrays byte by byte. Arrays that are shorter are deemed less than longer arrays if all the bytes
@ -73,10 +93,12 @@ sealed class ByteSequence : Comparable<ByteSequence> {
*/ */
override fun compareTo(other: ByteSequence): Int { override fun compareTo(other: ByteSequence): Int {
val min = minOf(this.size, other.size) val min = minOf(this.size, other.size)
val thisBytes = this._bytes
val otherBytes = other._bytes
// Compare min bytes // Compare min bytes
for (index in 0 until min) { for (index in 0 until min) {
val unsignedThis = java.lang.Byte.toUnsignedInt(this.bytes[this.offset + index]) val unsignedThis = java.lang.Byte.toUnsignedInt(thisBytes[this.offset + index])
val unsignedOther = java.lang.Byte.toUnsignedInt(other.bytes[other.offset + index]) val unsignedOther = java.lang.Byte.toUnsignedInt(otherBytes[other.offset + index])
if (unsignedThis != unsignedOther) { if (unsignedThis != unsignedOther) {
return Integer.signum(unsignedThis - unsignedOther) return Integer.signum(unsignedThis - unsignedOther)
} }
@ -89,7 +111,7 @@ sealed class ByteSequence : Comparable<ByteSequence> {
if (this === other) return true if (this === other) return true
if (other !is ByteSequence) return false if (other !is ByteSequence) return false
if (this.size != other.size) return false if (this.size != other.size) return false
return subArraysEqual(this.bytes, this.offset, this.size, other.bytes, other.offset) return subArraysEqual(this._bytes, this.offset, this.size, other._bytes, other.offset)
} }
private fun subArraysEqual(a: ByteArray, aOffset: Int, length: Int, b: ByteArray, bOffset: Int): Boolean { private fun subArraysEqual(a: ByteArray, aOffset: Int, length: Int, b: ByteArray, bOffset: Int): Boolean {
@ -103,14 +125,15 @@ sealed class ByteSequence : Comparable<ByteSequence> {
} }
override fun hashCode(): Int { override fun hashCode(): Int {
val thisBytes = _bytes
var result = 1 var result = 1
for (index in offset until (offset + size)) { for (index in offset until (offset + size)) {
result = 31 * result + bytes[index] result = 31 * result + thisBytes[index]
} }
return result return result
} }
override fun toString(): String = "[${bytes.copyOfRange(offset, offset + size).toHexString()}]" override fun toString(): String = "[${_bytes.copyOfRange(offset, offset + size).toHexString()}]"
} }
/** /**
@ -118,7 +141,7 @@ sealed class ByteSequence : Comparable<ByteSequence> {
* In an ideal JVM this would be a value type and be completely overhead free. Project Valhalla is adding such * In an ideal JVM this would be a value type and be completely overhead free. Project Valhalla is adding such
* functionality to Java, but it won't arrive for a few years yet! * functionality to Java, but it won't arrive for a few years yet!
*/ */
open class OpaqueBytes(bytes: ByteArray) : ByteSequence() { open class OpaqueBytes(bytes: ByteArray) : ByteSequence(bytes) {
companion object { companion object {
/** /**
* Create [OpaqueBytes] from a sequence of [Byte] values. * Create [OpaqueBytes] from a sequence of [Byte] values.
@ -147,7 +170,7 @@ open class OpaqueBytes(bytes: ByteArray) : ByteSequence() {
} }
/** /**
* Copy [size] bytes from this [ByteArray] starting from [offset] into a new [ByteArray]. * Wrap [size] bytes from this [ByteArray] starting from [offset] into a new [ByteArray].
*/ */
fun ByteArray.sequence(offset: Int = 0, size: Int = this.size) = ByteSequence.of(this, offset, size) fun ByteArray.sequence(offset: Int = 0, size: Int = this.size) = ByteSequence.of(this, offset, size)
@ -165,7 +188,7 @@ fun String.parseAsHex(): ByteArray = DatatypeConverter.parseHexBinary(this)
/** /**
* Class is public for serialization purposes * Class is public for serialization purposes
*/ */
class OpaqueBytesSubSequence(override val bytes: ByteArray, override val offset: Int, override val size: Int) : ByteSequence() { class OpaqueBytesSubSequence(override val bytes: ByteArray, override val offset: Int, override val size: Int) : ByteSequence(bytes) {
init { init {
require(offset >= 0 && offset < bytes.size) require(offset >= 0 && offset < bytes.size)
require(size >= 0 && size <= bytes.size) require(size >= 0 && size <= bytes.size)

View File

@ -14,7 +14,7 @@ class OpaqueBytesSubSequenceSerializer(factory: SerializerFactory) :
CustomSerializer.Proxy<OpaqueBytesSubSequence, OpaqueBytes>(OpaqueBytesSubSequence::class.java, OpaqueBytes::class.java, factory) { CustomSerializer.Proxy<OpaqueBytesSubSequence, OpaqueBytes>(OpaqueBytesSubSequence::class.java, OpaqueBytes::class.java, factory) {
override val additionalSerializers: Iterable<CustomSerializer<out Any>> = emptyList() override val additionalSerializers: Iterable<CustomSerializer<out Any>> = emptyList()
override fun toProxy(obj: OpaqueBytesSubSequence): OpaqueBytes = obj.copy() as OpaqueBytes override fun toProxy(obj: OpaqueBytesSubSequence): OpaqueBytes = OpaqueBytes(obj.copy().bytes)
override fun fromProxy(proxy: OpaqueBytes): OpaqueBytesSubSequence = OpaqueBytesSubSequence(proxy.bytes, proxy.offset, proxy.size) override fun fromProxy(proxy: OpaqueBytes): OpaqueBytesSubSequence = OpaqueBytesSubSequence(proxy.bytes, proxy.offset, proxy.size)
} }