From f0502a1f8ed6a880eb10fb6123205e8917a48f7d Mon Sep 17 00:00:00 2001 From: Andrey Pfau Date: Thu, 19 Sep 2024 11:59:20 +0400 Subject: [PATCH] 0.3.2 --- bitstring/src/BitString.kt | 8 ++- bitstring/src/ByteBackedBitString.kt | 24 +++---- bitstring/src/ByteBackedMutableBitString.kt | 46 +++++++++---- bitstring/src/EmptyBitString.kt | 4 +- build.gradle.kts | 2 +- tvm/src/boc/BagOfCellsUtils.kt | 72 +++++++++++++++++---- tvm/src/cell/CellBuilder.kt | 5 +- tvm/src/cell/DataCell.kt | 15 +++-- 8 files changed, 131 insertions(+), 45 deletions(-) diff --git a/bitstring/src/BitString.kt b/bitstring/src/BitString.kt index 086b5816..31992f8b 100644 --- a/bitstring/src/BitString.kt +++ b/bitstring/src/BitString.kt @@ -33,12 +33,14 @@ public interface BitString : Iterable, Comparable { public operator fun plus(bits: BooleanArray): BitString = plus(bits.asIterable()) + public operator fun plus(bits: Collection): BitString = plus(bits.asIterable()) public operator fun plus(bits: Iterable): BitString = binary(toBinary() + bits.joinToString("") { if (it) "1" else "0" }) + public operator fun plus(bits: BitString): BitString public operator fun plus(bytes: ByteArray): BitString public fun plus(bytes: ByteArray, bits: Int): BitString @@ -73,7 +75,11 @@ public interface BitString : Iterable, Comparable { override fun toString(): String public fun toBinary(): String = joinToString("") { if (it) "1" else "0" } - public fun toHex(): String + + @Deprecated(message = "Use toHexString()", replaceWith = ReplaceWith("toHexString()")) + public fun toHex(): String = toHexString() + + public fun toHexString(): String public companion object { @JvmStatic diff --git a/bitstring/src/ByteBackedBitString.kt b/bitstring/src/ByteBackedBitString.kt index 2b40f146..6e470603 100644 --- a/bitstring/src/ByteBackedBitString.kt +++ b/bitstring/src/ByteBackedBitString.kt @@ -11,11 +11,18 @@ public open class ByteBackedBitString protected constructor( override val size: Int, public open val bytes: ByteArray ) : BitString { + private val hashCode by lazy(LazyThreadSafetyMode.PUBLICATION) { + var result = size + result = 31 * result + bytes.contentHashCode() + result + } + override operator fun get(index: Int): Boolean = getOrNull(index) ?: throw BitStringUnderflowException() override fun getOrNull(index: Int): Boolean? = if (index in 0..size) get(bytes, index) else null + override fun plus(bits: BitString): BitString = toMutableBitString().plus(bits) override fun plus(bytes: ByteArray): BitString = toMutableBitString().plus(bytes) override fun plus(bytes: ByteArray, bits: Int): BitString = toMutableBitString().plus(bytes, bits) @@ -77,9 +84,9 @@ public open class ByteBackedBitString protected constructor( } } - override fun toString(): String = "x{${toHex()}}" + override fun toString(): String = "x{${toHexString()}}" - override fun toHex(): String { + override fun toHexString(): String { if (size == 0) return "" val data = appendTag(bytes, size) val result = StringBuilder(data.toHexString()) @@ -111,11 +118,7 @@ public open class ByteBackedBitString protected constructor( return true } - override fun hashCode(): Int { - var result = size - result = 31 * result + bytes.contentHashCode() - return result - } + override fun hashCode(): Int = hashCode internal open class BitStringIterator( val bitString: BitString, @@ -168,10 +171,9 @@ public open class ByteBackedBitString protected constructor( @JvmStatic protected fun expandByteArray(bytes: ByteArray, size: Int): ByteArray { val requiredBytesSize = bytesSize(size) - return if (bytes.size == requiredBytesSize) { - bytes - } else { - constructByteArray(bytes, size) + return when { + bytes.size < requiredBytesSize -> constructByteArray(bytes, size) + else -> bytes } } diff --git a/bitstring/src/ByteBackedMutableBitString.kt b/bitstring/src/ByteBackedMutableBitString.kt index a303e6d6..5e55e733 100644 --- a/bitstring/src/ByteBackedMutableBitString.kt +++ b/bitstring/src/ByteBackedMutableBitString.kt @@ -22,20 +22,28 @@ public open class ByteBackedMutableBitString( override fun plus(bits: BooleanArray): ByteBackedMutableBitString = plus(bits.asIterable()) override fun plus(bytes: ByteArray): ByteBackedMutableBitString = plus(bytes, bytes.size * Byte.SIZE_BITS) - override fun plus(bits: Iterable): ByteBackedMutableBitString = plus(bits.toList()) - override fun plus(bits: Collection): ByteBackedMutableBitString = apply { - if (bits is ByteBackedBitString) { - plus(bits.bytes, bits.size) + override fun plus(bits: Iterable): ByteBackedMutableBitString = + plus(if (bits is Collection) bits else bits.toList()) + + override fun plus(bits: BitString): BitString { + return if (bits is ByteBackedBitString) { + plus(bits) } else { - val bitsCount = bits.size + plus(bits.toList()) + } + } - val newBytes = expandByteArray(bytes, size + bitsCount) - bits.forEachIndexed { index, bit -> - set(newBytes, size + index, bit) - } - bytes = newBytes - size += bitsCount + public fun plus(bits: ByteBackedBitString): BitString = plus(bits.bytes, bits.size) + + override fun plus(bits: Collection): ByteBackedMutableBitString = apply { + val bitsCount = bits.size + + val newBytes = expandByteArray(bytes, size + bitsCount) + bits.forEachIndexed { index, bit -> + set(newBytes, size + index, bit) } + bytes = newBytes + size += bitsCount } override fun plus(bit: Boolean): MutableBitString = plus(listOf(bit)) @@ -118,6 +126,22 @@ public open class ByteBackedMutableBitString( } } + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is ByteBackedBitString) return false + + if (size != other.size) return false + if (!bytes.contentEquals(other.bytes)) return false + + return true + } + + override fun hashCode(): Int { + var result = size + result = 31 * result + bytes.contentHashCode() + return result + } + public companion object { @JvmStatic public fun of(size: Int = 0): ByteBackedMutableBitString { diff --git a/bitstring/src/EmptyBitString.kt b/bitstring/src/EmptyBitString.kt index 41f2895e..72e1628e 100644 --- a/bitstring/src/EmptyBitString.kt +++ b/bitstring/src/EmptyBitString.kt @@ -11,6 +11,8 @@ internal object EmptyBitString : BitString { override fun plus(bits: Iterable): BitString = BitString(bits) + override fun plus(bits: BitString): BitString = bits + override fun plus(bits: Collection): BitString = BitString(bits) override fun plus(bytes: ByteArray): BitString = BitString(bytes) @@ -40,7 +42,7 @@ internal object EmptyBitString : BitString { override fun toString(): String = "x{}" - override fun toHex(): String = "" + override fun toHexString(): String = "" override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/build.gradle.kts b/build.gradle.kts index 64393626..1a47037b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,7 +11,7 @@ plugins { allprojects { group = "org.ton" - version = "0.4.0-SNAPSHOT" + version = "0.3.2" repositories { mavenCentral() diff --git a/tvm/src/boc/BagOfCellsUtils.kt b/tvm/src/boc/BagOfCellsUtils.kt index fffcb2ac..11b667b6 100644 --- a/tvm/src/boc/BagOfCellsUtils.kt +++ b/tvm/src/boc/BagOfCellsUtils.kt @@ -80,14 +80,23 @@ internal fun Input.readBagOfCell(): BagOfCells { val cellDescriptors = Array(cellCount) { CellDescriptor(0, 0) } // measureTime { + val cellHashes = Array>?>(cellCount) { null } + repeat(cellCount) { cellIndex -> val d1 = readByte() val d2 = readByte() val descriptor = CellDescriptor(d1, d2) if (descriptor.hasHashes) { - discardExact(descriptor.hashCount * Cell.HASH_BYTES) - discardExact(descriptor.hashCount * Cell.DEPTH_BYTES) + val hashes = ArrayList(descriptor.hashCount) + val depths = ArrayList(descriptor.hashCount) + repeat(descriptor.hashCount) { + hashes.add(readBytes(Cell.HASH_BYTES)) + } + repeat(descriptor.hashCount) { + depths.add(readInt(2)) + } + cellHashes[cellIndex] = hashes.zip(depths) } val cellData = readBytes(descriptor.dataLength) @@ -106,11 +115,11 @@ internal fun Input.readBagOfCell(): BagOfCells { // } // Resolving references & constructing cells from leaves to roots - val cells = Array>(cellCount) { CompletableDeferred() } + val asyncCells = Array>(cellCount) { CompletableDeferred() } GlobalScope.launch { repeat(cellCount) { cellIndex -> launch { - createCell(cellIndex, cells, cellBits, cellRefs, cellDescriptors) + createCell(cellIndex, asyncCells, cellBits, cellRefs, cellDescriptors, cellHashes) } } } @@ -120,13 +129,25 @@ internal fun Input.readBagOfCell(): BagOfCells { readIntLittleEndian() } + val cells = runBlocking { + asyncCells.toList().awaitAll() + } + val roots = rootIndexes.map { rootIndex -> - runBlocking { - cells[rootIndex].await() - } + cells[rootIndex] } - return BagOfCells(roots) + return object : BagOfCells { + override val roots: List = roots + + override fun toString(): String = buildString { + roots.forEachIndexed { _, cell -> + Cell.toString(cell, this) + } + } + + override fun iterator(): Iterator = cells.iterator() + } } private suspend fun createCell( @@ -134,16 +155,45 @@ private suspend fun createCell( cells: Array>, bits: Array, refs: Array, - descriptors: Array + descriptors: Array, + cellHashes: Array>?> ) = coroutineScope { val cellBits = bits[index] val cellRefIndexes = refs[index] val cellRefs = cellRefIndexes.map { refIndex -> cells[refIndex].await() } + val descriptor = descriptors[index] + val hashes = cellHashes[index] +// val cell = if (!descriptors[index].isExotic && hashes != null) { +// val new = buildCell { +// isExotic = descriptor.isExotic +// levelMask = descriptor.levelMask +// storeBits(cellBits) +// storeRefs(cellRefs) +// } +// fun List>.print() = map { +// it.first.toHexString()+"="+it.second +// } +// DataCell(descriptor, cellBits, cellRefs, hashes).also { +// if (new is DataCell && new.hashes != it.hashes) { +//// println("\nnew:${new.hashes.print()}\nit:${it.hashes.print()}") +// } else { +// println("\nWOW: ${it.hashes.print()}") +// } +// } +// new +// } else { +// buildCell { +// isExotic = descriptor.isExotic +// levelMask = descriptor.levelMask +// storeBits(cellBits) +// storeRefs(cellRefs) +// } +// } val cell = buildCell { - isExotic = descriptors[index].isExotic - levelMask = descriptors[index].levelMask + isExotic = descriptor.isExotic + levelMask = descriptor.levelMask storeBits(cellBits) storeRefs(cellRefs) } diff --git a/tvm/src/cell/CellBuilder.kt b/tvm/src/cell/CellBuilder.kt index 57c4ade0..d2ef27af 100644 --- a/tvm/src/cell/CellBuilder.kt +++ b/tvm/src/cell/CellBuilder.kt @@ -151,7 +151,7 @@ public inline fun CellBuilder(cell: Cell): CellBuilder = CellBuilder.of(cell) public inline fun CellBuilder(): CellBuilder = CellBuilder.beginCell() private class CellBuilderImpl( - override var bits: MutableBitString = ByteBackedMutableBitString.of(), + override var bits: MutableBitString = ByteBackedMutableBitString(ByteArray(128), 0), override var refs: MutableList = ArrayList(), override var levelMask: LevelMask? = null, override var isExotic: Boolean = false @@ -351,8 +351,9 @@ private class CellBuilderImpl( val hashes = ArrayList>(levels) var (d1, d2) = descriptor + val hasher = SHA256() repeat(levels) { level -> - val hasher = SHA256() + hasher.reset() val levelMask = if (descriptor.cellType == CellType.PRUNED_BRANCH) { descriptor.levelMask } else { diff --git a/tvm/src/cell/DataCell.kt b/tvm/src/cell/DataCell.kt index 4dc2edf0..b3c556f6 100644 --- a/tvm/src/cell/DataCell.kt +++ b/tvm/src/cell/DataCell.kt @@ -6,8 +6,14 @@ public class DataCell( override val descriptor: CellDescriptor, override val bits: BitString, override val refs: List, - private val hashes: List> + internal val hashes: List> ) : Cell { + private val hashCode: Int by lazy(LazyThreadSafetyMode.PUBLICATION) { + var result = descriptor.hashCode() + result = 31 * result + hashes.hashCode() + result + } + override fun hash(level: Int): BitString { val hashIndex = levelMask.apply(level).hashIndex return BitString(hashes[hashIndex].first) @@ -37,10 +43,5 @@ public class DataCell( return refs == other.refs } - override fun hashCode(): Int { - var result = descriptor.hashCode() - result = 31 * result + bits.hashCode() - result = 31 * result + refs.hashCode() - return result - } + override fun hashCode(): Int = hashCode }